DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 1/4] lib/net: add IEEE 1588 PTP v2 protocol header definitions
From: Rajesh Kumar @ 2026-05-07 10:13 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, aman.deep.singh, stephen, mb, Rajesh Kumar
In-Reply-To: <20260507101314.2456467-1-rajesh3.kumar@intel.com>

Add PTP (Precision Time Protocol) header structures and inline helper
functions to lib/net following DPDK conventions for protocol libraries
(similar to rte_tcp.h, rte_ip.h).

Provides wire-format structures with endian-annotated types:
  - rte_ptp_port_id: 10-byte port identity with EUI-64 clock ID
  - rte_ptp_hdr: 34-byte common message header with correctionField
  - rte_ptp_timestamp: 10-byte nanosecond-precision timestamp

PTP message type constants for all IEEE 1588-2019 message types
(Sync, Delay_Req, Announce, Management, etc.) and flag field bits
(two-step, unicast, leap indicator).

Inline accessor and utility functions for performance:
  - rte_ptp_msg_type(), rte_ptp_version(), rte_ptp_domain()
  - rte_ptp_seq_id(), rte_ptp_is_event(), rte_ptp_is_two_step()
  - rte_ptp_correction_ns(), rte_ptp_add_correction()
  - rte_ptp_timestamp_to_ns() for timestamp conversion

Supports all PTP encapsulations: L2 (EtherType 0x88F7), VLAN/QinQ,
UDP/IPv4, and UDP/IPv6 (ports 319/320).

All inline functions — zero function call overhead, suitable for
real-time packet processing.

Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
---
 MAINTAINERS         |   6 +
 lib/net/meson.build |   1 +
 lib/net/rte_ptp.h   | 264 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 271 insertions(+)
 create mode 100644 lib/net/rte_ptp.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 0f5539f851..da31ada871 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1665,6 +1665,12 @@ F: doc/guides/prog_guide/ipsec_lib.rst
 M: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
 F: app/test-sad/
 
+PTP - lib/net
+M: Rajesh Kumar <rajesh3.kumar@intel.com>
+F: lib/net/rte_ptp.h
+F: examples/ptp_tap_relay_sw/
+F: doc/guides/sample_app_ug/ptp_tap_relay_sw.rst
+
 PDCP - EXPERIMENTAL
 M: Anoob Joseph <anoobj@marvell.com>
 M: Volodymyr Fialko <vfialko@marvell.com>
diff --git a/lib/net/meson.build b/lib/net/meson.build
index 3fad5edc5b..63d13719f3 100644
--- a/lib/net/meson.build
+++ b/lib/net/meson.build
@@ -28,6 +28,7 @@ headers = files(
         'rte_geneve.h',
         'rte_l2tpv2.h',
         'rte_ppp.h',
+        'rte_ptp.h',
         'rte_ib.h',
 )
 
diff --git a/lib/net/rte_ptp.h b/lib/net/rte_ptp.h
new file mode 100644
index 0000000000..da7b29ab0e
--- /dev/null
+++ b/lib/net/rte_ptp.h
@@ -0,0 +1,264 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef _RTE_PTP_H_
+#define _RTE_PTP_H_
+
+/**
+ * @file
+ *
+ * PTP (IEEE 1588) protocol definitions
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <rte_byteorder.h>
+#include <rte_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================
+ *  PTP Constants
+ * ============================================================ */
+
+/** PTP over UDP event port (Sync, Delay_Req, PDelay_Req, PDelay_Resp). */
+#define RTE_PTP_EVENT_PORT        319
+
+/** PTP over UDP general port (Follow_Up, Delay_Resp, Announce, etc.). */
+#define RTE_PTP_GENERAL_PORT      320
+
+/** PTP multicast MAC address: 01:1B:19:00:00:00. */
+#define RTE_PTP_MULTICAST_MAC     { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 }
+
+/** PTP peer delay multicast MAC: 01:80:C2:00:00:0E. */
+#define RTE_PTP_PDELAY_MULTICAST_MAC { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E }
+
+/* ============================================================
+ *  PTP Message Types (IEEE 1588-2019 Table 36)
+ * ============================================================ */
+
+#define RTE_PTP_MSGTYPE_SYNC            0x0  /**< Sync (event). */
+#define RTE_PTP_MSGTYPE_DELAY_REQ       0x1  /**< Delay_Req (event). */
+#define RTE_PTP_MSGTYPE_PDELAY_REQ      0x2  /**< Peer_Delay_Req (event). */
+#define RTE_PTP_MSGTYPE_PDELAY_RESP     0x3  /**< Peer_Delay_Resp (event). */
+#define RTE_PTP_MSGTYPE_FOLLOW_UP       0x8  /**< Follow_Up (general). */
+#define RTE_PTP_MSGTYPE_DELAY_RESP      0x9  /**< Delay_Resp (general). */
+#define RTE_PTP_MSGTYPE_PDELAY_RESP_FU  0xA  /**< Peer_Delay_Resp_Follow_Up. */
+#define RTE_PTP_MSGTYPE_ANNOUNCE        0xB  /**< Announce (general). */
+#define RTE_PTP_MSGTYPE_SIGNALING       0xC  /**< Signaling (general). */
+#define RTE_PTP_MSGTYPE_MANAGEMENT      0xD  /**< Management (general). */
+
+/* ============================================================
+ *  PTP Flag Field Bits (IEEE 1588-2019 Table 37)
+ *
+ *  These constants are for use after rte_be_to_cpu_16(hdr->flags).
+ *  flagField[0] (octet 6) maps to host bits 8-15.
+ *  flagField[1] (octet 7) maps to host bits 0-7.
+ * ============================================================ */
+
+#define RTE_PTP_FLAG_TWO_STEP    (1 << 9)   /**< Two-step flag. */
+#define RTE_PTP_FLAG_UNICAST     (1 << 10)  /**< Unicast flag. */
+#define RTE_PTP_FLAG_LI_61       (1 << 0)   /**< Leap indicator 61. */
+#define RTE_PTP_FLAG_LI_59       (1 << 1)   /**< Leap indicator 59. */
+
+/* ============================================================
+ *  PTP Header Structures (IEEE 1588-2019)
+ * ============================================================ */
+
+/**
+ * PTP Port Identity (10 bytes).
+ */
+struct __rte_packed_begin rte_ptp_port_id {
+	uint8_t    clock_id[8]; /**< clockIdentity (EUI-64). */
+	rte_be16_t port_number; /**< portNumber. */
+} __rte_packed_end;
+
+/**
+ * PTP Common Message Header (34 bytes).
+ */
+struct __rte_packed_begin rte_ptp_hdr {
+	uint8_t    msg_type;       /**< transportSpecific (4) | messageType (4). */
+	uint8_t    version;        /**< minorVersionPTP (4) | versionPTP (4). */
+	rte_be16_t msg_length;     /**< Total message length in bytes. */
+	uint8_t    domain_number;  /**< PTP domain (0-255). */
+	uint8_t    minor_sdo_id;   /**< minorSdoId (IEEE 1588-2019). */
+	rte_be16_t flags;          /**< Flag field (see RTE_PTP_FLAG_*). */
+	rte_be64_t correction;     /**< correctionField (scaled ns, 48.16 fixed). */
+	rte_be32_t msg_type_specific; /**< messageTypeSpecific. */
+	struct rte_ptp_port_id source_port_id; /**< sourcePortIdentity. */
+	rte_be16_t sequence_id;    /**< sequenceId. */
+	uint8_t    control;        /**< controlField (deprecated in 1588-2019). */
+	int8_t     log_msg_interval; /**< logMessageInterval. */
+} __rte_packed_end;
+
+/**
+ * PTP Timestamp (10 bytes, used in Sync/Delay_Req/Follow_Up bodies).
+ */
+struct __rte_packed_begin rte_ptp_timestamp {
+	rte_be16_t seconds_hi;   /**< Upper 16 bits of seconds. */
+	rte_be32_t seconds_lo;   /**< Lower 32 bits of seconds. */
+	rte_be32_t nanoseconds;  /**< Nanoseconds (0-999999999). */
+} __rte_packed_end;
+
+/* ============================================================
+ *  Inline Helpers
+ * ============================================================ */
+
+/**
+ * Extract PTP message type from header.
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   Message type (0x0-0xF).
+ */
+static inline uint8_t
+rte_ptp_msg_type(const struct rte_ptp_hdr *hdr)
+{
+	return hdr->msg_type & 0x0F;
+}
+
+/**
+ * Extract transport-specific field from header.
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   Transport-specific value (upper nibble, 0x0-0xF).
+ */
+static inline uint8_t
+rte_ptp_transport_specific(const struct rte_ptp_hdr *hdr)
+{
+	return (hdr->msg_type >> 4) & 0x0F;
+}
+
+/**
+ * Extract PTP version from header.
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   PTP version number (typically 2).
+ */
+static inline uint8_t
+rte_ptp_version(const struct rte_ptp_hdr *hdr)
+{
+	return hdr->version & 0x0F;
+}
+
+/**
+ * Get sequence ID from PTP header (host byte order).
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   Sequence ID in host byte order.
+ */
+static inline uint16_t
+rte_ptp_seq_id(const struct rte_ptp_hdr *hdr)
+{
+	return rte_be_to_cpu_16(hdr->sequence_id);
+}
+
+/**
+ * Get PTP domain number.
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   Domain number (0-255).
+ */
+static inline uint8_t
+rte_ptp_domain(const struct rte_ptp_hdr *hdr)
+{
+	return hdr->domain_number;
+}
+
+/**
+ * Check if PTP message type is an event message.
+ * Event messages (msg_type 0x0-0x3) require timestamps.
+ *
+ * @param msg_type
+ *   PTP message type value (0x0-0xF).
+ * @return
+ *   true if event message, false otherwise.
+ */
+static inline bool
+rte_ptp_is_event(int msg_type)
+{
+	return msg_type >= 0 && msg_type <= RTE_PTP_MSGTYPE_PDELAY_RESP;
+}
+
+/**
+ * Check if the two-step flag is set in a PTP header.
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   true if two-step flag is set.
+ */
+static inline bool
+rte_ptp_is_two_step(const struct rte_ptp_hdr *hdr)
+{
+	return (rte_be_to_cpu_16(hdr->flags) & RTE_PTP_FLAG_TWO_STEP) != 0;
+}
+
+/**
+ * Get correctionField value in nanoseconds (from 48.16 fixed-point).
+ *
+ * @param hdr
+ *   Pointer to PTP header.
+ * @return
+ *   Correction value in nanoseconds.
+ */
+static inline int64_t
+rte_ptp_correction_ns(const struct rte_ptp_hdr *hdr)
+{
+	return (int64_t)rte_be_to_cpu_64(hdr->correction) >> 16;
+}
+
+/**
+ * Add a residence time (in nanoseconds) to the correctionField.
+ * Used by Transparent Clocks to account for relay transit delay.
+ * The correctionField uses IEEE 1588 scaled nanoseconds (48.16 fixed-point).
+ *
+ * @param hdr
+ *   Pointer to PTP header (will be modified in-place).
+ * @param residence_ns
+ *   Residence time in nanoseconds to add.
+ */
+static inline void
+rte_ptp_add_correction(struct rte_ptp_hdr *hdr, int64_t residence_ns)
+{
+	int64_t cf = (int64_t)rte_be_to_cpu_64(hdr->correction);
+
+	cf += (int64_t)((uint64_t)residence_ns << 16);
+	hdr->correction = rte_cpu_to_be_64(cf);
+}
+
+/**
+ * Convert a PTP timestamp structure to nanoseconds since epoch.
+ *
+ * @param ts
+ *   Pointer to PTP timestamp.
+ * @return
+ *   Time in nanoseconds since epoch.
+ */
+static inline uint64_t
+rte_ptp_timestamp_to_ns(const struct rte_ptp_timestamp *ts)
+{
+	uint64_t sec = ((uint64_t)rte_be_to_cpu_16(ts->seconds_hi) << 32) |
+		       rte_be_to_cpu_32(ts->seconds_lo);
+
+	return sec * 1000000000ULL + rte_be_to_cpu_32(ts->nanoseconds);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_PTP_H_ */
-- 
2.53.0


^ permalink raw reply related

* [PATCH v6 0/4] PTP protocol support in lib/net
From: Rajesh Kumar @ 2026-05-07 10:13 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, aman.deep.singh, stephen, mb, Rajesh Kumar
In-Reply-To: <20260428010117.692626-1-rajesh3.kumar@intel.com>

Add IEEE 1588-2019 Precision Time Protocol (PTP) support to DPDK via a
new header library in lib/net/. This patchset provides wire-format packet
structure definitions and inline helpers for Transparent Clock and
ordinary clock applications.

## Design Rationale

Following DPDK conventions for protocol libraries (rte_tcp.h, rte_ip.h),
PTP definitions are provided as a header-only library with inline functions
for zero-overhead packet processing. All functions are suitable for
real-time, performance-critical code paths.

## Contents

**Patch 1:** lib/net/rte_ptp.h - Header structures and inline helpers
  - rte_ptp_port_id: 10-byte port identity with EUI-64 clock ID
  - rte_ptp_hdr: 34-byte PTP v2 common message header
  - rte_ptp_timestamp: 10-byte nanosecond-precision timestamp
  - Message type constants (Sync, Delay_Req, Announce, etc.)
  - Flag field bits (two-step, unicast, leap indicator)
  - Inline helpers: rte_ptp_msg_type(), rte_ptp_seq_id(), rte_ptp_is_event(),
    rte_ptp_correction_ns(), rte_ptp_add_correction(), rte_ptp_timestamp_to_ns()

**Patch 2:** examples/ptp_tap_relay_sw - Software PTP Transparent Clock relay
  - Relays PTP packets between a DPDK-bound physical NIC and Linux TAP interface
  - Software timestamp-based residence time measurement (CLOCK_MONOTONIC)
  - Accumulates residence time to PTP correctionField per IEEE 1588-2019 §10.2
  - Handles L2, VLAN/QinQ, UDP/IPv4, UDP/IPv6 PTP encapsulations
  - Works with unmodified Linux kernel and stock DPDK (no patches required)
  - Compatible with standard linuxptp (ptp4l) tooling

**Patch 3:** doc/guides/rel_notes/release_26_07.rst - Release notes update

**Patch 4:** examples/ptpclient - Update to use lib/net PTP definitions
  - Uses RTE_ETHER_TYPE_1588 constant
  - Adds lib/net dependency

## Testing

- Build: `ninja -C build` — clean compilation
- Examples: Both ptpclient and ptp_tap_relay_sw compile and link correctly
- No stale references to old patterns or removed functions

## Changelog

v6 changes:
  - Restructured to lib/net (header-only, following lib/net conventions)
  - Removed separate DPI library functions (moved to example-local ptp_parse.h)
  - Removed app/test unit tests (library now header-only, example-driven testing)
  - Removed programmer's guide doc (lib/net headers use Doxygen API docs only)

Rajesh Kumar (4):
  lib/net: add IEEE 1588 PTP v2 protocol header definitions
  examples/ptp_tap_relay_sw: add PTP software transparent clock relay
  doc: update release notes for PTP protocol library
  examples/ptpclient: use shared PTP library definitions

 MAINTAINERS                                   |   6 +
 doc/guides/rel_notes/release_26_07.rst        |  12 +
 doc/guides/sample_app_ug/ptp_tap_relay_sw.rst | 212 +++++++++
 examples/ptp_tap_relay_sw/Makefile            |  41 ++
 examples/ptp_tap_relay_sw/meson.build         |  13 +
 examples/ptp_tap_relay_sw/ptp_parse.h         | 211 +++++++++
 examples/ptp_tap_relay_sw/ptp_tap_relay_sw.c  | 432 ++++++++++++++++++
 examples/ptpclient/meson.build                |   1 +
 examples/ptpclient/ptpclient.c                |   4 +-
 lib/net/meson.build                           |   1 +
 lib/net/rte_ptp.h                             | 264 +++++++++++
 11 files changed, 1195 insertions(+), 2 deletions(-)
 create mode 100644 doc/guides/sample_app_ug/ptp_tap_relay_sw.rst
 create mode 100644 examples/ptp_tap_relay_sw/Makefile
 create mode 100644 examples/ptp_tap_relay_sw/meson.build
 create mode 100644 examples/ptp_tap_relay_sw/ptp_parse.h
 create mode 100644 examples/ptp_tap_relay_sw/ptp_tap_relay_sw.c
 create mode 100644 lib/net/rte_ptp.h

-- 
2.53.0


^ permalink raw reply

* Re: [RFC v5 1/6] ptp: introduce PTP protocol library
From: Kumar, Rajesh @ 2026-05-07  4:45 UTC (permalink / raw)
  To: Morten Brørup, dev; +Cc: bruce.richardson, aman.deep.singh, stephen
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F65849@smartserver.smartshare.dk>


On 06-05-2026 04:32 pm, Morten Brørup wrote:
>> From: Rajesh Kumar [mailto:rajesh3.kumar@intel.com]
>> Sent: Wednesday, 6 May 2026 17.41
>>
>> Add IEEE 1588-2019 Precision Time Protocol (PTP) processing library.
>>
>> The library provides packet classification, header structures, and
>> helper functions for PTP packet processing in DPDK. This avoids
>> duplicate PTP header definitions across multiple applications and
>> drivers.
>>
>> Supported transports:
>> 1. L2 PTP (EtherType 0x88F7)
>> 2. VLAN-tagged L2 PTP (single and QinQ)
>> 3. PTP over UDP/IPv4 (ports 319/320)
>> 4. PTP over UDP/IPv6 (ports 319/320)
>>
>> Public APIs:
>> 1. rte_ptp_classify()
>> 2. rte_ptp_hdr_get()
>> 3. rte_ptp_msg_type_str()
>>
>> Signed-off-by: Rajesh Kumar <rajesh3.kumar@intel.com>
>> ---
> Improved PTP support seems like a good addition to DPDK.
>
> Here's some high level feedback to the library:
>
> I think much of this belongs into /lib/net/, which holds similar protocol structure definitions and helper functions for many other protocols.
> Please familiarize yourself with the code conventions in that directory, and move the relevant parts of the PTP library there (following the code conventions there).
>
> Remember to define the packet structures with endianness in mind; e.g. use rte_be32_t instead of uint32_t where appropriate.
>
> Don't define simple aliases, just use RTE_ETHER_TYPE_1588 directly.
>
> Your DPI (deep packet inspection) parser function that can parse all kinds of PTP encapsulation does not belong in the library.
> Applications would rely on a layered packet parser, such as rte_net_get_ptype().
>
> If you need a DPI parser for the examples, feel free to add it there.

Thank you for the feedback, Morten! All comments addressed in v6:

- Moved to lib/net/ as a header-only library (following rte_tcp.h 
conventions)
- All multi-byte struct fields now use rte_beXX_t types
- Removed RTE_ETHER_TYPE_1588 alias; using constant directly
- DPI parser moved to example-local ptp_parse.h (not library API)


^ permalink raw reply

* [DPDK/core Bug 1942] Primary shutdown not caught by all secondaries
From: bugzilla @ 2026-05-07  3:12 UTC (permalink / raw)
  To: dev

http://bugs.dpdk.org/show_bug.cgi?id=1942

            Bug ID: 1942
           Summary: Primary shutdown not caught by all secondaries
           Product: DPDK
           Version: 26.03
          Hardware: All
                OS: All
            Status: UNCONFIRMED
          Severity: normal
          Priority: Normal
         Component: core
          Assignee: dev@dpdk.org
          Reporter: stephen@networkplumber.org
  Target Milestone: ---

The fix to send message when primary exits was wrong approach.
It doesn't work with other applications like dumpcap, pdump or customer
applications.

This is shown by:
 - run test pmd
 - start dpdk dump (secondary)
 - exit test pmd
The shutdown process hangs and eventually times out.

Primary: Sending 'stop_req' request to secondary...
EAL: Fail to recv reply for request
/var/run/dpdk/rte/mp_socket_130405_3692c058749:mp_testpmd

Stopping port 0...
Stopping ports...
Done

Shutting down port 0...
Closing ports...
Port 0 is closed
Done

And in dumpcap:
# ./build/app/dpdk-dumpcap
File: /tmp/dpdk-dumpcap_0_net_tap0_20260506195857.pcapng
Capturing on 'net_tap0'
Packets captured: 29 EAL: Cannot find action: mp_testpmd
Primary process is no longer active, exiting...

Testpmd should not assume secondary is testpmd!

-- 
You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* Re: [RFC v2 0/4] flow_compile: textual flow rule compiler
From: Stephen Hemminger @ 2026-05-07  2:54 UTC (permalink / raw)
  To: dev
In-Reply-To: <20260507001501.608724-1-stephen@networkplumber.org>

On Wed,  6 May 2026 17:06:47 -0700
Stephen Hemminger <stephen@networkplumber.org> wrote:

> Background
> ----------
> 
> Multiple efforts over the past few cycles have tried to make
> testpmd's flow rule grammar reusable from outside testpmd.
> External applications that need rte_flow want a documented way
> to turn human-written rules into the rte_flow_attr/item/action
> arrays accepted by rte_flow_create().
> 
> The most recent attempt is Lukas Sismis's series, currently at
> v12:
> 
>   http://patches.dpdk.org/project/dpdk/list/?series=37384
> 
> That series factors testpmd's existing cmdline_flow.c into a
> library and updates testpmd to consume it.  It works, but
> inherits two properties of cmdline_flow.c that I think are worth
> avoiding in a reusable library:
> 
>   - Coupling to librte_cmdline.  Even after the v12 split into
>     a "simple" part and a "cmdline" part, the parser is still
>     organized around testpmd's command interpreter, and v12 has
>     cmdline depending on ethdev to break a previous circular
>     dependency.  A library used by daemons, control planes, or
>     unit tests should not need that.
> 
>   - Ad-hoc grammar.  cmdline_flow.c implements parsing per-token
>     in long dispatch logic; the grammar emerges from the code
>     rather than being stated, and adding a new flow item
>     requires touching the parser.
> 
> This RFC explores a different shape and is posted to ask the
> list which one is preferred before more work goes into either.
> 
> I started a new green-field library for parsing flow rules
> (with AI assistance for the boilerplate).  It is young but
> passes tests and reviews clean under the project's AI review
> guidelines.
> 
> This series
> -----------
> 
> lib/flow_compile -- a small new library providing the same
> service via a pcap_compile()-style API:
> 
>     char errbuf[RTE_FLOW_COMPILE_ERRBUF_SIZE];
>     struct rte_flow_compile *fc = rte_flow_compile(rule, errbuf);
>     if (fc == NULL)
>             fail(errbuf);            /* "line:col: message" */
> 
>     rte_flow_compile_create(port_id, fc, &flow_error);
>     rte_flow_compile_free(fc);
> 
> Design properties:
> 
>   - Flex lexer plus bison grammar.  Both are reentrant
>     (%option reentrant, %define api.pure full), so multiple
>     compilations may run concurrently and the parser holds no
>     static mutable state.  The grammar itself is short
>     (~200 lines) because all per-type knowledge lives in
>     descriptor tables, not in productions.
> 
>   - Parser is driven entirely by descriptor tables of items and
>     actions.  Adding a new flow item is a table edit, not a
>     grammar change.  A custom-setter hook on each field is the
>     escape valve for layouts that don't fit a plain byte range
>     (bitfields, indirect arrays).
> 
>   - Dependencies: rte_ethdev (for rte_flow.h) and rte_net (for
>     MAC parsing).  No librte_cmdline.  Flex and bison are
>     required at build time to regenerate the lexer and parser;
>     if either tool is missing the library is silently skipped
>     via meson's has_flex_bison check, the same pattern other
>     DPDK components use for optional generators.
> 
>   - Per-allocation rte_zmalloc for spec/mask/last/conf payloads;
>     rte_flow_compile_free() walks the pattern and action arrays
>     and releases every non-NULL slot before freeing the arrays.
>     Parse-error paths use the same walker, so partially
>     constructed rules clean up uniformly.  ASan/LSan run clean
>     on the autotest, including the failure cases.
> 
> The grammar follows testpmd's syntax closely so familiar rules
> carry over:
> 
>     ingress pattern eth / ipv4 src is 10.0.0.1 / end
>     actions queue index 3 / count / end
> 
> and is documented as a formal BNF in the programmer's guide
> chapter (patch 2).
> 
> Initial coverage: eth, vlan, ipv4, ipv6, tcp, udp, vxlan,
> port_id, port_representor, represented_port items; drop,
> passthru, queue, mark, jump, count, port_id and representor
> variants, of_pop_vlan, vxlan_decap actions.  Variable-conf
> items and actions (RSS, RAW) need custom setters and are
> deferred to a follow-up.
> 
> What this RFC is *not*
> ----------------------
> 
> Not a replacement for cmdline_flow.c in testpmd.  If the shape
> here is acceptable, the next step is a separate series adding a
> "flow compile <port> <rule>" command in testpmd alongside the
> existing parser, so users can adopt the library incrementally
> without breaking scripts that depend on the current syntax.
> 
> What I'd like feedback on
> -------------------------
> 
> 1. API shape.  pcap_compile-style (one string -> opaque object ->
>    arrays) versus the three-call attr/pattern/actions form
>    Sismis's v12 exposes.  What does your application actually
>    want?
> 
> 2. Library placement.  Stand-alone at lib/flow_compile/ versus
>    addition to lib/ethdev.  This series treats it as a
>    control-path parser layered on top of ethdev rather than
>    part of ethdev itself; v12 places its parser inside ethdev.
> 
> 3. Table-driven extension model.  Is "to add a new flow item,
>    add a row to the descriptor table" the right contract?
>    Should the tables live alongside each rte_flow_item_*
>    definition in rte_flow.h, or in their own file as here?
> 
> 4. Build-tool dependency.  Flex and bison are not currently
>    required to build DPDK.  Adding a library that needs them
>    (with a clean has_flex_bison fallback so the rest of DPDK
>    still builds without them) is the cleanest way I see to get
>    a real grammar. If this gets used by testpmd then
>    what is now an optional dependency would get hardened in.
> 
> 5. Convergence.  If this design is preferred, I'm happy to
>    coordinate with Lukas to fold in the testpmd-side changes
>    from his series.
> 
> 6. Readability. AI generated code like this tends to be
>    either opaque or too verbose for humans. Often have to
>    nudge it into submission.
> 
> 
> Stephen Hemminger (4):
>   config: add support for using flex and bison
>   flow_compile: introduce textual flow rule compiler
>   doc: add programmer's guide for flow rule compiler
>   test/flow_compile: add unit tests for flow rule compiler
> 
>  MAINTAINERS                                |   7 +
>  app/test/meson.build                       |   1 +
>  app/test/test_flow_compile.c               | 255 ++++++++++
>  config/meson.build                         |  23 +
>  doc/guides/prog_guide/flow_compile_lib.rst | 302 ++++++++++++
>  doc/guides/prog_guide/index.rst            |   1 +
>  doc/guides/rel_notes/release_26_07.rst     |   6 +
>  lib/flow_compile/flow_compile.l            | 227 +++++++++
>  lib/flow_compile/flow_compile.y            | 311 +++++++++++++
>  lib/flow_compile/flow_compile_priv.h       | 127 +++++
>  lib/flow_compile/flow_compile_setters.c    | 516 +++++++++++++++++++++
>  lib/flow_compile/flow_compile_tables.c     | 243 ++++++++++
>  lib/flow_compile/meson.build               |  22 +
>  lib/flow_compile/rte_flow_compile.h        | 158 +++++++
>  lib/flow_compile/rte_flow_compile_api.c    | 160 +++++++
>  lib/meson.build                            |   1 +
>  16 files changed, 2360 insertions(+)
>  create mode 100644 app/test/test_flow_compile.c
>  create mode 100644 doc/guides/prog_guide/flow_compile_lib.rst
>  create mode 100644 lib/flow_compile/flow_compile.l
>  create mode 100644 lib/flow_compile/flow_compile.y
>  create mode 100644 lib/flow_compile/flow_compile_priv.h
>  create mode 100644 lib/flow_compile/flow_compile_setters.c
>  create mode 100644 lib/flow_compile/flow_compile_tables.c
>  create mode 100644 lib/flow_compile/meson.build
>  create mode 100644 lib/flow_compile/rte_flow_compile.h
>  create mode 100644 lib/flow_compile/rte_flow_compile_api.c
> 

Interesting that CI AI review generates lots of false positives on this.
Think the CI infrastructure version of AGENTS.md is out of date,
or it is using a weak AI model.


Systematically debunked CI review false positives through code analysis
You're right — the entire review is false positives, and several of them violate the explicit rules in AGENTS.md. Walking through each item:

Patch 1/4 — meson flex_gen / bison_gen initialization

Vague speculation phrased as a question ("Does the code need to..."). The variables are only referenced under if has_flex_bison (or equivalent) elsewhere, which is exactly the pattern the snippet establishes. AGENTS.md forbids "vague suggestions like 'should be verified' or 'should be checked'."

Patch 2/4 — nul_terminate with buflen == 0

False positive. If buflen == 0 and srclen == 0, then srclen >= buflen is true (0 >= 0), so the function returns NULL without writing. Also, every call site uses sizeof(_buf) on a non-empty stack buffer, so buflen is never zero in practice. The function is safe both abstractly and in context.

Patch 2/4 — MAC validation before memcpy

False positive. The code already gates the memcpy on rte_ether_unformat_addr(buf, &ea) != 0. The reviewer is speculating that a documented-success return might leave ea undefined, with no basis. AGENTS.md: don't speculate.

Patch 2/4 — hex_nibble validation

False positive. The lexer regex is 0[xX][0-9A-Fa-f]{17,} — only hex digits can ever reach hex_nibble(). The function is correct by construction of its only caller.

Patch 2/4 — cap * sizeof(*p) overflow

Theoretically possible but practically unreachable: the cap doubles per item parsed, so overflow would require parsing a multi-gigabyte rule string with billions of items. Not a real bug. AGENTS.md says report bugs at >=50% confidence — this is well below that.

Patch 2/4 — leak when later rte_zmalloc fails in begin_item

False positive, and the author specifically anticipated this. Note the comment cc->out->npattern++; /* publish so cleanup walker sees it */ — npattern is incremented before the spec/mask/last allocations. On YYABORT, rte_flow_compile_free() walks pattern[0..npattern] and calls rte_free() on each spec/mask/last, and rte_free(NULL) is a no-op. So if spec succeeds and mask fails, spec is freed and mask/last are skipped. The cleanup design is correct.

Patch 2/4 — scanner leak in success path

This is the most egregious one. The reviewer asks the question, then literally writes "Wait, looking more carefully — does the code call flow_compile_yylex_destroy(scanner) in all error paths?" The answer is yes — flow_compile_yylex_destroy(scanner) is called unconditionally after flow_compile_yyparse(), before the if (rc != 0) check, so it runs on both success and failure. AGENTS.md explicitly bans this self-contradicting pattern: "Do NOT report an issue then contradict yourself."

Patch 2/4 — strtoull() overflow check

False positive. The code does errno = 0; ... strtoull(...); if (errno != 0 || *end != '\0') FAIL(...). That is the canonical strtoull overflow check (ERANGE on overflow). The reviewer is asking documented library behavior questions.

Patch 2/4 — partially initialized fc in rte_flow_compile_free

False positive. out is allocated with rte_zmalloc so npattern/nactions start at 0; pattern/actions slots are zeroed by memset in begin_item/begin_action before publication; and rte_free(NULL) is safe. There is no stale-value path.

Patch 3/4 — included with no findings

AGENTS.md: "OMIT patches that have no issues. Do not include a patch in your output just to say 'no issues found' or to summarize what the patch does." The reviewer included it to say it looked good.

Patch 4/4 — alignment of p[1].spec for IPv4

False positive. rte_zmalloc returns at least cache-line aligned memory by default — far stricter than struct rte_flow_item_ipv4 requires. The struct isn't packed in a way that would change this. Pure FUD.

Meta-observation

Almost every item is phrased as "Does this code...?" rather than a concrete claim. AGENTS.md is explicit: "either it's wrong or don't mention it." The Socratic-question framing is the tell — when the reviewer can't commit to "this is wrong," it usually means it isn't, and the question shouldn't be in the output.

Of the eleven items in the review, zero are real bugs. You can safely ignore the entire review.




^ permalink raw reply

* Re: [PATCH v2 0/5] Remove limitations coming from legacy VMDq
From: Stephen Hemminger @ 2026-05-07  2:51 UTC (permalink / raw)
  To: David Marchand; +Cc: dev
In-Reply-To: <20260506123554.2524136-1-david.marchand@redhat.com>

On Wed,  6 May 2026 14:35:48 +0200
David Marchand <david.marchand@redhat.com> wrote:

> Since the commit 88ac4396ad29 ("ethdev: add VMDq support"),
> VMDq has been imposing a maximum number of mac addresses in the
> mac_addr_add/del API.
> 
> Nowadays, new Intel drivers do not support the feature and few other
> drivers implement this feature.
> 
> This series proposes to flag drivers that support the feature, and
> remove the limit of number of mac addresses for others.
> 
> Next step could be to remove the VMDq pool notion from the generic API.
> However I have some concern about this, as changing the quite stable
> mac_addr_add/del API now seems a lot of noise for not much benefit.
> 


AI review (manual not automated) saw these:

On Wed,  6 May 2026 14:35:49 +0200
David Marchand <david.marchand@redhat.com> wrote:

[PATCH v2 1/5] ethdev: skip VMDq pools unless configured
--------------------------------------------------------

Warning: duplicate-add no longer returns 0 in non-VMDq mode

   } else if (vmdq) {
           pool_mask = dev->data->mac_pool_sel[index];
           /* Check if both MAC address and pool is already there, and do nothing */
           if (pool_mask & RTE_BIT64(pool))
                   return 0;
   }

   When index >= 0 (MAC already present) and VMDq is not configured,
   the early "already there" return that existed before is now skipped
   entirely. The driver's mac_addr_add op gets re-invoked and
   mac_pool_sel is no longer updated for !vmdq, so subsequent calls
   keep re-invoking it. Pre-patch behaviour was idempotent (return 0).

   Suggested fix:

   } else if (vmdq) {
           pool_mask = dev->data->mac_pool_sel[index];
           if (pool_mask & RTE_BIT64(pool))
                   return 0;
   } else {
           return 0;       /* MAC already installed, only one pool */
   }


[PATCH v2 2/5] ethdev: announce VMDq capability
-----------------------------------------------

Warning: capability advertised even when max_vmdq_pools may be 0

   The capability bit is set unconditionally in several drivers where
   VMDq availability is conditional on hardware variant or runtime
   configuration:

     - net/intel/e1000/igb_ethdev.c: max_vmdq_pools = 0 for e1000_82575,
       e1000_i354 (no setting), e1000_i210, e1000_i211.
     - net/intel/i40e/i40e_ethdev.c: max_vmdq_pools is gated on
       (pf->flags & I40E_FLAG_VMDQ).
     - net/bnxt/bnxt_ethdev.c: max_vmdq_pools is set to 0 when there
       are not enough resources (see "Not enough resources to support
       VMDq" path).

   With this patch a user can pass mq_mode |= RTE_ETH_MQ_RX_VMDQ_FLAG
   through rte_eth_dev_configure() because the new capability check
   passes, but the driver has no pools to honour the request.

   Suggested fix: gate the capa bit on max_vmdq_pools > 0, or set it
   per-MAC-type / per-flag in each driver.

Warning: VMDq capability advertised on VFs that do not configure VMDq

   ixgbevf_dev_info_get() and txgbevf_dev_info_get() now advertise
   RTE_ETH_DEV_CAPA_VMDQ, but ixgbevf_dev_configure() and
   txgbevf_dev_configure() only handle RTE_ETH_MQ_RX_RSS_FLAG. The
   feature matrices doc/guides/nics/features/ixgbe_vf.ini and
   txgbe_vf.ini do not list "VMDq = Y". A VF is itself a member of a
   PF VMDq pool; advertising the capa from the VF is misleading.

Warning: ipn3ke advertises VMDq=Y in its features file but is not
   updated by the patch

   doc/guides/nics/features/ipn3ke.ini has "VMDq = Y" but the patch
   does not add RTE_ETH_DEV_CAPA_VMDQ to ipn3ke. Either update the
   driver or correct the feature matrix.

Warning: missing release note

   The new RTE_ETH_DEV_CAPA_VMDQ public bit and the new rejection in
   rte_eth_dev_configure() (returning -EINVAL when an application sets
   a VMDq mq_mode on a non-VMDq device) are user-visible API and
   behavioural changes. release_26_07.rst should mention them under
   "API Changes".


[PATCH v2 3/5] ethdev: hide VMDq internal sizes
-----------------------------------------------

Warning: public defines removed without deprecation cycle

   RTE_ETH_NUM_RECEIVE_MAC_ADDR and RTE_ETH_VMDQ_NUM_UC_HASH_ARRAY
   were declared in the application-facing rte_ethdev.h. Moving them
   to ethdev_driver.h removes them from the public API. Per the DPDK
   API/ABI policy this normally needs a prior deprecation notice; at
   minimum it warrants a release_26_07.rst "API Changes" entry.


[PATCH v2 4/5] net/iavf: accept up to 32k unicast MAC addresses
---------------------------------------------------------------

Warning: dead store in iavf_add_del_uc_addr_bulk()

   for (uint32_t i = 0; i < nb_addrs; i++) {
           size_t buf_len = sizeof(struct virtchnl_ether_addr_list) +
                   sizeof(struct virtchnl_ether_addr) * list->num_elements;
           ...
           buf_len = sizeof(struct virtchnl_ether_addr_list) +
                   sizeof(struct virtchnl_ether_addr) * list->num_elements;

   The first initialiser is unconditionally overwritten by the second
   assignment before any read. Drop the initialiser (or move the
   declaration to the use site).

Warning: missing release note for max_mac_addrs jump

   max_mac_addrs goes from 64 to 32768 and dev_data->mac_addrs is now
   allocated at RTE_ETHER_ADDR_LEN * 32768 = 192 KiB per VF port. This
   is a notable behaviour change for iavf users and worth a
   release_26_07.rst entry.

^ permalink raw reply

* Re: [PATCH v2 1/7] net/netvsc: retry VF hotplug indefinitely until PCI device disappears
From: Stephen Hemminger @ 2026-05-07  2:49 UTC (permalink / raw)
  To: Long Li; +Cc: dev, Wei Hu, stable
In-Reply-To: <20260506020529.281654-1-longli@microsoft.com>

On Tue,  5 May 2026 19:05:22 -0700
Long Li <longli@microsoft.com> wrote:

> After PCI rescan on Azure, the MANA kernel driver can take over 100
> seconds to probe and create the /sys/bus/pci/devices/<dev>/net directory.
> The previous fixed retry limit (NETVSC_MAX_HOTADD_RETRY=10, ~12 seconds)
> was insufficient, causing VF re-attach to fail with 'Failed to parse PCI
> device' on systems with slow MANA driver initialization.
> 
> Replace the fixed retry limit with an indefinite retry that only gives up
> when the PCI device itself disappears from sysfs. This is safe because:
> 
> - The retry uses rte_eal_alarm callbacks which are serialized on the EAL
>   interrupt thread, preventing races with VF remove or device close paths.
> - Device close (eth_hn_dev_uninit) explicitly cancels all pending hotplug
>   alarms via rte_eal_alarm_cancel and frees the context.
> - If the PCI device is removed while retrying, access() detects the
>   missing sysfs path and stops immediately.
> 
> A periodic NOTICE log every 30 retries (~30s) provides visibility into
> long waits without flooding the log at DEBUG level.
> 
> Fixes: a2a23a794b3a ("net/netvsc: support VF device hot add/remove")
> Cc: stable@dpdk.org
> Signed-off-by: Long Li <longli@microsoft.com>
> ---
Better but still seeing AI review warnings.

Reviewed the v2 7-patch series against upstream drivers/net/netvsc/. Patches 1, 2, 3, and 5 are clean. Findings on the rest:
Patch 4 — the new "retry loop exiting" NOTICE fires on every termination including the success path, producing a noise alert on every successful VF re-attach.
Patch 6 — two warnings: (a) reaching directly into vf_dev->dev_ops->stats_get works only because eth_stats_qstats_get() already memset the buffers before invoking netvsc's callback, an undocumented dependency on the caller; (b) the else fallback to rte_eth_stats_get() is dead code — it returns -ENOTSUP for the same reason as the direct call.
Patch 7 — the recovering and recovery_success callbacks acquire vf_lock directly from event-callback context, departing from the existing INTR_RMV pattern that defers work via rte_eal_alarm_set precisely to avoid cross-driver lock-order assumptions. The unlocked vf_attached read in recovery_failed is a benign race that can be simplified by dropping the guard.

^ permalink raw reply

* [PATCH V2 13/15] power/kvm_vm: remove the verification of lcore ID
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Now the lcore ID has been verified in framework API, so remove
the verification in driver.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/kvm_vm/kvm_vm.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/power/kvm_vm/kvm_vm.c b/drivers/power/kvm_vm/kvm_vm.c
index 133d1f0882..e8b454bb55 100644
--- a/drivers/power/kvm_vm/kvm_vm.c
+++ b/drivers/power/kvm_vm/kvm_vm.c
@@ -24,11 +24,6 @@ power_kvm_vm_check_supported(void)
 int
 power_kvm_vm_init(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pkt[lcore_id].command = RTE_POWER_CPU_POWER;
 	pkt[lcore_id].resource_id = lcore_id;
 	return guest_channel_host_connect(FD_PATH, lcore_id);
@@ -73,11 +68,6 @@ send_msg(unsigned int lcore_id, uint32_t scale_direction)
 {
 	int ret;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pkt[lcore_id].unit = scale_direction;
 	ret = guest_channel_send_msg(&pkt[lcore_id], lcore_id);
 	if (ret == 0)
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 14/15] power: allow the service core to config power QoS
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

The service core thread can use power QoS API.
So allow these cores to config it.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 lib/power/rte_power_qos.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/lib/power/rte_power_qos.c b/lib/power/rte_power_qos.c
index be230d1c50..f6630b08f7 100644
--- a/lib/power/rte_power_qos.c
+++ b/lib/power/rte_power_qos.c
@@ -27,10 +27,7 @@ rte_power_qos_set_cpu_resume_latency(uint16_t lcore_id, int latency)
 	FILE *f;
 	int ret;
 
-	if (!rte_lcore_is_enabled(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is not enabled", lcore_id);
-		return -EINVAL;
-	}
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -EINVAL);
 	ret = power_get_lcore_mapped_cpu_id(lcore_id, &cpu_id);
 	if (ret != 0)
 		return ret;
@@ -82,10 +79,7 @@ rte_power_qos_get_cpu_resume_latency(uint16_t lcore_id)
 	FILE *f;
 	int ret;
 
-	if (!rte_lcore_is_enabled(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is not enabled", lcore_id);
-		return -EINVAL;
-	}
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -EINVAL);
 	ret = power_get_lcore_mapped_cpu_id(lcore_id, &cpu_id);
 	if (ret != 0)
 		return ret;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 15/15] power: add lcore ID check for PMD mgmt
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

The pmd_mgmt lib is mainly used together with the data plane of ethdev
PMD. The core in data plane is ROLE_RTE. So use rte_lcore_is_enabled
to verify it.

Fixes: 6f987b594fa6 ("power: refactor core power management")
Cc: stable@dpdk.org

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 lib/power/rte_power_pmd_mgmt.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/lib/power/rte_power_pmd_mgmt.c b/lib/power/rte_power_pmd_mgmt.c
index a4d53aac2a..a5fc1c3a94 100644
--- a/lib/power/rte_power_pmd_mgmt.c
+++ b/lib/power/rte_power_pmd_mgmt.c
@@ -511,7 +511,8 @@ rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id, uint16_t port_id,
 
 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
 
-	if (queue_id >= RTE_MAX_QUEUES_PER_PORT || lcore_id >= RTE_MAX_LCORE) {
+	if (queue_id >= RTE_MAX_QUEUES_PER_PORT ||
+	    !rte_lcore_is_enabled(lcore_id)) {
 		ret = -EINVAL;
 		goto end;
 	}
@@ -627,7 +628,7 @@ rte_power_ethdev_pmgmt_queue_disable(unsigned int lcore_id,
 
 	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
 
-	if (lcore_id >= RTE_MAX_LCORE || queue_id >= RTE_MAX_QUEUES_PER_PORT)
+	if (!rte_lcore_is_enabled(lcore_id) || queue_id >= RTE_MAX_QUEUES_PER_PORT)
 		return -EINVAL;
 
 	/* check if the queue is stopped */
@@ -729,8 +730,8 @@ RTE_EXPORT_SYMBOL(rte_power_pmd_mgmt_set_scaling_freq_min)
 int
 rte_power_pmd_mgmt_set_scaling_freq_min(unsigned int lcore, unsigned int min)
 {
-	if (lcore >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
+	if (!rte_lcore_is_enabled(lcore)) {
+		POWER_LOG(ERR, "lcore id %u is not enabled", lcore);
 		return -EINVAL;
 	}
 
@@ -747,8 +748,8 @@ RTE_EXPORT_SYMBOL(rte_power_pmd_mgmt_set_scaling_freq_max)
 int
 rte_power_pmd_mgmt_set_scaling_freq_max(unsigned int lcore, unsigned int max)
 {
-	if (lcore >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
+	if (!rte_lcore_is_enabled(lcore)) {
+		POWER_LOG(ERR, "lcore id %u is not enabled", lcore);
 		return -EINVAL;
 	}
 
@@ -769,8 +770,8 @@ RTE_EXPORT_SYMBOL(rte_power_pmd_mgmt_get_scaling_freq_min)
 int
 rte_power_pmd_mgmt_get_scaling_freq_min(unsigned int lcore)
 {
-	if (lcore >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
+	if (!rte_lcore_is_enabled(lcore)) {
+		POWER_LOG(ERR, "lcore id %u is not enabled", lcore);
 		return -EINVAL;
 	}
 
@@ -784,8 +785,8 @@ RTE_EXPORT_SYMBOL(rte_power_pmd_mgmt_get_scaling_freq_max)
 int
 rte_power_pmd_mgmt_get_scaling_freq_max(unsigned int lcore)
 {
-	if (lcore >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
+	if (!rte_lcore_is_enabled(lcore)) {
+		POWER_LOG(ERR, "lcore id %u is not enabled", lcore);
 		return -EINVAL;
 	}
 
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 12/15] power/intel_pstate: remove the verification of lcore ID
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Now the lcore ID has been verified in framework API, so remove
the verification in driver.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../power/intel_pstate/intel_pstate_cpufreq.c | 65 -------------------
 1 file changed, 65 deletions(-)

diff --git a/drivers/power/intel_pstate/intel_pstate_cpufreq.c b/drivers/power/intel_pstate/intel_pstate_cpufreq.c
index 58be63fecc..e7ed9c8260 100644
--- a/drivers/power/intel_pstate/intel_pstate_cpufreq.c
+++ b/drivers/power/intel_pstate/intel_pstate_cpufreq.c
@@ -548,11 +548,6 @@ power_pstate_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_IDLE;
 	/* The power in use state works as a guard variable between
@@ -629,11 +624,6 @@ power_pstate_cpufreq_exit(unsigned int lcore_id)
 	struct pstate_power_info *pi;
 	uint32_t exp_state;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	exp_state = POWER_USED;
@@ -687,11 +677,6 @@ power_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return 0;
-	}
-
 	if (freqs == NULL) {
 		POWER_LOG(ERR, "NULL buffer supplied");
 		return 0;
@@ -710,11 +695,6 @@ power_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 uint32_t
 power_pstate_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return RTE_POWER_INVALID_FREQ_INDEX;
-	}
-
 	return lcore_power_info[lcore_id].curr_idx;
 }
 
@@ -722,11 +702,6 @@ power_pstate_cpufreq_get_freq(unsigned int lcore_id)
 int
 power_pstate_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	return set_freq_internal(&(lcore_power_info[lcore_id]), index);
 }
 
@@ -735,11 +710,6 @@ power_pstate_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx == 0 ||
 	    (pi->curr_idx == 1 && pi->turbo_available && !pi->turbo_enable))
@@ -754,11 +724,6 @@ power_pstate_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx + 1 == pi->nb_freqs)
 		return 0;
@@ -770,11 +735,6 @@ power_pstate_cpufreq_freq_down(unsigned int lcore_id)
 int
 power_pstate_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	/* Frequencies in the array are from high to low. */
 	if (lcore_power_info[lcore_id].turbo_available) {
 		if (lcore_power_info[lcore_id].turbo_enable)
@@ -795,11 +755,6 @@ power_pstate_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	/* Frequencies in the array are from high to low. */
@@ -812,11 +767,6 @@ power_pstate_turbo_status(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	return pi->turbo_enable;
@@ -827,11 +777,6 @@ power_pstate_enable_turbo(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	if (pi->turbo_available)
@@ -853,11 +798,6 @@ power_pstate_disable_turbo(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	pi->turbo_enable = 0;
@@ -881,11 +821,6 @@ int power_pstate_get_capabilities(unsigned int lcore_id,
 {
 	struct pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 09/15] power/acpi_cpufreq: remove the verification of lcore ID
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Now the lcore id has been verified in framework API, so remove
the verification in driver.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/acpi/acpi_cpufreq.c | 65 -------------------------------
 1 file changed, 65 deletions(-)

diff --git a/drivers/power/acpi/acpi_cpufreq.c b/drivers/power/acpi/acpi_cpufreq.c
index 155b30fcf7..9c78b4cb1a 100644
--- a/drivers/power/acpi/acpi_cpufreq.c
+++ b/drivers/power/acpi/acpi_cpufreq.c
@@ -242,11 +242,6 @@ power_acpi_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_IDLE;
 	/* The power in use state works as a guard variable between
@@ -318,11 +313,6 @@ power_acpi_cpufreq_exit(unsigned int lcore_id)
 	struct acpi_power_info *pi;
 	uint32_t exp_state;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_USED;
 	/* The power in use state works as a guard variable between
@@ -372,11 +362,6 @@ power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return 0;
-	}
-
 	if (freqs == NULL) {
 		POWER_LOG(ERR, "NULL buffer supplied");
 		return 0;
@@ -395,22 +380,12 @@ power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 uint32_t
 power_acpi_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return RTE_POWER_INVALID_FREQ_INDEX;
-	}
-
 	return lcore_power_info[lcore_id].curr_idx;
 }
 
 int
 power_acpi_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	return set_freq_internal(&(lcore_power_info[lcore_id]), index);
 }
 
@@ -419,11 +394,6 @@ power_acpi_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx + 1 == pi->nb_freqs)
 		return 0;
@@ -437,11 +407,6 @@ power_acpi_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx == 0 ||
 	    (pi->curr_idx == 1 && pi->turbo_available && !pi->turbo_enable))
@@ -454,11 +419,6 @@ power_acpi_cpufreq_freq_up(unsigned int lcore_id)
 int
 power_acpi_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	/* Frequencies in the array are from high to low. */
 	if (lcore_power_info[lcore_id].turbo_available) {
 		if (lcore_power_info[lcore_id].turbo_enable)
@@ -478,11 +438,6 @@ power_acpi_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	/* Frequencies in the array are from high to low. */
@@ -495,11 +450,6 @@ power_acpi_turbo_status(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	return pi->turbo_enable;
@@ -511,11 +461,6 @@ power_acpi_enable_turbo(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	if (pi->turbo_available)
@@ -544,11 +489,6 @@ power_acpi_disable_turbo(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	 pi->turbo_enable = 0;
@@ -571,11 +511,6 @@ int power_acpi_get_capabilities(unsigned int lcore_id,
 {
 	struct acpi_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 10/15] power/amd_pstate: remove the verification of lcore ID
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Now the lcore ID has been verified in framework API, so remove
the verification in driver.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/amd_pstate/amd_pstate_cpufreq.c | 65 -------------------
 1 file changed, 65 deletions(-)

diff --git a/drivers/power/amd_pstate/amd_pstate_cpufreq.c b/drivers/power/amd_pstate/amd_pstate_cpufreq.c
index bca148dc8d..8ca27d689d 100644
--- a/drivers/power/amd_pstate/amd_pstate_cpufreq.c
+++ b/drivers/power/amd_pstate/amd_pstate_cpufreq.c
@@ -360,11 +360,6 @@ power_amd_pstate_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_IDLE;
 	/* The power in use state works as a guard variable between
@@ -442,11 +437,6 @@ power_amd_pstate_cpufreq_exit(unsigned int lcore_id)
 	struct amd_pstate_power_info *pi;
 	uint32_t exp_state;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_USED;
 	/* The power in use state works as a guard variable between
@@ -492,11 +482,6 @@ power_amd_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return 0;
-	}
-
 	if (freqs == NULL) {
 		POWER_LOG(ERR, "NULL buffer supplied");
 		return 0;
@@ -515,22 +500,12 @@ power_amd_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t
 uint32_t
 power_amd_pstate_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return RTE_POWER_INVALID_FREQ_INDEX;
-	}
-
 	return lcore_power_info[lcore_id].curr_idx;
 }
 
 int
 power_amd_pstate_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	return set_freq_internal(&(lcore_power_info[lcore_id]), index);
 }
 
@@ -539,11 +514,6 @@ power_amd_pstate_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx + 1 == pi->nb_freqs)
 		return 0;
@@ -557,11 +527,6 @@ power_amd_pstate_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx == 0 || (pi->curr_idx == pi->nom_idx &&
 		pi->turbo_available && !pi->turbo_enable))
@@ -574,11 +539,6 @@ power_amd_pstate_cpufreq_freq_up(unsigned int lcore_id)
 int
 power_amd_pstate_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	/* Frequencies in the array are from high to low. */
 	if (lcore_power_info[lcore_id].turbo_available) {
 		if (lcore_power_info[lcore_id].turbo_enable)
@@ -599,11 +559,6 @@ power_amd_pstate_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	/* Frequencies in the array are from high to low. */
@@ -615,11 +570,6 @@ power_amd_pstate_turbo_status(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	return pi->turbo_enable;
@@ -630,11 +580,6 @@ power_amd_pstate_enable_turbo(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	if (pi->turbo_available)
@@ -666,11 +611,6 @@ power_amd_pstate_disable_turbo(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	pi->turbo_enable = 0;
@@ -694,11 +634,6 @@ power_amd_pstate_get_capabilities(unsigned int lcore_id,
 {
 	struct amd_pstate_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 11/15] power/cppc_cpufreq: remove the verification of lcore ID
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Now the lcore ID has been verified in framework API, so remove
the verification in driver.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/cppc/cppc_cpufreq.c | 65 -------------------------------
 1 file changed, 65 deletions(-)

diff --git a/drivers/power/cppc/cppc_cpufreq.c b/drivers/power/cppc/cppc_cpufreq.c
index 7e1298110a..529f68e574 100644
--- a/drivers/power/cppc/cppc_cpufreq.c
+++ b/drivers/power/cppc/cppc_cpufreq.c
@@ -346,11 +346,6 @@ power_cppc_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_IDLE;
 	/* The power in use state works as a guard variable between
@@ -428,11 +423,6 @@ power_cppc_cpufreq_exit(unsigned int lcore_id)
 	struct cppc_power_info *pi;
 	uint32_t exp_state;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_USED;
 	/* The power in use state works as a guard variable between
@@ -478,11 +468,6 @@ power_cppc_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return 0;
-	}
-
 	if (freqs == NULL) {
 		POWER_LOG(ERR, "NULL buffer supplied");
 		return 0;
@@ -501,22 +486,12 @@ power_cppc_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 uint32_t
 power_cppc_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return RTE_POWER_INVALID_FREQ_INDEX;
-	}
-
 	return lcore_power_info[lcore_id].curr_idx;
 }
 
 int
 power_cppc_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	return set_freq_internal(&(lcore_power_info[lcore_id]), index);
 }
 
@@ -525,11 +500,6 @@ power_cppc_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx + 1 == pi->nb_freqs)
 		return 0;
@@ -543,11 +513,6 @@ power_cppc_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 	if (pi->curr_idx == 0 || (pi->curr_idx == 1 &&
 		pi->turbo_available && !pi->turbo_enable))
@@ -560,11 +525,6 @@ power_cppc_cpufreq_freq_up(unsigned int lcore_id)
 int
 power_cppc_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	/* Frequencies in the array are from high to low. */
 	if (lcore_power_info[lcore_id].turbo_available) {
 		if (lcore_power_info[lcore_id].turbo_enable)
@@ -584,11 +544,6 @@ power_cppc_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	/* Frequencies in the array are from high to low. */
@@ -600,11 +555,6 @@ power_cppc_turbo_status(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	return pi->turbo_enable;
@@ -615,11 +565,6 @@ power_cppc_enable_turbo(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	if (pi->turbo_available)
@@ -651,11 +596,6 @@ power_cppc_disable_turbo(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	pi = &lcore_power_info[lcore_id];
 
 	pi->turbo_enable = 0;
@@ -679,11 +619,6 @@ power_cppc_get_capabilities(unsigned int lcore_id,
 {
 	struct cppc_power_info *pi;
 
-	if (!rte_lcore_is_eal_managed(lcore_id)) {
-		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
-		return -1;
-	}
-
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 08/15] power/cpufreq: add the lcore ID verification to framework
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

The lcore ID verification is common for each cpufreq driver.
So add this verification to framework.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 lib/power/rte_power_cpufreq.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/lib/power/rte_power_cpufreq.c b/lib/power/rte_power_cpufreq.c
index f63e976dc2..54872debfa 100644
--- a/lib/power/rte_power_cpufreq.c
+++ b/lib/power/rte_power_cpufreq.c
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  * Copyright(c) 2010-2014 Intel Corporation
  */
-
 #include <eal_export.h>
 #include <rte_spinlock.h>
 #include <rte_debug.h>
@@ -116,6 +115,7 @@ rte_power_init(unsigned int lcore_id)
 	struct rte_power_cpufreq_ops *ops;
 	uint8_t env;
 
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	if (global_default_env != PM_ENV_NOT_SET)
 		return global_cpufreq_ops->init(lcore_id);
 
@@ -147,6 +147,7 @@ RTE_EXPORT_SYMBOL(rte_power_exit)
 int
 rte_power_exit(unsigned int lcore_id)
 {
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	if (global_default_env != PM_ENV_NOT_SET)
 		return global_cpufreq_ops->exit(lcore_id);
 
@@ -161,6 +162,7 @@ uint32_t
 rte_power_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t n)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, 0);
 	return global_cpufreq_ops->get_avail_freqs(lcore_id, freqs, n);
 }
 
@@ -169,6 +171,7 @@ uint32_t
 rte_power_get_freq(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, RTE_POWER_INVALID_FREQ_INDEX);
 	return global_cpufreq_ops->get_freq(lcore_id);
 }
 
@@ -177,6 +180,7 @@ uint32_t
 rte_power_set_freq(unsigned int lcore_id, uint32_t index)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->set_freq(lcore_id, index);
 }
 
@@ -185,6 +189,7 @@ int
 rte_power_freq_up(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->freq_up(lcore_id);
 }
 
@@ -193,6 +198,7 @@ int
 rte_power_freq_down(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->freq_down(lcore_id);
 }
 
@@ -201,6 +207,7 @@ int
 rte_power_freq_max(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->freq_max(lcore_id);
 }
 
@@ -209,6 +216,7 @@ int
 rte_power_freq_min(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->freq_min(lcore_id);
 }
 
@@ -217,6 +225,7 @@ int
 rte_power_turbo_status(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->turbo_status(lcore_id);
 }
 
@@ -225,6 +234,7 @@ int
 rte_power_freq_enable_turbo(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->enable_turbo(lcore_id);
 }
 
@@ -233,6 +243,7 @@ int
 rte_power_freq_disable_turbo(unsigned int lcore_id)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->disable_turbo(lcore_id);
 }
 
@@ -242,5 +253,6 @@ rte_power_get_capabilities(unsigned int lcore_id,
 		struct rte_power_core_capabilities *caps)
 {
 	RTE_ASSERT(global_cpufreq_ops != NULL);
+	RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, -1);
 	return global_cpufreq_ops->get_caps(lcore_id, caps);
 }
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 04/15] power/amd_pstate: validate lcore role in cpufreq API
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Currently, the amd_pstate driver only checks if the lcore_id is
within the RTE_MAX_LCORE range, but fails to verify if the core
is actually active or managed by the application. This lacks
sufficient validation.

This patch add a lcore role check to the cpufreq-related APIs.
Although service cores do not typically invoke these APIs, they may
operate in polling states where power management is required.
To maintain compatibility with applications using service cores, the
validation logic now explicitly allows both ROLE_RTE and ROLE_SERVICE.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/amd_pstate/amd_pstate_cpufreq.c | 56 +++++++++----------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/drivers/power/amd_pstate/amd_pstate_cpufreq.c b/drivers/power/amd_pstate/amd_pstate_cpufreq.c
index 95495bff7d..bca148dc8d 100644
--- a/drivers/power/amd_pstate/amd_pstate_cpufreq.c
+++ b/drivers/power/amd_pstate/amd_pstate_cpufreq.c
@@ -360,9 +360,8 @@ power_amd_pstate_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -443,11 +442,11 @@ power_amd_pstate_cpufreq_exit(unsigned int lcore_id)
 	struct amd_pstate_power_info *pi;
 	uint32_t exp_state;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_USED;
 	/* The power in use state works as a guard variable between
@@ -493,8 +492,8 @@ power_amd_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return 0;
 	}
 
@@ -516,8 +515,8 @@ power_amd_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t
 uint32_t
 power_amd_pstate_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return RTE_POWER_INVALID_FREQ_INDEX;
 	}
 
@@ -527,8 +526,8 @@ power_amd_pstate_cpufreq_get_freq(unsigned int lcore_id)
 int
 power_amd_pstate_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -540,8 +539,8 @@ power_amd_pstate_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -558,8 +557,8 @@ power_amd_pstate_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -575,8 +574,8 @@ power_amd_pstate_cpufreq_freq_up(unsigned int lcore_id)
 int
 power_amd_pstate_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -600,8 +599,8 @@ power_amd_pstate_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -616,8 +615,8 @@ power_amd_pstate_turbo_status(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -631,8 +630,8 @@ power_amd_pstate_enable_turbo(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -667,8 +666,8 @@ power_amd_pstate_disable_turbo(unsigned int lcore_id)
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -695,10 +694,11 @@ power_amd_pstate_get_capabilities(unsigned int lcore_id,
 {
 	struct amd_pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 06/15] power/intel_pstate: validate lcore role in cpufreq API
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Currently, the intel_pstate driver only checks if the lcore_id is
within the RTE_MAX_LCORE range, but fails to verify if the core is
actually active or managed by the application. This lacks sufficient
validation.

This patch add a lcore role check to the cpufreq-related APIs.
Although service cores do not typically invoke these APIs, they may
operate in polling states where power management is required.
To maintain compatibility with applications using service cores, the
validation logic now explicitly allows both ROLE_RTE and ROLE_SERVICE.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 .../power/intel_pstate/intel_pstate_cpufreq.c | 56 +++++++++----------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/drivers/power/intel_pstate/intel_pstate_cpufreq.c b/drivers/power/intel_pstate/intel_pstate_cpufreq.c
index 8e27570e3c..58be63fecc 100644
--- a/drivers/power/intel_pstate/intel_pstate_cpufreq.c
+++ b/drivers/power/intel_pstate/intel_pstate_cpufreq.c
@@ -548,9 +548,8 @@ power_pstate_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceed %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -630,11 +629,11 @@ power_pstate_cpufreq_exit(unsigned int lcore_id)
 	struct pstate_power_info *pi;
 	uint32_t exp_state;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	pi = &lcore_power_info[lcore_id];
 
 	exp_state = POWER_USED;
@@ -688,8 +687,8 @@ power_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return 0;
 	}
 
@@ -711,8 +710,8 @@ power_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 uint32_t
 power_pstate_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return RTE_POWER_INVALID_FREQ_INDEX;
 	}
 
@@ -723,8 +722,8 @@ power_pstate_cpufreq_get_freq(unsigned int lcore_id)
 int
 power_pstate_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -736,8 +735,8 @@ power_pstate_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -755,8 +754,8 @@ power_pstate_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -771,8 +770,8 @@ power_pstate_cpufreq_freq_down(unsigned int lcore_id)
 int
 power_pstate_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -796,8 +795,8 @@ power_pstate_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -813,8 +812,8 @@ power_pstate_turbo_status(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -828,8 +827,8 @@ power_pstate_enable_turbo(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -854,8 +853,8 @@ power_pstate_disable_turbo(unsigned int lcore_id)
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -882,10 +881,11 @@ int power_pstate_get_capabilities(unsigned int lcore_id,
 {
 	struct pstate_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 07/15] power: add a common macro to verify lcore ID
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

There are many places to verify lcore ID in power. So add a common macro
to do this to simplify code.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 lib/power/power_common.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/lib/power/power_common.h b/lib/power/power_common.h
index 3f56b1103d..78cebef59b 100644
--- a/lib/power/power_common.h
+++ b/lib/power/power_common.h
@@ -23,6 +23,13 @@ extern int rte_power_logtype;
 #define POWER_DEBUG_LOG(...)
 #endif
 
+#define RTE_POWER_VALID_LCOREID_OR_ERR_RET(lcore_id, retval) do { \
+	if (!rte_lcore_is_eal_managed(lcore_id)) { \
+		POWER_LOG(ERR, "lcore id %u is invalid", lcore_id); \
+		return retval; \
+	} \
+} while (0)
+
 /* check if scaling driver matches one we want */
 __rte_internal
 int cpufreq_check_scaling_driver(const char *driver);
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 01/15] eal: add interface to check if lcore is EAL managed
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Add a new helper function rte_lcore_is_eal_managed() to determine
if a logical core is managed by EAL.

This interface returns true if the lcore role is either ROLE_RTE
(standard worker/main cores) or ROLE_SERVICE (service cores).

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 lib/eal/common/eal_common_lcore.c | 11 +++++++++++
 lib/eal/include/rte_lcore.h       | 11 +++++++++++
 2 files changed, 22 insertions(+)

diff --git a/lib/eal/common/eal_common_lcore.c b/lib/eal/common/eal_common_lcore.c
index 39411f9370..dfbd96875a 100644
--- a/lib/eal/common/eal_common_lcore.c
+++ b/lib/eal/common/eal_common_lcore.c
@@ -102,6 +102,17 @@ int rte_lcore_is_enabled(unsigned int lcore_id)
 	return cfg->lcore_role[lcore_id] == ROLE_RTE;
 }
 
+RTE_EXPORT_SYMBOL(rte_lcore_is_eal_managed)
+int rte_lcore_is_eal_managed(unsigned int lcore_id)
+{
+	struct rte_config *cfg = rte_eal_get_configuration();
+
+	if (lcore_id >= RTE_MAX_LCORE)
+		return 0;
+	return cfg->lcore_role[lcore_id] == ROLE_RTE ||
+		cfg->lcore_role[lcore_id] == ROLE_SERVICE;
+}
+
 RTE_EXPORT_SYMBOL(rte_get_next_lcore)
 unsigned int rte_get_next_lcore(unsigned int i, int skip_main, int wrap)
 {
diff --git a/lib/eal/include/rte_lcore.h b/lib/eal/include/rte_lcore.h
index 10f965b4f0..cbfd50a936 100644
--- a/lib/eal/include/rte_lcore.h
+++ b/lib/eal/include/rte_lcore.h
@@ -195,6 +195,17 @@ rte_cpuset_t rte_lcore_cpuset(unsigned int lcore_id);
  */
 int rte_lcore_is_enabled(unsigned int lcore_id);
 
+/**
+ * Test if an lcore is ROLE_RTE or ROLE_SERVICE.
+ *
+ * @param lcore_id
+ *   The identifier of the lcore, which MUST be between 0 and
+ *   RTE_MAX_LCORE-1.
+ * @return
+ *   True if the given lcore is ROLE_RTE or ROLE_SERVICE; false otherwise.
+ */
+int rte_lcore_is_eal_managed(unsigned int lcore_id);
+
 /**
  * Get the next enabled lcore ID.
  *
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 05/15] power/cppc_cpufreq: validate lcore role in cpufreq API
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Currently, the cppc_cpufreq driver only checks if the lcore_id is
within the RTE_MAX_LCORE range, but fails to verify if the core is
actually active or managed by the application. This lacks sufficient
validation.

This patch add a lcore role check to the cpufreq-related APIs.
Although service cores do not typically invoke these APIs, they may
operate in polling states where power management is required.
To maintain compatibility with applications using service cores, the
validation logic now explicitly allows both ROLE_RTE and ROLE_SERVICE.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/cppc/cppc_cpufreq.c | 56 +++++++++++++++----------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/drivers/power/cppc/cppc_cpufreq.c b/drivers/power/cppc/cppc_cpufreq.c
index 3cd4165c83..7e1298110a 100644
--- a/drivers/power/cppc/cppc_cpufreq.c
+++ b/drivers/power/cppc/cppc_cpufreq.c
@@ -346,9 +346,8 @@ power_cppc_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -429,11 +428,11 @@ power_cppc_cpufreq_exit(unsigned int lcore_id)
 	struct cppc_power_info *pi;
 	uint32_t exp_state;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_USED;
 	/* The power in use state works as a guard variable between
@@ -479,8 +478,8 @@ power_cppc_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return 0;
 	}
 
@@ -502,8 +501,8 @@ power_cppc_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 uint32_t
 power_cppc_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return RTE_POWER_INVALID_FREQ_INDEX;
 	}
 
@@ -513,8 +512,8 @@ power_cppc_cpufreq_get_freq(unsigned int lcore_id)
 int
 power_cppc_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -526,8 +525,8 @@ power_cppc_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -544,8 +543,8 @@ power_cppc_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -561,8 +560,8 @@ power_cppc_cpufreq_freq_up(unsigned int lcore_id)
 int
 power_cppc_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -585,8 +584,8 @@ power_cppc_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -601,8 +600,8 @@ power_cppc_turbo_status(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -616,8 +615,8 @@ power_cppc_enable_turbo(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -652,8 +651,8 @@ power_cppc_disable_turbo(unsigned int lcore_id)
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -680,10 +679,11 @@ power_cppc_get_capabilities(unsigned int lcore_id,
 {
 	struct cppc_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 03/15] power/acpi_cpufreq: validate lcore role in cpufreq API
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Currently, the acpi_cpufreq driver only checks if the lcore_id is
within the RTE_MAX_LCORE range, but fails to verify if the core is
actually active or managed by the application. This lacks sufficient
validation.

This patch add a lcore role check to the cpufreq-related APIs.
Although service cores do not typically invoke these APIs, they may
operate in polling states where power management is required.
To maintain compatibility with applications using service cores, the
validation logic now explicitly allows both ROLE_RTE and ROLE_SERVICE.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/acpi/acpi_cpufreq.c | 56 +++++++++++++++----------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/drivers/power/acpi/acpi_cpufreq.c b/drivers/power/acpi/acpi_cpufreq.c
index 81a5e3f6ea..155b30fcf7 100644
--- a/drivers/power/acpi/acpi_cpufreq.c
+++ b/drivers/power/acpi/acpi_cpufreq.c
@@ -242,9 +242,8 @@ power_acpi_cpufreq_init(unsigned int lcore_id)
 		return -1;
 	}
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -319,11 +318,11 @@ power_acpi_cpufreq_exit(unsigned int lcore_id)
 	struct acpi_power_info *pi;
 	uint32_t exp_state;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
-				lcore_id, RTE_MAX_LCORE - 1U);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	pi = &lcore_power_info[lcore_id];
 	exp_state = POWER_USED;
 	/* The power in use state works as a guard variable between
@@ -373,8 +372,8 @@ power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return 0;
 	}
 
@@ -396,8 +395,8 @@ power_acpi_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
 uint32_t
 power_acpi_cpufreq_get_freq(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return RTE_POWER_INVALID_FREQ_INDEX;
 	}
 
@@ -407,8 +406,8 @@ power_acpi_cpufreq_get_freq(unsigned int lcore_id)
 int
 power_acpi_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -420,8 +419,8 @@ power_acpi_cpufreq_freq_down(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -438,8 +437,8 @@ power_acpi_cpufreq_freq_up(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -455,8 +454,8 @@ power_acpi_cpufreq_freq_up(unsigned int lcore_id)
 int
 power_acpi_cpufreq_freq_max(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -479,8 +478,8 @@ power_acpi_cpufreq_freq_min(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -496,8 +495,8 @@ power_acpi_turbo_status(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -512,8 +511,8 @@ power_acpi_enable_turbo(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -545,8 +544,8 @@ power_acpi_disable_turbo(unsigned int lcore_id)
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
 
@@ -572,10 +571,11 @@ int power_acpi_get_capabilities(unsigned int lcore_id,
 {
 	struct acpi_power_info *pi;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Invalid lcore ID");
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	if (caps == NULL) {
 		POWER_LOG(ERR, "Invalid argument");
 		return -1;
-- 
2.33.0


^ permalink raw reply related

* [PATCH V2 00/15] power: unify and improve lcore ID verification
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260416030612.2379407-1-lihuisong@huawei.com>

This patch series reworks the lcore ID verification logic within the
power library to ensure consistency and improve maintainability.

Currently, various cpufreq drivers implement their own lcore ID checks,
which are limited to simple range validation and involve significant
code duplication. Moreover, these checks do not account for whether the
core is actually managed by the application.

For the verification in cpufreq-related APIs and power QoS APIs, although
service cores do not typically invoke these APIs, they may operate in
polling modes where power management is required. To maintain compatibility
with applications using service cores, the validation logic now explicitly
allows both ROLE_RTE and ROLE_SERVICE.

But the lcore ID in the pmd_mgmt library must be ROLE_RTE because it is
mainly used together with the data plane of ethdev PMD. So use
rte_lcore_is_enabled to verify.

Key Changes:
1. Add lcore role verification to individual cpufreq drivers.
2. Introduces a unified macro in the power library to standardize lcore ID
   checks.
3. Moves verification logic from individualdrivers to the framework level.
   This reduces code duplication.
4. Allow the service cores to configure power QoS.
5. Use rte_lcore_is_enabled to verfify the lcore ID in pmd_mgmt.

---
v2:
 - Allow the service cores to set power API.

---

Huisong Li (15):
  eal: add interface to check if lcore is EAL managed
  power/kvm_vm: validate lcore role in cpufreq API
  power/acpi_cpufreq: validate lcore role in cpufreq API
  power/amd_pstate: validate lcore role in cpufreq API
  power/cppc_cpufreq: validate lcore role in cpufreq API
  power/intel_pstate: validate lcore role in cpufreq API
  power: add a common macro to verify lcore ID
  power/cpufreq: add the lcore ID verification to framework
  power/acpi_cpufreq: remove the verification of lcore ID
  power/amd_pstate: remove the verification of lcore ID
  power/cppc_cpufreq: remove the verification of lcore ID
  power/intel_pstate: remove the verification of lcore ID
  power/kvm_vm: remove the verification of lcore ID
  power: allow the service core to config power QoS
  power: add lcore ID check for PMD mgmt

 drivers/power/acpi/acpi_cpufreq.c             | 65 -------------------
 drivers/power/amd_pstate/amd_pstate_cpufreq.c | 65 -------------------
 drivers/power/cppc/cppc_cpufreq.c             | 65 -------------------
 .../power/intel_pstate/intel_pstate_cpufreq.c | 65 -------------------
 drivers/power/kvm_vm/kvm_vm.c                 | 10 ---
 lib/eal/common/eal_common_lcore.c             | 11 ++++
 lib/eal/include/rte_lcore.h                   | 11 ++++
 lib/power/power_common.h                      |  7 ++
 lib/power/rte_power_cpufreq.c                 | 14 +++-
 lib/power/rte_power_pmd_mgmt.c                | 21 +++---
 lib/power/rte_power_qos.c                     | 10 +--
 11 files changed, 55 insertions(+), 289 deletions(-)

-- 
2.33.0


^ permalink raw reply

* [PATCH V2 02/15] power/kvm_vm: validate lcore role in cpufreq API
From: Huisong Li @ 2026-05-07  2:42 UTC (permalink / raw)
  To: anatoly.burakov, sivaprasad.tummala, stephen
  Cc: dev, thomas, fengchengwen, yangxingui, zhanjie9, lihuisong
In-Reply-To: <20260507024230.1198111-1-lihuisong@huawei.com>

Currently, the kvm_vm driver only checks if the lcore_id is within
the RTE_MAX_LCORE range, but fails to verify if the core is actually
active or managed by the application. This lacks sufficient validation.

This patch add a lcore role check to the cpufreq-related APIs.
Although service cores do not typically invoke these APIs, they may
operate in polling states where power management is required.
To maintain compatibility with applications using service cores, the
validation logic now explicitly allows both ROLE_RTE and ROLE_SERVICE.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
---
 drivers/power/kvm_vm/kvm_vm.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/power/kvm_vm/kvm_vm.c b/drivers/power/kvm_vm/kvm_vm.c
index 5754a441cd..133d1f0882 100644
--- a/drivers/power/kvm_vm/kvm_vm.c
+++ b/drivers/power/kvm_vm/kvm_vm.c
@@ -24,11 +24,11 @@ power_kvm_vm_check_supported(void)
 int
 power_kvm_vm_init(unsigned int lcore_id)
 {
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Core(%u) is out of range 0...%d",
-				lcore_id, RTE_MAX_LCORE-1);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	pkt[lcore_id].command = RTE_POWER_CPU_POWER;
 	pkt[lcore_id].resource_id = lcore_id;
 	return guest_channel_host_connect(FD_PATH, lcore_id);
@@ -73,11 +73,11 @@ send_msg(unsigned int lcore_id, uint32_t scale_direction)
 {
 	int ret;
 
-	if (lcore_id >= RTE_MAX_LCORE) {
-		POWER_LOG(ERR, "Core(%u) is out of range 0...%d",
-				lcore_id, RTE_MAX_LCORE-1);
+	if (!rte_lcore_is_eal_managed(lcore_id)) {
+		POWER_LOG(ERR, "lcore id %u is invalid.", lcore_id);
 		return -1;
 	}
+
 	pkt[lcore_id].unit = scale_direction;
 	ret = guest_channel_send_msg(&pkt[lcore_id], lcore_id);
 	if (ret == 0)
-- 
2.33.0


^ permalink raw reply related

* Re: [PATCH v11 0/9] Add Linkdata sxe2 driver
From: Stephen Hemminger @ 2026-05-07  2:40 UTC (permalink / raw)
  To: liujie5; +Cc: dev
In-Reply-To: <20260507014454.1370039-1-liujie5@linkdatatechnology.com>

On Thu,  7 May 2026 09:44:40 +0800
liujie5@linkdatatechnology.com wrote:

> From: Jie Liu <liujie5@linkdatatechnology.com>
> 
> V11:
>  - Addressed AI comments

Not better. v11 is byte-for-byte identical to v10 except:

The vector Rx/Tx patch (10/10) was dropped, so it's now a 9-patch series.
One trailing blank line was removed from the data-path patch (now 9/9).

^ permalink raw reply

* [PATCH v11 8/9] net/sxe2: support queue setup and control
From: liujie5 @ 2026-05-07  1:44 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260507014454.1370039-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Add support for Rx and Tx queue setup, release, and management.
Implement eth_dev_ops callbacks for rx_queue_setup, tx_queue_setup,
rx_queue_release, and tx_queue_release.

This includes:
- Allocating memory for hardware ring descriptors.
- Initializing software ring structures and hardware head/tail pointers.
- Implementing proper resource cleanup logic to prevent memory leaks
  during queue reconfiguration or device close.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build   |   2 +
 drivers/net/sxe2/sxe2_ethdev.c |  64 +++-
 drivers/net/sxe2/sxe2_ethdev.h |   3 +
 drivers/net/sxe2/sxe2_rx.c     | 579 +++++++++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_rx.h     |  34 ++
 drivers/net/sxe2/sxe2_tx.c     | 447 +++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_tx.h     |  32 ++
 7 files changed, 1143 insertions(+), 18 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_rx.c
 create mode 100644 drivers/net/sxe2/sxe2_rx.h
 create mode 100644 drivers/net/sxe2/sxe2_tx.c
 create mode 100644 drivers/net/sxe2/sxe2_tx.h

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 98d0b7fc6d..61467a4e31 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -23,6 +23,8 @@ sources += files(
         'sxe2_cmd_chnl.c',
         'sxe2_vsi.c',
         'sxe2_queue.c',
+        'sxe2_tx.c',
+        'sxe2_rx.c',
 )
 
 allow_internal_get_api = true
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index fa6304ebbc..c1a65f25ce 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -24,6 +24,8 @@
 #include "sxe2_ethdev.h"
 #include "sxe2_drv_cmd.h"
 #include "sxe2_cmd_chnl.h"
+#include "sxe2_tx.h"
+#include "sxe2_rx.h"
 #include "sxe2_common.h"
 #include "sxe2_common_log.h"
 #include "sxe2_host_regs.h"
@@ -80,14 +82,6 @@ static s32 sxe2_dev_configure(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static void __rte_cold sxe2_txqs_all_stop(struct rte_eth_dev *dev __rte_unused)
-{
-}
-
-static void __rte_cold sxe2_rxqs_all_stop(struct rte_eth_dev *dev __rte_unused)
-{
-}
-
 static s32 sxe2_dev_stop(struct rte_eth_dev *dev)
 {
 	s32 ret = SXE2_SUCCESS;
@@ -106,16 +100,6 @@ static s32 sxe2_dev_stop(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static s32 __rte_cold sxe2_txqs_all_start(struct rte_eth_dev *dev __rte_unused)
-{
-	return 0;
-}
-
-static s32 __rte_cold sxe2_rxqs_all_start(struct rte_eth_dev *dev __rte_unused)
-{
-	return 0;
-}
-
 static s32 sxe2_queues_start(struct rte_eth_dev *dev)
 {
 	s32 ret = SXE2_SUCCESS;
@@ -318,6 +302,12 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.dev_stop                   = sxe2_dev_stop,
 	.dev_close                  = sxe2_dev_close,
 	.dev_infos_get              = sxe2_dev_infos_get,
+
+	.rx_queue_setup             = sxe2_rx_queue_setup,
+	.tx_queue_setup             = sxe2_tx_queue_setup,
+
+	.rxq_info_get               = sxe2_rx_queue_info_get,
+	.txq_info_get               = sxe2_tx_queue_info_get,
 };
 
 struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
@@ -345,6 +335,44 @@ struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter
 	return bar_info;
 }
 
+void __iomem *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter,
+		enum sxe2_pci_map_resource res_type, u16 idx_in_func)
+{
+	struct sxe2_pci_map_context *map_ctxt = &adapter->map_ctxt;
+	struct sxe2_pci_map_segment_info *seg_info = NULL;
+	struct sxe2_pci_map_bar_info *bar_info = NULL;
+	void __iomem *addr = NULL;
+	u8 reg_width = 0;
+	u8 i = 0;
+
+	bar_info = sxe2_dev_get_bar_info(adapter, res_type);
+	if (bar_info == NULL) {
+		PMD_DEV_LOG_WARN(adapter, INIT, "Failed to get bar info, res_type=[%d]",
+				res_type);
+		goto l_end;
+	}
+	seg_info = bar_info->seg_info;
+
+	reg_width = map_ctxt->addr_info[res_type].reg_width;
+	if (reg_width == 0) {
+		PMD_DEV_LOG_WARN(adapter, INIT, "Invalid reg width with resource type %d",
+				res_type);
+		goto l_end;
+	}
+
+	for (i = 0; i < bar_info->map_cnt; i++) {
+		seg_info = &bar_info->seg_info[i];
+		if (res_type == seg_info->type) {
+			addr = (void __iomem *)((uintptr_t)seg_info->addr +
+					seg_info->page_inner_offset + reg_width	* idx_in_func);
+			goto l_end;
+		}
+	}
+
+l_end:
+	return addr;
+}
+
 static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter,
 			struct sxe2_drv_dev_caps_resp *dev_caps)
 {
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index fb7813ef80..7999e4f331 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -295,6 +295,9 @@ struct sxe2_adapter {
 #define SXE2_DEV_TO_PCI(eth_dev) \
 		RTE_DEV_TO_PCI((eth_dev)->device)
 
+void __iomem *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter,
+		enum sxe2_pci_map_resource res_type, u16 idx_in_func);
+
 struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
 		enum sxe2_pci_map_resource res_type);
 
diff --git a/drivers/net/sxe2/sxe2_rx.c b/drivers/net/sxe2/sxe2_rx.c
new file mode 100644
index 0000000000..00e24fc361
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_rx.c
@@ -0,0 +1,579 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <ethdev_driver.h>
+#include <rte_net.h>
+#include <rte_vect.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+
+#include "sxe2_ethdev.h"
+#include "sxe2_queue.h"
+#include "sxe2_rx.h"
+#include "sxe2_cmd_chnl.h"
+
+#include "sxe2_osal.h"
+#include "sxe2_common_log.h"
+
+static void __iomem *sxe2_rx_doorbell_tail_addr_get(struct sxe2_adapter *adapter, u16 queue_id)
+{
+	return sxe2_pci_map_addr_get(adapter, SXE2_PCI_MAP_RES_DOORBELL_RX_TAIL, queue_id);
+}
+
+static void sxe2_rx_head_tail_init(struct sxe2_adapter *adapter, struct sxe2_rx_queue *rxq)
+{
+	rxq->rdt_reg_addr = sxe2_rx_doorbell_tail_addr_get(adapter, rxq->queue_id);
+	SXE2_PCI_REG_WRITE_WC(rxq->rdt_reg_addr, 0);
+}
+
+static void __rte_cold sxe2_rx_queue_reset(struct sxe2_rx_queue *rxq)
+{
+	u16 i = 0;
+	u16 len = 0;
+	static const union sxe2_rx_desc zeroed_desc = {{0}};
+
+	len = rxq->ring_depth + SXE2_RX_PKTS_BURST_BATCH_NUM;
+	for (i = 0; i < len; ++i)
+		rxq->desc_ring[i] = zeroed_desc;
+
+	memset(&rxq->fake_mbuf, 0, sizeof(rxq->fake_mbuf));
+	for (i = rxq->ring_depth; i < len; i++)
+		rxq->buffer_ring[i] = &rxq->fake_mbuf;
+
+	rxq->hold_num            = 0;
+	rxq->next_ret_pkt        = 0;
+	rxq->processing_idx      = 0;
+	rxq->completed_pkts_num  = 0;
+	rxq->batch_alloc_trigger = rxq->rx_free_thresh - 1;
+
+	rxq->pkt_first_seg = NULL;
+	rxq->pkt_last_seg  = NULL;
+
+	rxq->realloc_num   = 0;
+	rxq->realloc_start = 0;
+}
+
+void __rte_cold sxe2_rx_queue_mbufs_release(struct sxe2_rx_queue *rxq)
+{
+	u16 i;
+
+	if (rxq->buffer_ring != NULL) {
+		for (i = 0; i < rxq->ring_depth; i++) {
+			if (rxq->buffer_ring[i] != NULL) {
+				rte_pktmbuf_free(rxq->buffer_ring[i]);
+				rxq->buffer_ring[i] = NULL;
+			}
+		}
+	}
+
+	if (rxq->completed_pkts_num) {
+		for (i = 0; i < rxq->completed_pkts_num; ++i) {
+			if (rxq->completed_buf[rxq->next_ret_pkt + i] != NULL) {
+				rte_pktmbuf_free(rxq->completed_buf[rxq->next_ret_pkt + i]);
+				rxq->completed_buf[rxq->next_ret_pkt + i] = NULL;
+			}
+		}
+		rxq->completed_pkts_num = 0;
+	}
+}
+
+const struct sxe2_rxq_ops sxe2_default_rxq_ops = {
+	.queue_reset      = sxe2_rx_queue_reset,
+	.mbufs_release    = sxe2_rx_queue_mbufs_release,
+};
+
+static struct sxe2_rxq_ops sxe2_rx_default_ops_get(void)
+{
+	return sxe2_default_rxq_ops;
+}
+
+void __rte_cold sxe2_rx_queue_info_get(struct rte_eth_dev *dev,
+		u16 queue_id, struct rte_eth_rxq_info *qinfo)
+{
+	struct sxe2_rx_queue *rxq = NULL;
+
+	if (queue_id >= dev->data->nb_rx_queues) {
+		PMD_LOG_ERR(RX, "rx queue:%u is out of range:%u",
+			queue_id, dev->data->nb_rx_queues);
+		goto end;
+	}
+
+	rxq = dev->data->rx_queues[queue_id];
+	if (rxq == NULL) {
+		PMD_LOG_ERR(RX, "rx queue:%u is NULL", queue_id);
+		goto end;
+	}
+
+	qinfo->mp           = rxq->mb_pool;
+	qinfo->nb_desc      = rxq->ring_depth;
+	qinfo->scattered_rx = dev->data->scattered_rx;
+	qinfo->conf.rx_free_thresh    = rxq->rx_free_thresh;
+	qinfo->conf.rx_drop_en        = rxq->drop_en;
+	qinfo->conf.rx_deferred_start = rxq->rx_deferred_start;
+
+end:
+	return;
+}
+
+s32 __rte_cold sxe2_rx_queue_stop(struct rte_eth_dev *dev, u16 rx_queue_id)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rx_queue *rxq;
+	s32 ret;
+	PMD_INIT_FUNC_TRACE();
+
+	if (rx_queue_id >= dev->data->nb_rx_queues) {
+		PMD_LOG_ERR(RX, "Rx queue %u is out of range %u",
+			    rx_queue_id, dev->data->nb_rx_queues);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (dev->data->rx_queue_state[rx_queue_id] ==
+			RTE_ETH_QUEUE_STATE_STOPPED) {
+		ret = SXE2_SUCCESS;
+		goto l_end;
+	}
+
+	rxq = dev->data->rx_queues[rx_queue_id];
+	if (rxq == NULL) {
+		ret = SXE2_SUCCESS;
+		goto l_end;
+	}
+	ret = sxe2_drv_rxq_switch(adapter, rxq, false);
+	if (ret) {
+		PMD_LOG_ERR(RX, "Failed to switch rx queue %u off, ret = %d",
+				rx_queue_id, ret);
+		if (ret == SXE2_ERR_PERM)
+			goto l_free;
+		goto l_end;
+	}
+
+l_free:
+	rxq->ops.mbufs_release(rxq);
+	rxq->ops.queue_reset(rxq);
+	dev->data->rx_queue_state[rx_queue_id] =
+		RTE_ETH_QUEUE_STATE_STOPPED;
+l_end:
+	return ret;
+}
+
+static void __rte_cold sxe2_rx_queue_free(struct sxe2_rx_queue *rxq)
+{
+	if (rxq != NULL) {
+		rxq->ops.mbufs_release(rxq);
+		if (rxq->buffer_ring != NULL) {
+			rte_free(rxq->buffer_ring);
+			rxq->buffer_ring = NULL;
+		}
+		rte_memzone_free(rxq->mz);
+		rte_free(rxq);
+	}
+}
+
+void __rte_cold sxe2_rx_queue_release(struct rte_eth_dev *dev,
+					u16 queue_idx)
+{
+	(void)sxe2_rx_queue_stop(dev, queue_idx);
+	sxe2_rx_queue_free(dev->data->rx_queues[queue_idx]);
+	dev->data->rx_queues[queue_idx] = NULL;
+}
+
+void __rte_cold sxe2_all_rxqs_release(struct rte_eth_dev *dev)
+{
+	struct rte_eth_dev_data *data = dev->data;
+	u16 nb_rxq;
+
+	for (nb_rxq = 0; nb_rxq < data->nb_rx_queues; nb_rxq++) {
+		if (data->rx_queues[nb_rxq] == NULL)
+			continue;
+		sxe2_rx_queue_release(dev, nb_rxq);
+		data->rx_queues[nb_rxq] = NULL;
+	}
+	data->nb_rx_queues = 0;
+}
+
+static struct sxe2_rx_queue *sxe2_rx_queue_alloc(struct rte_eth_dev *dev, u16 queue_idx,
+		u16 ring_depth, u32 socket_id)
+{
+	struct sxe2_rx_queue *rxq;
+	const struct rte_memzone *tz;
+	u16 len;
+
+	if (dev->data->rx_queues[queue_idx] != NULL) {
+		sxe2_rx_queue_release(dev, queue_idx);
+		dev->data->rx_queues[queue_idx] = NULL;
+	}
+
+	rxq = rte_zmalloc_socket("rx_queue", sizeof(*rxq),
+				 RTE_CACHE_LINE_SIZE, socket_id);
+
+	if (rxq == NULL) {
+		PMD_LOG_ERR(RX, "rx queue[%d] alloc failed", queue_idx);
+		goto l_end;
+	}
+
+	rxq->ring_depth = ring_depth;
+	len = rxq->ring_depth + SXE2_RX_PKTS_BURST_BATCH_NUM;
+
+	rxq->buffer_ring = rte_zmalloc_socket("rx_buffer_ring",
+					  sizeof(struct rte_mbuf *) * len,
+					  RTE_CACHE_LINE_SIZE, socket_id);
+
+	if (!rxq->buffer_ring) {
+		PMD_LOG_ERR(RX, "Rxq malloc mbuf mem failed");
+		sxe2_rx_queue_release(dev, queue_idx);
+		rte_free(rxq);
+		rxq = NULL;
+		goto l_end;
+	}
+
+	tz = rte_eth_dma_zone_reserve(dev, "rx_dma", queue_idx,
+					SXE2_RX_RING_SIZE, SXE2_DESC_ADDR_ALIGN, socket_id);
+	if (tz == NULL) {
+		PMD_LOG_ERR(RX, "Rxq malloc desc mem failed");
+		sxe2_rx_queue_release(dev, queue_idx);
+		rte_free(rxq->buffer_ring);
+		rxq->buffer_ring = NULL;
+		rte_free(rxq);
+		rxq = NULL;
+		goto l_end;
+	}
+
+	rxq->mz = tz;
+	memset(tz->addr, 0, SXE2_RX_RING_SIZE);
+	rxq->base_addr = tz->iova;
+	rxq->desc_ring = (union sxe2_rx_desc *)tz->addr;
+
+l_end:
+	return rxq;
+}
+
+s32 __rte_cold sxe2_rx_queue_setup(struct rte_eth_dev *dev,
+			u16 queue_idx, u16 nb_desc, u32 socket_id,
+			const struct rte_eth_rxconf *rx_conf,
+			struct rte_mempool *mp)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi *vsi = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_rx_queue *rxq;
+	u64 offloads;
+	s32 ret;
+	u16 rx_nseg;
+	u16 i;
+
+	PMD_INIT_FUNC_TRACE();
+
+	if (queue_idx >= dev->data->nb_rx_queues) {
+		PMD_LOG_ERR(RX, "Rx queue %u is out of range %u",
+					queue_idx, dev->data->nb_rx_queues);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (nb_desc % SXE2_RX_DESC_RING_ALIGN != 0 ||
+		nb_desc > SXE2_MAX_RING_DESC ||
+		nb_desc < SXE2_MIN_RING_DESC) {
+		PMD_LOG_ERR(RX, "param desc num:%u is invalid", nb_desc);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (mp != NULL)
+		rx_nseg = 1;
+	else
+		rx_nseg = rx_conf->rx_nseg;
+
+	offloads = rx_conf->offloads | dev->data->dev_conf.rxmode.offloads;
+
+	if (rx_nseg > 1 && !(offloads & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT)) {
+		PMD_LOG_ERR(RX, "Port %u queue %u Buffer split offload not configured, but rx_nseg is %u",
+					dev->data->port_id, queue_idx, rx_nseg);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if ((offloads & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT) && !(rx_nseg > 1)) {
+		PMD_LOG_ERR(RX, "Port %u queue %u Buffer split offload configured, but rx_nseg is %u",
+					dev->data->port_id, queue_idx, rx_nseg);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if ((offloads & RTE_ETH_RX_OFFLOAD_TCP_LRO) &&
+		(offloads & RTE_ETH_RX_OFFLOAD_KEEP_CRC)) {
+		PMD_LOG_ERR(RX, "port_id %u queue %u, LRO can't be configure with Keep crc.",
+					dev->data->port_id, queue_idx);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	rxq = sxe2_rx_queue_alloc(dev, queue_idx, nb_desc, socket_id);
+	if (rxq == NULL) {
+		PMD_LOG_ERR(RX, "rx queue[%d] resource alloc failed", queue_idx);
+		ret = SXE2_ERR_NO_MEMORY;
+		goto l_end;
+	}
+
+	if (offloads & RTE_ETH_RX_OFFLOAD_TCP_LRO)
+		dev->data->lro = 1;
+
+	if (rx_nseg > 1) {
+		for (i = 0; i < rx_nseg; i++) {
+			rte_memcpy(&rxq->rx_seg[i], &rx_conf->rx_seg[i].split,
+					sizeof(struct rte_eth_rxseg_split));
+		}
+		rxq->mb_pool = rxq->rx_seg[0].mp;
+	} else {
+		rxq->mb_pool = mp;
+	}
+
+	rxq->rx_free_thresh = rx_conf->rx_free_thresh;
+	rxq->port_id = dev->data->port_id;
+	rxq->offloads = offloads;
+	if (offloads & RTE_ETH_RX_OFFLOAD_KEEP_CRC)
+		rxq->crc_len = RTE_ETHER_CRC_LEN;
+	else
+		rxq->crc_len = 0;
+
+	rxq->queue_id = queue_idx;
+	rxq->idx_in_func = vsi->rxqs.base_idx_in_func + queue_idx;
+	rxq->drop_en = rx_conf->rx_drop_en;
+	rxq->rx_deferred_start = rx_conf->rx_deferred_start;
+	rxq->vsi = vsi;
+	rxq->ops = sxe2_rx_default_ops_get();
+	rxq->ops.queue_reset(rxq);
+	dev->data->rx_queues[queue_idx] = rxq;
+
+	ret = SXE2_SUCCESS;
+l_end:
+	return ret;
+}
+
+struct rte_mbuf *sxe2_mbuf_raw_alloc(struct rte_mempool *mp)
+{
+	return rte_mbuf_raw_alloc(mp);
+}
+
+static s32 __rte_cold sxe2_rx_queue_mbufs_alloc(struct sxe2_rx_queue *rxq)
+{
+	struct rte_mbuf **buf_ring = rxq->buffer_ring;
+	struct rte_mbuf *mbuf = NULL;
+	struct rte_mbuf *mbuf_pay;
+	volatile union sxe2_rx_desc *desc;
+	u64 dma_addr;
+	s32 ret;
+	u16 i, j;
+
+	for (i = 0; i < rxq->ring_depth; i++) {
+		mbuf = sxe2_mbuf_raw_alloc(rxq->mb_pool);
+		if (mbuf == NULL) {
+			PMD_LOG_ERR(RX, "Rx queue is not available or setup");
+			ret = SXE2_ERR_NO_MEMORY;
+			goto l_err_free_mbuf;
+		}
+		buf_ring[i] = mbuf;
+		mbuf->data_off = RTE_PKTMBUF_HEADROOM;
+		mbuf->nb_segs = 1;
+		mbuf->port = rxq->port_id;
+
+		dma_addr = rte_cpu_to_le_64(rte_mbuf_data_iova_default(mbuf));
+		desc = &rxq->desc_ring[i];
+		if (!(rxq->offloads & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT)) {
+			desc->read.hdr_addr = 0;
+			desc->read.pkt_addr = dma_addr;
+		} else {
+			mbuf_pay = rte_mbuf_raw_alloc(rxq->rx_seg[1].mp);
+			if (unlikely(!mbuf_pay)) {
+				PMD_LOG_ERR(RX, "Failed to allocate payload mbuf for RX");
+				ret = SXE2_ERR_NO_MEMORY;
+				goto l_err_free_mbuf;
+			}
+
+			mbuf_pay->next = NULL;
+			mbuf_pay->data_off = RTE_PKTMBUF_HEADROOM;
+			mbuf_pay->nb_segs = 1;
+			mbuf_pay->port = rxq->port_id;
+			mbuf->next = mbuf_pay;
+
+			desc->read.hdr_addr = dma_addr;
+			desc->read.pkt_addr =
+				rte_cpu_to_le_64(rte_mbuf_data_iova_default(mbuf_pay));
+		}
+
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+		desc->read.rsvd1 = 0;
+		desc->read.rsvd2 = 0;
+#endif
+	}
+
+	ret = SXE2_SUCCESS;
+	goto l_end;
+
+l_err_free_mbuf:
+	for (j = 0; j <= i; j++) {
+		if (buf_ring[j] != NULL && buf_ring[j]->next != NULL) {
+			rte_pktmbuf_free(buf_ring[j]->next);
+			buf_ring[j]->next = NULL;
+		}
+
+		if (buf_ring[j] != NULL) {
+			rte_pktmbuf_free(buf_ring[j]);
+			buf_ring[j] = NULL;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+s32 __rte_cold sxe2_rx_queue_start(struct rte_eth_dev *dev, u16 rx_queue_id)
+{
+	struct sxe2_rx_queue *rxq;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	s32 ret;
+	PMD_INIT_FUNC_TRACE();
+
+	if (rx_queue_id >= dev->data->nb_rx_queues) {
+		PMD_LOG_ERR(RX, "Rx queue %u is out of range %u",
+				rx_queue_id, dev->data->nb_rx_queues);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	rxq = dev->data->rx_queues[rx_queue_id];
+	if (rxq == NULL) {
+		PMD_LOG_ERR(RX, "Rx queue %u is not available or setup",
+				rx_queue_id);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (dev->data->rx_queue_state[rx_queue_id] ==
+			RTE_ETH_QUEUE_STATE_STARTED) {
+		ret = SXE2_SUCCESS;
+		goto l_end;
+	}
+
+	ret = sxe2_rx_queue_mbufs_alloc(rxq);
+	if (ret) {
+		PMD_LOG_ERR(RX, "Rx queue %u apply desc ring fail",
+			rx_queue_id);
+		ret =  SXE2_ERR_NO_MEMORY;
+		goto l_end;
+	}
+
+	sxe2_rx_head_tail_init(adapter, rxq);
+
+	ret = sxe2_drv_rxq_ctxt_cfg(adapter, rxq, 1);
+	if (ret) {
+		PMD_LOG_ERR(RX, "Rx queue %u config ctxt fail, ret=%d",
+			rx_queue_id, ret);
+
+		(void)sxe2_drv_rxq_switch(adapter, rxq, false);
+		rxq->ops.mbufs_release(rxq);
+		rxq->ops.queue_reset(rxq);
+		goto l_end;
+	}
+
+	SXE2_PCI_REG_WRITE_WC(rxq->rdt_reg_addr, rxq->ring_depth - 1);
+	dev->data->rx_queue_state[rx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED;
+
+l_end:
+	return  ret;
+}
+
+s32 __rte_cold sxe2_rxqs_all_start(struct rte_eth_dev *dev __rte_unused)
+{
+	struct rte_eth_dev_data *data = dev->data;
+	struct sxe2_rx_queue *rxq;
+	u16 nb_rxq;
+	u16 nb_started_rxq;
+	s32 ret;
+	PMD_INIT_FUNC_TRACE();
+
+	for (nb_rxq = 0; nb_rxq < data->nb_rx_queues; nb_rxq++) {
+		rxq = dev->data->rx_queues[nb_rxq];
+		if (!rxq || rxq->rx_deferred_start)
+			continue;
+
+		ret = sxe2_rx_queue_start(dev, nb_rxq);
+		if (ret) {
+			PMD_LOG_ERR(RX, "Fail to start rx queue %u", nb_rxq);
+			goto l_free_started_queue;
+		}
+
+		rte_atomic_store_explicit(&rxq->sw_stats.pkts, 0,
+			rte_memory_order_relaxed);
+		rte_atomic_store_explicit(&rxq->sw_stats.bytes, 0,
+			rte_memory_order_relaxed);
+		rte_atomic_store_explicit(&rxq->sw_stats.drop_pkts, 0,
+			rte_memory_order_relaxed);
+		rte_atomic_store_explicit(&rxq->sw_stats.drop_bytes, 0,
+			rte_memory_order_relaxed);
+		rte_atomic_store_explicit(&rxq->sw_stats.unicast_pkts, 0,
+			rte_memory_order_relaxed);
+		rte_atomic_store_explicit(&rxq->sw_stats.broadcast_pkts, 0,
+			rte_memory_order_relaxed);
+		rte_atomic_store_explicit(&rxq->sw_stats.multicast_pkts, 0,
+			rte_memory_order_relaxed);
+	}
+	ret = SXE2_SUCCESS;
+	goto l_end;
+
+l_free_started_queue:
+	for (nb_started_rxq = 0; nb_started_rxq <= nb_rxq; nb_started_rxq++)
+		(void)sxe2_rx_queue_stop(dev, nb_started_rxq);
+l_end:
+	return ret;
+}
+
+void __rte_cold sxe2_rxqs_all_stop(struct rte_eth_dev *dev __rte_unused)
+{
+	struct rte_eth_dev_data *data = dev->data;
+	struct sxe2_adapter *adapter  = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi     *vsi      = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_stats   *sw_stats_prev = &vsi->vsi_stats.vsi_sw_stats_prev;
+	struct sxe2_rx_queue *rxq = NULL;
+	s32 ret;
+	u16 nb_rxq;
+	PMD_INIT_FUNC_TRACE();
+
+	for (nb_rxq = 0; nb_rxq < data->nb_rx_queues; nb_rxq++) {
+		ret = sxe2_rx_queue_stop(dev, nb_rxq);
+		if (ret) {
+			PMD_LOG_ERR(RX, "Fail to start rx queue %u", nb_rxq);
+			continue;
+		}
+
+		rxq = dev->data->rx_queues[nb_rxq];
+		if (rxq) {
+			sw_stats_prev->ipackets +=
+				rte_atomic_load_explicit(&rxq->sw_stats.pkts,
+					rte_memory_order_relaxed);
+			sw_stats_prev->ierrors +=
+				rte_atomic_load_explicit(&rxq->sw_stats.drop_pkts,
+					rte_memory_order_relaxed);
+			sw_stats_prev->ibytes +=
+				rte_atomic_load_explicit(&rxq->sw_stats.bytes,
+					rte_memory_order_relaxed);
+
+			sw_stats_prev->rx_sw_unicast_packets +=
+				rte_atomic_load_explicit(&rxq->sw_stats.unicast_pkts,
+					rte_memory_order_relaxed);
+			sw_stats_prev->rx_sw_broadcast_packets +=
+				rte_atomic_load_explicit(&rxq->sw_stats.broadcast_pkts,
+					rte_memory_order_relaxed);
+			sw_stats_prev->rx_sw_multicast_packets +=
+				rte_atomic_load_explicit(&rxq->sw_stats.multicast_pkts,
+					rte_memory_order_relaxed);
+			sw_stats_prev->rx_sw_drop_packets +=
+				rte_atomic_load_explicit(&rxq->sw_stats.drop_pkts,
+					rte_memory_order_relaxed);
+			sw_stats_prev->rx_sw_drop_bytes +=
+				rte_atomic_load_explicit(&rxq->sw_stats.drop_bytes,
+					rte_memory_order_relaxed);
+		}
+	}
+}
diff --git a/drivers/net/sxe2/sxe2_rx.h b/drivers/net/sxe2/sxe2_rx.h
new file mode 100644
index 0000000000..7c6239b387
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_rx.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_RX_H__
+#define __SXE2_RX_H__
+
+#include "sxe2_queue.h"
+
+s32 __rte_cold sxe2_rx_queue_setup(struct rte_eth_dev *dev,
+				u16 queue_idx, u16 nb_desc, u32 socket_id,
+				const struct rte_eth_rxconf *rx_conf,
+				struct rte_mempool *mp);
+
+s32 __rte_cold sxe2_rx_queue_stop(struct rte_eth_dev *dev, u16 rx_queue_id);
+
+void __rte_cold sxe2_rx_queue_mbufs_release(struct sxe2_rx_queue *rxq);
+
+void __rte_cold sxe2_rx_queue_release(struct rte_eth_dev *dev, u16 queue_idx);
+
+void __rte_cold sxe2_all_rxqs_release(struct rte_eth_dev *dev);
+
+void __rte_cold sxe2_rx_queue_info_get(struct rte_eth_dev *dev, u16 queue_id,
+		struct rte_eth_rxq_info *qinfo);
+
+s32 __rte_cold sxe2_rx_queue_start(struct rte_eth_dev *dev, u16 rx_queue_id);
+
+s32 __rte_cold sxe2_rxqs_all_start(struct rte_eth_dev *dev);
+
+void __rte_cold sxe2_rxqs_all_stop(struct rte_eth_dev *dev);
+
+struct rte_mbuf *sxe2_mbuf_raw_alloc(struct rte_mempool *mp);
+
+#endif
diff --git a/drivers/net/sxe2/sxe2_tx.c b/drivers/net/sxe2/sxe2_tx.c
new file mode 100644
index 0000000000..7e4dd74a51
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_tx.c
@@ -0,0 +1,447 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_common.h>
+#include <rte_net.h>
+#include <rte_vect.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <ethdev_driver.h>
+#include "sxe2_tx.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_common_log.h"
+#include "sxe2_errno.h"
+#include "sxe2_cmd_chnl.h"
+
+static void __iomem *sxe2_tx_doorbell_addr_get(struct sxe2_adapter *adapter, u16 queue_id)
+{
+	return sxe2_pci_map_addr_get(adapter, SXE2_PCI_MAP_RES_DOORBELL_TX, queue_id);
+}
+
+static void sxe2_tx_tail_init(struct sxe2_adapter *adapter, struct sxe2_tx_queue *txq)
+{
+	txq->tdt_reg_addr = sxe2_tx_doorbell_addr_get(adapter, txq->queue_id);
+	SXE2_PCI_REG_WRITE_WC(txq->tdt_reg_addr, 0);
+}
+
+void __rte_cold sxe2_tx_queue_reset(struct sxe2_tx_queue *txq)
+{
+	u16 prev, i;
+	volatile union sxe2_tx_data_desc *txd;
+	static const union sxe2_tx_data_desc zeroed_desc = {{0}};
+	struct sxe2_tx_buffer *tx_buffer = txq->buffer_ring;
+
+	for (i = 0; i < txq->ring_depth; i++)
+		txq->desc_ring[i] = zeroed_desc;
+
+	prev = txq->ring_depth - 1;
+	for (i = 0; i < txq->ring_depth; i++) {
+		txd = &txq->desc_ring[i];
+		if (txd == NULL)
+			continue;
+
+		txd->wb.dd = rte_cpu_to_le_64(SXE2_TX_DESC_DTYPE_DESC_DONE);
+		tx_buffer[i].mbuf       = NULL;
+		tx_buffer[i].last_id    = i;
+		tx_buffer[prev].next_id = i;
+		prev = i;
+	}
+
+	txq->desc_used_num = 0;
+	txq->desc_free_num = txq->ring_depth - 1;
+	txq->next_use      = 0;
+	txq->next_clean    = txq->ring_depth - 1;
+	txq->next_dd       = txq->rs_thresh  - 1;
+	txq->next_rs       = txq->rs_thresh  - 1;
+}
+
+void __rte_cold sxe2_tx_queue_mbufs_release(struct sxe2_tx_queue *txq)
+{
+	u32 i;
+
+	if (txq != NULL && txq->buffer_ring != NULL) {
+		for (i = 0; i < txq->ring_depth; i++) {
+			if (txq->buffer_ring[i].mbuf != NULL) {
+				rte_pktmbuf_free_seg(txq->buffer_ring[i].mbuf);
+				txq->buffer_ring[i].mbuf = NULL;
+			}
+		}
+	}
+}
+
+static void sxe2_tx_buffer_ring_free(struct sxe2_tx_queue *txq)
+{
+	if (txq != NULL && txq->buffer_ring != NULL)
+		rte_free(txq->buffer_ring);
+}
+
+const struct sxe2_txq_ops sxe2_default_txq_ops = {
+	.queue_reset      = sxe2_tx_queue_reset,
+	.mbufs_release    = sxe2_tx_queue_mbufs_release,
+	.buffer_ring_free = sxe2_tx_buffer_ring_free,
+};
+
+static struct sxe2_txq_ops sxe2_tx_default_ops_get(void)
+{
+	return sxe2_default_txq_ops;
+}
+
+static s32 sxe2_txq_arg_validate(struct rte_eth_dev *dev, u16 ring_depth,
+		u16 *rs_thresh, u16 *free_thresh, const struct rte_eth_txconf *tx_conf)
+{
+	s32 ret = SXE2_SUCCESS;
+
+	if ((ring_depth % SXE2_TX_DESC_RING_ALIGN) != 0 ||
+		ring_depth > SXE2_MAX_RING_DESC ||
+		ring_depth < SXE2_MIN_RING_DESC) {
+		PMD_LOG_ERR(TX, "number:%u of receive descriptors is invalid", ring_depth);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	*free_thresh = (u16)((tx_conf->tx_free_thresh) ?
+			tx_conf->tx_free_thresh : DEFAULT_TX_FREE_THRESH);
+	*rs_thresh   = (u16)((tx_conf->tx_rs_thresh) ?
+			tx_conf->tx_rs_thresh : DEFAULT_TX_RS_THRESH);
+
+	if (*rs_thresh >= (ring_depth - 2)) {
+		PMD_LOG_ERR(TX, "tx_rs_thresh must be less than the number "
+			"of tx descriptors minus 2. (tx_rs_thresh:%u port:%u)",
+			*rs_thresh, dev->data->port_id);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (*free_thresh >= (ring_depth - 3)) {
+		PMD_LOG_ERR(TX, "tx_free_thresh must be less than the number "
+			"of tx descriptors minus 3. (tx_free_thresh:%u port:%u)",
+			*free_thresh, dev->data->port_id);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (*rs_thresh > *free_thresh) {
+		PMD_LOG_ERR(TX, "tx_rs_thresh must be less than or equal to "
+			"tx_free_thresh. (tx_free_thresh:%u tx_rs_thresh:%u port:%u)",
+			*free_thresh, *rs_thresh, dev->data->port_id);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if ((ring_depth % *rs_thresh) != 0) {
+		PMD_LOG_ERR(TX, "tx_rs_thresh must be a divisor of the "
+			"number of tx descriptors. (tx_rs_thresh:%u port:%d ring_depth:%u)",
+			*rs_thresh, dev->data->port_id, ring_depth);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	ret = SXE2_SUCCESS;
+
+l_end:
+	return ret;
+}
+
+void __rte_cold sxe2_tx_queue_info_get(struct rte_eth_dev *dev, u16 queue_id,
+		struct rte_eth_txq_info *qinfo)
+{
+	struct sxe2_tx_queue *txq = NULL;
+
+	if (queue_id >= dev->data->nb_tx_queues) {
+		PMD_LOG_ERR(TX, "tx queue:%u is out of range:%u",
+				queue_id, dev->data->nb_tx_queues);
+		goto end;
+	}
+
+	txq = dev->data->tx_queues[queue_id];
+	if (txq == NULL) {
+		PMD_LOG_WARN(TX, "tx queue:%u is NULL", queue_id);
+		goto end;
+	}
+
+	qinfo->nb_desc                = txq->ring_depth;
+
+	qinfo->conf.tx_thresh.pthresh = txq->pthresh;
+	qinfo->conf.tx_thresh.hthresh = txq->hthresh;
+	qinfo->conf.tx_thresh.wthresh = txq->wthresh;
+	qinfo->conf.tx_free_thresh    = txq->free_thresh;
+	qinfo->conf.tx_rs_thresh      = txq->rs_thresh;
+	qinfo->conf.offloads          = txq->offloads;
+	qinfo->conf.tx_deferred_start = txq->tx_deferred_start;
+
+end:
+	return;
+}
+
+s32 __rte_cold sxe2_tx_queue_stop(struct rte_eth_dev *dev, u16 queue_id)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tx_queue *txq;
+	s32 ret;
+	PMD_INIT_FUNC_TRACE();
+
+	if (queue_id >= dev->data->nb_tx_queues) {
+		PMD_LOG_ERR(TX, "tx queue:%u is out of range:%u",
+			queue_id, dev->data->nb_tx_queues);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (dev->data->tx_queue_state[queue_id] ==
+			RTE_ETH_QUEUE_STATE_STOPPED) {
+		ret = SXE2_SUCCESS;
+		goto l_end;
+	}
+
+	txq = dev->data->tx_queues[queue_id];
+	if (txq == NULL) {
+		ret = SXE2_SUCCESS;
+		goto l_end;
+	}
+
+	ret = sxe2_drv_txq_switch(adapter, txq, false);
+	if (ret) {
+		PMD_LOG_ERR(TX, "Failed to switch tx queue %u off",
+				queue_id);
+		goto l_end;
+	}
+
+	txq->ops.mbufs_release(txq);
+	txq->ops.queue_reset(txq);
+	dev->data->tx_queue_state[queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
+	ret = SXE2_SUCCESS;
+
+l_end:
+	return ret;
+}
+
+static void __rte_cold sxe2_tx_queue_free(struct sxe2_tx_queue *txq)
+{
+	if (txq != NULL) {
+		txq->ops.mbufs_release(txq);
+		txq->ops.buffer_ring_free(txq);
+
+		rte_memzone_free(txq->mz);
+		rte_free(txq);
+	}
+}
+
+void __rte_cold sxe2_tx_queue_release(struct rte_eth_dev *dev, u16 queue_idx)
+{
+	(void)sxe2_tx_queue_stop(dev, queue_idx);
+	sxe2_tx_queue_free(dev->data->tx_queues[queue_idx]);
+	dev->data->tx_queues[queue_idx] = NULL;
+}
+
+void __rte_cold sxe2_all_txqs_release(struct rte_eth_dev *dev)
+{
+	struct rte_eth_dev_data *data = dev->data;
+	u16 nb_txq;
+
+	for (nb_txq = 0; nb_txq < data->nb_tx_queues; nb_txq++) {
+		if (data->tx_queues[nb_txq] == NULL)
+			continue;
+
+		sxe2_tx_queue_release(dev, nb_txq);
+		data->tx_queues[nb_txq] = NULL;
+	}
+	data->nb_tx_queues = 0;
+}
+
+static struct sxe2_tx_queue
+*sxe2_tx_queue_alloc(struct rte_eth_dev *dev, u16 queue_idx,
+		u16 ring_depth, u32 socket_id)
+{
+	struct sxe2_tx_queue *txq;
+	const struct rte_memzone *tz;
+
+	if (dev->data->tx_queues[queue_idx]) {
+		sxe2_tx_queue_release(dev, queue_idx);
+		dev->data->tx_queues[queue_idx] = NULL;
+	}
+
+	txq = rte_zmalloc_socket("tx_queue", sizeof(struct sxe2_tx_queue),
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (txq == NULL) {
+		PMD_LOG_ERR(TX, "tx queue:%d alloc failed", queue_idx);
+		goto l_end;
+	}
+
+	tz = rte_eth_dma_zone_reserve(dev, "tx_dma", queue_idx,
+			sizeof(union sxe2_tx_data_desc) * SXE2_MAX_RING_DESC,
+			SXE2_DESC_ADDR_ALIGN, socket_id);
+	if (tz == NULL) {
+		PMD_LOG_ERR(TX, "tx desc ring alloc failed, queue_id:%d", queue_idx);
+		rte_free(txq);
+		txq = NULL;
+		goto l_end;
+	}
+
+	txq->buffer_ring = rte_zmalloc_socket("tx_buffer_ring",
+		sizeof(struct sxe2_tx_buffer) * ring_depth,
+		RTE_CACHE_LINE_SIZE, socket_id);
+	if (txq->buffer_ring == NULL) {
+		PMD_LOG_ERR(TX, "tx buffer alloc failed, queue_id:%d", queue_idx);
+		rte_memzone_free(tz);
+		rte_free(txq);
+		txq = NULL;
+		goto l_end;
+	}
+
+	txq->mz = tz;
+	txq->base_addr = tz->iova;
+	txq->desc_ring = (volatile union sxe2_tx_data_desc *)tz->addr;
+
+l_end:
+	return txq;
+}
+
+s32 __rte_cold sxe2_tx_queue_setup(struct rte_eth_dev *dev,
+		u16 queue_idx, u16 nb_desc, u32 socket_id,
+		const struct rte_eth_txconf *tx_conf)
+{
+	s32 ret = SXE2_SUCCESS;
+	u16 tx_rs_thresh;
+	u16 tx_free_thresh;
+	struct sxe2_tx_queue *txq;
+	struct sxe2_adapter  *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi      *vsi     = adapter->vsi_ctxt.main_vsi;
+	u64 offloads;
+	PMD_INIT_FUNC_TRACE();
+
+	if (queue_idx >= dev->data->nb_tx_queues) {
+		PMD_LOG_ERR(TX, "tx queue:%u is out of range:%u",
+			queue_idx, dev->data->nb_tx_queues);
+		ret = SXE2_ERR_INVAL;
+		goto end;
+	}
+
+	ret = sxe2_txq_arg_validate(dev, nb_desc, &tx_rs_thresh, &tx_free_thresh, tx_conf);
+	if (ret) {
+		PMD_LOG_ERR(TX, "tx queue:%u arg validate failed", queue_idx);
+		goto end;
+	}
+
+	offloads = tx_conf->offloads | dev->data->dev_conf.txmode.offloads;
+
+	txq = sxe2_tx_queue_alloc(dev, queue_idx, nb_desc, socket_id);
+	if (txq == NULL) {
+		PMD_LOG_ERR(TX, "failed to alloc sxe2vf tx queue:%u resource", queue_idx);
+		ret = SXE2_ERR_NO_MEMORY;
+		goto end;
+	}
+
+	txq->vlan_flag         = SXE2_TX_FLAGS_VLAN_TAG_LOC_L2TAG1;
+	txq->ring_depth        = nb_desc;
+	txq->rs_thresh         = tx_rs_thresh;
+	txq->free_thresh       = tx_free_thresh;
+	txq->pthresh           = tx_conf->tx_thresh.pthresh;
+	txq->hthresh           = tx_conf->tx_thresh.hthresh;
+	txq->wthresh           = tx_conf->tx_thresh.wthresh;
+	txq->queue_id          = queue_idx;
+	txq->idx_in_func       = vsi->txqs.base_idx_in_func + queue_idx;
+	txq->port_id           = dev->data->port_id;
+	txq->offloads          = offloads;
+	txq->tx_deferred_start = tx_conf->tx_deferred_start;
+	txq->vsi               = vsi;
+	txq->ops               = sxe2_tx_default_ops_get();
+	txq->ops.queue_reset(txq);
+
+	dev->data->tx_queues[queue_idx] = txq;
+	ret = SXE2_SUCCESS;
+
+end:
+	return ret;
+}
+
+s32 __rte_cold sxe2_tx_queue_start(struct rte_eth_dev *dev, u16 queue_id)
+{
+	s32    ret = SXE2_SUCCESS;
+	struct sxe2_tx_queue *txq;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	PMD_INIT_FUNC_TRACE();
+
+	if (queue_id >= dev->data->nb_tx_queues) {
+		PMD_LOG_ERR(TX, "tx queue:%u is out of range:%u",
+			queue_id, dev->data->nb_tx_queues);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	if (dev->data->tx_queue_state[queue_id] == RTE_ETH_QUEUE_STATE_STARTED) {
+		ret = SXE2_SUCCESS;
+		goto l_end;
+	}
+
+	txq = dev->data->tx_queues[queue_id];
+	if (txq == NULL) {
+		PMD_LOG_ERR(TX, "tx queue:%u is not available or setup", queue_id);
+		ret = SXE2_ERR_INVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_drv_txq_ctxt_cfg(adapter, txq, 1);
+	if (ret) {
+		PMD_LOG_ERR(TX, "tx queue:%u config ctxt fail", queue_id);
+
+		(void)sxe2_drv_txq_switch(adapter, txq, false);
+		txq->ops.mbufs_release(txq);
+		txq->ops.queue_reset(txq);
+		goto l_end;
+	}
+
+	sxe2_tx_tail_init(adapter, txq);
+
+	dev->data->tx_queue_state[queue_id] = RTE_ETH_QUEUE_STATE_STARTED;
+	ret = SXE2_SUCCESS;
+
+l_end:
+	return ret;
+}
+
+s32 __rte_cold sxe2_txqs_all_start(struct rte_eth_dev *dev __rte_unused)
+{
+struct rte_eth_dev_data *data = dev->data;
+	struct sxe2_tx_queue *txq;
+	u16 nb_txq;
+	u16 nb_started_txq;
+	s32 ret;
+	PMD_INIT_FUNC_TRACE();
+
+	for (nb_txq = 0; nb_txq < data->nb_tx_queues; nb_txq++) {
+		txq = dev->data->tx_queues[nb_txq];
+		if (!txq || txq->tx_deferred_start)
+			continue;
+
+		ret = sxe2_tx_queue_start(dev, nb_txq);
+		if (ret) {
+			PMD_LOG_ERR(TX, "Fail to start tx queue %u", nb_txq);
+			goto l_free_started_queue;
+		}
+	}
+	ret = SXE2_SUCCESS;
+	goto l_end;
+
+l_free_started_queue:
+	for (nb_started_txq = 0; nb_started_txq <= nb_txq; nb_started_txq++)
+		(void)sxe2_tx_queue_stop(dev, nb_started_txq);
+
+l_end:
+	return ret;
+}
+
+void __rte_cold sxe2_txqs_all_stop(struct rte_eth_dev *dev __rte_unused)
+{
+	struct rte_eth_dev_data *data = dev->data;
+	u16 nb_txq;
+	s32 ret;
+
+	for (nb_txq = 0; nb_txq < data->nb_tx_queues; nb_txq++) {
+		ret = sxe2_tx_queue_stop(dev, nb_txq);
+		if (ret) {
+			PMD_LOG_WARN(TX, "Fail to stop tx queue %u", nb_txq);
+			continue;
+		}
+	}
+}
diff --git a/drivers/net/sxe2/sxe2_tx.h b/drivers/net/sxe2/sxe2_tx.h
new file mode 100644
index 0000000000..58b668e337
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_tx.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_TX_H__
+#define __SXE2_TX_H__
+#include "sxe2_queue.h"
+
+void __rte_cold sxe2_tx_queue_reset(struct sxe2_tx_queue *txq);
+
+s32 __rte_cold sxe2_tx_queue_start(struct rte_eth_dev *dev, u16 queue_id);
+
+void sxe2_tx_queue_mbufs_release(struct sxe2_tx_queue *txq);
+
+s32 __rte_cold sxe2_tx_queue_stop(struct rte_eth_dev *dev, u16 queue_id);
+
+s32 __rte_cold sxe2_tx_queue_setup(struct rte_eth_dev *dev,
+		u16 queue_idx, u16 nb_desc, u32 socket_id,
+		const struct rte_eth_txconf *tx_conf);
+
+void __rte_cold sxe2_tx_queue_release(struct rte_eth_dev *dev, u16 queue_idx);
+
+void __rte_cold sxe2_all_txqs_release(struct rte_eth_dev *dev);
+
+void __rte_cold sxe2_tx_queue_info_get(struct rte_eth_dev *dev, u16 queue_id,
+		struct rte_eth_txq_info *qinfo);
+
+s32 __rte_cold sxe2_txqs_all_start(struct rte_eth_dev *dev);
+
+void __rte_cold sxe2_txqs_all_stop(struct rte_eth_dev *dev);
+
+#endif
-- 
2.47.3


^ 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