Power-optimized RX for Ethernet devices
Subsystems (3)
cover Power-optimized RX for Ethernet devices
This patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive, along with a set of (hopefully generic) intrinsics that facilitate that. This is achieved through cooperation with the NIC driver that will allow us to know address of the next NIC RX ring packet descriptor, and wait for writes on it.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism
used: we're counting empty polls, and if a certain threshold is reached, we get the address of next RX ring descriptor from the NIC driver, arm the monitoring hardware, and enter a power-optimized state. We will then wake up when either a timeout happens, or a write happens (or generally whenever CPU feels like waking up - this is platform- specific), and proceed as normal. The empty poll counter is reset whenever we actually get packets, so we only go to sleep when we know nothing is going on.
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it wit minimal changes to the application code. The current approach allows to just flip a switch and automagically have power savings.
There are certain limitations in this patchset right now:
- Currently, only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Currently, power management is enabled per-port, not per-queue
- There is potential to greatly increase TX latency if we are buffering things, and go to sleep before sending packets
- The API is not perfect and could use some improvement and discussion
- The API doesn't extend to other device types
- The intrinsics are platform-specific, so ethdev has some platform-specific code in it
- Support was only implemented for devices using net/ixgbe, net/i40e and net/ice drivers
Hopefully this would generate enough feedback to clear a path forward!
Anatoly Burakov (6):
eal: add power management intrinsics
ethdev: add simple power management API net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API app/testpmd: add command for power management on a port
app/test-pmd/cmdline.c | 48 +++++++ drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 23 +++ drivers/net/i40e/i40e_rxtx.h | 2 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 23 +++ drivers/net/ice/ice_rxtx.h | 2 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 22 +++ drivers/net/ixgbe/ixgbe_rxtx.h | 2 + .../include/generic/rte_power_intrinsics.h | 64 +++++++++ lib/librte_eal/include/meson.build | 1 + lib/librte_eal/x86/include/meson.build | 1 + lib/librte_eal/x86/include/rte_cpuflags.h | 1 + .../x86/include/rte_power_intrinsics.h | 134 ++++++++++++++++++ lib/librte_eal/x86/rte_cpuflags.c | 2 + lib/librte_ethdev/rte_ethdev.c | 39 +++++ lib/librte_ethdev/rte_ethdev.h | 70 +++++++++ lib/librte_ethdev/rte_ethdev_core.h | 41 +++++- lib/librte_ethdev/rte_ethdev_version.map | 4 + 20 files changed, 480 insertions(+), 2 deletions(-) create mode 100644 lib/librte_eal/include/generic/rte_power_intrinsics.h create mode 100644 lib/librte_eal/x86/include/rte_power_intrinsics.h
Signature · 1 line
2.17.1
RFC 1/6 eal: add power management intrinsics
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
--- .../include/generic/rte_power_intrinsics.h | 64 +++++++++ lib/librte_eal/include/meson.build | 1 + lib/librte_eal/x86/include/meson.build | 1 + lib/librte_eal/x86/include/rte_cpuflags.h | 1 + .../x86/include/rte_power_intrinsics.h | 134 ++++++++++++++++++ lib/librte_eal/x86/rte_cpuflags.c | 2 + 6 files changed, 203 insertions(+) create mode 100644 lib/librte_eal/include/generic/rte_power_intrinsics.h create mode 100644 lib/librte_eal/x86/include/rte_power_intrinsics.h
lib/librte_eal/include/generic/rte_power_intrinsics.h new file +64 -0
@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2020 Intel Corporation +*/ + +#ifndef _RTE_POWER_INTRINSIC_H_ +#define _RTE_POWER_INTRINSIC_H_ + +#include <inttypes.h> + +/** +* @file +* Advanced power management operations. +* +* This file define APIs for advanced power management, +* which are architecture-dependent. +*/ + +/** +* Monitor specific address for changes. This will cause the CPU to enter an +* architecture-defined optimized power state until either the specified +* memory address is written to, or a certain TSC timestamp is reached. +* +* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If +* mask is non-zero, the current value pointed to by the `p` pointer will be +* checked against the expected value, and if they match, the entering of +* optimized power state may be aborted. +* +* @param p +* Address to monitor for changes. Must be aligned on an 8-byte boundary. +* @param expected_value +* Before attempting the monitoring, the `p` address may be read and compared +* against this value. If `value_mask` is zero, this step will be skipped. +* @param value_mask +* The 64-bit mask to use to extract current value from `p`. +* @param state +* Architecture-dependent optimized power state number +* @param tsc_timestamp +* Maximum TSC timestamp to wait for. Note that the wait behavior is +* architecture-dependent. +* +* @return +* Architecture-dependent return value. +*/ +static inline int rte_power_monitor (const volatile void *p, +const uint64_t expected_value,const uint64_t value_mask, +const uint32_t state,const uint64_t tsc_timestamp); + +/** +* Enter an architecture-defined optimized power state until a certain TSC +* timestamp is reached. +* +* @param state +* Architecture-dependent optimized power state number +* @param tsc_timestamp +* Maximum TSC timestamp to wait for. Note that the wait behavior is +* architecture-dependent. +* +* @return +* Architecture-dependent return value. +*/ +static inline int rte_power_pause (const uint32_t state, +const uint64_t tsc_timestamp); + +#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/include/meson.build +1 -0
@@ -59,6 +59,7 @@ generic_headers = files( 'generic/rte_memcpy.h', 'generic/rte_pause.h', 'generic/rte_prefetch.h', + 'generic/rte_power_intrinsics.h', 'generic/rte_rwlock.h', 'generic/rte_spinlock.h', 'generic/rte_ticketlock.h',
lib/librte_eal/x86/include/meson.build +1 -0
@@ -13,6 +13,7 @@ arch_headers = files( 'rte_io.h', 'rte_memcpy.h', 'rte_prefetch.h', + 'rte_power_intrinsics.h', 'rte_pause.h', 'rte_rtm.h', 'rte_rwlock.h',
lib/librte_eal/x86/include/rte_cpuflags.h +1 -0
@@ -110,6 +110,7 @@ enum rte_cpu_flag_t {RTE_CPUFLAG_RDTSCP ,/**< RDTSCP */ RTE_CPUFLAG_EM64T ,/**< EM64T */ +RTE_CPUFLAG_WAITPKG ,/**< UMINITOR/UMWAIT/TPAUSE */ /* (EAX 80000007h) EDX features */ RTE_CPUFLAG_INVTSC, /**< INVTSC */
lib/librte_eal/x86/include/rte_power_intrinsics.h new file +134 -0
134 lines not on this page · raw message
lib/librte_eal/x86/rte_cpuflags.c +2 -0
@@ -110,6 +110,8 @@ const struct feature_entry rte_cpu_feature_table[] = { FEAT_DEF(AVX512F, 0x00000007, 0, RTE_REG_EBX, 16) FEAT_DEF(RDSEED, 0x00000007, 0, RTE_REG_EBX, 18) + FEAT_DEF(WAITPKG, 0x00000007, 0, RTE_REG_ECX, 5) + FEAT_DEF(LAHF_SAHF, 0x80000001, 0, RTE_REG_ECX, 0)FEAT_DEF (LZCNT ,0x80000001 ,0 ,RTE_REG_ECX ,4 )
Signature · 1 line
2.17.1
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
Recently ARM guys introduced new generic API
for similar (as I understand) purposes: rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
Konstantin
Quoted 275 lines
Signed-off-by Liang J. Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>--- .../include/generic/rte_power_intrinsics.h | 64 +++++++++ lib/librte_eal/include/meson.build | 1 + lib/librte_eal/x86/include/meson.build | 1 + lib/librte_eal/x86/include/rte_cpuflags.h | 1 + .../x86/include/rte_power_intrinsics.h | 134 ++++++++++++++++++ lib/librte_eal/x86/rte_cpuflags.c | 2 + 6 files changed, 203 insertions(+) create mode 100644 lib/librte_eal/include/generic/rte_power_intrinsics.h create mode 100644 lib/librte_eal/x86/include/rte_power_intrinsics.hlib/librte_eal/include/generic/rte_power_intrinsics.h new file +64
-0@@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2020 Intel Corporation +*/ + +#ifndef _RTE_POWER_INTRINSIC_H_ +#define _RTE_POWER_INTRINSIC_H_ + +#include <inttypes.h> + +/** +* @file +* Advanced power management operations. +* +* This file define APIs for advanced power management, +* which are architecture-dependent. +*/ + +/** +* Monitor specific address for changes. This will cause the CPU to enter an +* architecture-defined optimized power state until either the specified +* memory address is written to, or a certain TSC timestamp is reached. +* +* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If +* mask is non-zero, the current value pointed to by the `p` pointer will be +* checked against the expected value, and if they match, the entering of +* optimized power state may be aborted. +* +* @param p +* Address to monitor for changes. Must be aligned on an 8-byte boundary. +* @param expected_value +* Before attempting the monitoring, the `p` address may be read and compared +* against this value. If `value_mask` is zero, this step will be skipped. +* @param value_mask +* The 64-bit mask to use to extract current value from `p`. +* @param state +* Architecture-dependent optimized power state number +* @param tsc_timestamp +* Maximum TSC timestamp to wait for. Note that the wait behavior is +* architecture-dependent. +* +* @return +* Architecture-dependent return value. +*/ +static inline int rte_power_monitor (const volatile void *p, +const uint64_t expected_value,const uint64_t value_mask, +const uint32_t state,const uint64_t tsc_timestamp); + +/** +* Enter an architecture-defined optimized power state until a certain TSC +* timestamp is reached. +* +* @param state +* Architecture-dependent optimized power state number +* @param tsc_timestamp +* Maximum TSC timestamp to wait for. Note that the wait behavior is +* architecture-dependent. +* +* @return +* Architecture-dependent return value. +*/ +static inline int rte_power_pause (const uint32_t state, +const uint64_t tsc_timestamp); + +#endif /* _RTE_POWER_INTRINSIC_H_ */ lib/librte_eal/include/meson.build +1
-0@@ -59,6 +59,7 @@ generic_headers = files( 'generic/rte_memcpy.h', 'generic/rte_pause.h', 'generic/rte_prefetch.h', + 'generic/rte_power_intrinsics.h', 'generic/rte_rwlock.h', 'generic/rte_spinlock.h', 'generic/rte_ticketlock.h',lib/librte_eal/x86/include/meson.build +1
-0@@ -13,6 +13,7 @@ arch_headers = files( 'rte_io.h', 'rte_memcpy.h', 'rte_prefetch.h', + 'rte_power_intrinsics.h', 'rte_pause.h', 'rte_rtm.h', 'rte_rwlock.h',lib/librte_eal/x86/include/rte_cpuflags.h +1
-0@@ -110,6 +110,7 @@ enum rte_cpu_flag_t {RTE_CPUFLAG_RDTSCP ,/**< RDTSCP */ RTE_CPUFLAG_EM64T ,/**< EM64T */ +RTE_CPUFLAG_WAITPKG ,/**< UMINITOR/UMWAIT/TPAUSE */ /* (EAX 80000007h) EDX features */ RTE_CPUFLAG_INVTSC, /**< INVTSC */lib/librte_eal/x86/include/rte_power_intrinsics.h new file +134
-0@@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2020 Intel Corporation +*/ + +#ifndef _RTE_POWER_INTRINSIC_X86_64_H_ +#define _RTE_POWER_INTRINSIC_X86_64_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <rte_atomic.h> +#include <rte_common.h> + +#include "generic/rte_power_intrinsics.h" + +/** +* Monitor specific address for changes. This will cause the CPU to enter an +* architecture-defined optimized power state until either the specified +* memory address is written to, or a certain TSC timestamp is reached. +* +* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If +* mask is non-zero, the current value pointed to by the `p` pointer will be +* checked against the expected value, and if they match, the entering of +* optimized power state may be aborted. +* +* This function uses UMONITOR/UMWAIT instructions. For more information about +* their usage, please refer to Intel(R) 64 and IA-32 Architectures Software +* Developer's Manual. +* +* @param p +* Address to monitor for changes. Must be aligned on an 8-byte boundary. +* @param expected_value +* Before attempting the monitoring, the `p` address may be read and compared +* against this value. If `value_mask` is zero, this step will be skipped. +* @param value_mask +* The 64-bit mask to use to extract current value from `p`. +* @param state +* Architecture-dependent optimized power state number. Can be 0 (C0.2) or +* 1 (C0.1). +* @param tsc_timestamp +* Maximum TSC timestamp to wait for. +* +* @return +* - 1 if wakeup was due to TSC timeout expiration. +* - 0 if wakeup was due to memory write or other reasons. +*/ +static inline int rte_power_monitor (const volatile void *p, +const uint64_t expected_value,const uint64_t value_mask, +const uint32_t state,const uint64_t tsc_timestamp) +{ +const uint32_t tsc_l = (uint32_t )tsc_timestamp; +const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +uint64_t rflags; + +/* +* we're using raw byte codes for now as only the newest compiler +* versions support this instruction natively. +*/ + +/* set address for UMONITOR */ + asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" + : + :"D" (p)); +rte_mb (); +if (value_mask) { +const uint64_t cur_value = *(const volatile uint64_t *)p; +const uint64_t masked = cur_value & value_mask; +/* if the masked value is already matching, abort */ +if (masked == expected_value) +return 0 ; + } +/* execute UMWAIT */ + asmvolatile (".byte 0xf2, 0x0f, 0xae, 0xf7;\n" +/* +* UMWAIT sets CF flag in RFLAGS, so PUSHF to push them +* onto the stack, then pop them back into `rflags` so that +* we can read it. +*/ +"pushf;\n" +"pop %0;\n" + :"=r" (rflags) + :"D" (state),"a" (tsc_l),"d" (tsc_h)); + +/* we're interested in the first bit (the carry flag) */ +return rflags &0x1 ; +} + +/** +* Enter an architecture-defined optimized power state until a certain TSC +* timestamp is reached. +* +* This function uses TPAUSE instruction. For more information about its usage, +* please refer to Intel(R) 64 and IA-32 Architectures Software Developer's +* Manual. +* +* @param state +* Architecture-dependent optimized power state number. Can be 0 (C0.2) or +* 1 (C0.1). +* @param tsc_timestamp +* Maximum TSC timestamp to wait for. +* +* @return +* - 1 if wakeup was due to TSC timeout expiration. +* - 0 if wakeup was due to other reasons. +*/ +static inline int rte_power_pause (const uint32_t state, +const uint64_t tsc_timestamp) +{ +const uint32_t tsc_l = (uint32_t )tsc_timestamp; +const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +uint64_t rflags; + +/* execute TPAUSE */ + asmvolatile (".byte 0x66, 0x0f, 0xae, 0xf7;\n" +/* +* TPAUSE sets CF flag in RFLAGS, so PUSHF to push them +* onto the stack, then pop them back into `rflags` so that +* we can read it. +*/ +"pushf;\n" +"pop %0;\n" + :"=r" (rflags) + :"D" (state),"a" (tsc_l),"d" (tsc_h)); + +/* we're interested in the first bit (the carry flag) */ +return rflags &0x1 ; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _RTE_POWER_INTRINSIC_X86_64_H_ */ lib/librte_eal/x86/rte_cpuflags.c +2
-1@@ -110,6 +110,8 @@ const struct feature_entry rte_cpu_feature_table[] = { FEAT_DEF(AVX512F, 0x00000007, 0, RTE_REG_EBX, 16) FEAT_DEF(RDSEED, 0x00000007, 0, RTE_REG_EBX, 18) + FEAT_DEF(WAITPKG, 0x00000007, 0, RTE_REG_ECX, 5) + FEAT_DEF(LAHF_SAHF, 0x80000001, 0, RTE_REG_ECX, 0)FEAT_DEF (LZCNT ,0x80000001 ,0 ,RTE_REG_ECX ,4 ) --2.17.1
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
Quoted 15 lines · Konstantin
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
Quoted 5 lines
Recently ARM guys introduced new generic API
for similar (as I understand) purposes:rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
Konstantin
Hi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value. The "expected" value is there as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
Quoted 22 lines · Anatoly
Quoted 15 lines · Konstantin
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
Quoted 5 lines
Recently ARM guys introduced new generic API
for similar (as I understand) purposes:rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
Konstantin
Quoted 8 lines
Hi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value. The "expected" value is there as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.
Rather than the "expected" value, is it not more useful for a general API to check for the existing value? Since we are awaiting writes, we may not know what new value will be written, but we do know what the value is now, and can just check that it's not been changed.
Regards,
/Bruce
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
In reply to Burakov, AnatolyQuoted 22 lines
Quoted 16 lines
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
Quoted 5 lines
Recently ARM guys introduced new generic API
for similar (as I understand) purposes:rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
Konstantin
Quoted 4 lines
Hi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value.
I understand that.
Quoted 4 lines
The "expected" value is there
as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.
AFAIK, current rte_wait_until_equal_* does pretty much the same thing:
LDXR memaddr, $reg // an address to monitor for
if ($reg != expected_value)
SEVL // arm monitor
do {
WFE // waits for write to that memory address
LDXR memaddr, $reg
} while ($reg != expected_value);
Looks pretty similar to what rte_power_monitor() does, except you don't have a loop for checking the new value. Plus rte_power_monitor() provides extra options to the user - timestamp and power save mode to enter.
Also I don't know what is the granularity of such events on ARM, is it a cache-line or more/less.
Might be ARM people can comment/correct me here. Konstantin
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
Quoted 45 lines · Konstantin
Quoted 23 lines
Quoted 17 lines
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
Quoted 5 lines
Recently ARM guys introduced new generic API
for similar (as I understand) purposes:rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
KonstantinQuoted 4 lines
Hi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value.
I understand that.
Quoted 4 lines
The "expected" value is there
as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.AFAIK, current rte_wait_until_equal_* does pretty much the same thing:
LDXR memaddr, $reg // an address to monitor for
if ($reg !=expected_value)
SEVL // arm monitor
do {
WFE // waits for write to that memory address
LDXR memaddr, $reg
} while ($reg !=expected_value);
Quoted 6 lines
Looks pretty similar to what
rte_power_monitor()does, except you don't have a loop for checking the new value. Plusrte_power_monitor()provides extra options to the user - timestamp and power save mode to enter.
Also I don't know what is the granularity of such events on ARM, is it a cache-line or more/less.
As I understand it, Granularity is per the cache-line. ie. Load-exclusive(LDXR) followed by WFE will wait in a low-power state until the cache line is written.
But I see UMONITOR bit different, Where _without_ other core signaling to wakeup from wait state,
it can wake on TSC expiry. I think, that's is the main primitive on this feature. Right?
WFE can also wake based on Timer stream events(kind of TSC in x86 analogy) but it has a configuration
bit that needs to allow for this scheme in userspace(EL0) or not? defined by EL1(Linux kernel).
I am planning to spend time on this after understanding the value addition of the feature/usecase[1]
[1]
http://mails.dpdk.org/archives/dev/2020-May/168888.html
Quoted 2 lines
Might be ARM people can comment/correct me here.
Konstantin
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
Quoted 65 lines
Quoted 45 lines · Konstantin
Quoted 23 lines
Quoted 17 lines
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more writes are expected.
Quoted 5 lines
Recently ARM guys introduced new generic API
for similar (as I understand) purposes:rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
KonstantinQuoted 4 lines
Hi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value.
I understand that.
Quoted 4 lines
The "expected" value is there
as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.AFAIK, current rte_wait_until_equal_* does pretty much the same thing:
LDXR memaddr, $reg // an address to monitor for
if ($reg !=expected_value)
SEVL // arm monitor
do {
WFE // waits for write to that memory address
LDXR memaddr, $reg
} while ($reg !=expected_value);Quoted 6 lines
Looks pretty similar to what
rte_power_monitor()does, except you don't have a loop for checking the new value. Plusrte_power_monitor()provides extra options to the user - timestamp and power save mode to enter.
Also I don't know what is the granularity of such events on ARM, is it a cache-line or more/less.As I understand it, Granularity is per the cache-line. ie. Load-exclusive(LDXR) followed by WFE will wait in a low-power state until the cache line is written.
But I see UMONITOR bit different, Where _without_ other core signaling to wakeup from wait state,
it can wake on TSC expiry. I think, that's is the main primitive on this feature. Right?
Quoted 9 lines
WFE can also wake based on Timer stream events(kind of TSC in x86 analogy) but it has a configuration
bit that needs to allow for this scheme in userspace(EL0) or not? defined by EL1(Linux kernel).
I am planning to spend time on this after understanding the value addition of the feature/usecase[1]
[1]
http://mails.dpdk.org/archives/dev/2020-May/168888.html
Ok, if there is a consensus to keep these two APIs disjoint for now - I wouldn't insist.
Konstantin
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
In reply to Jerin Jacob<snip>
Quoted 56 lines
Quoted 10 lines · Konstantin
Quoted 10 lines
Quoted 10 lines
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more
writes are expected.
Quoted 29 lines
Quoted 11 lines
Quoted 6 lines
Recently ARM guys introduced new generic API for similar (as I understand) purposes:
rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
KonstantinHi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value.
I understand that.
Quoted 4 lines
The "expected" value is there
as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.AFAIK, current rte_wait_until_equal_* does pretty much the same thing:
LDXR memaddr, $reg // an address to monitor for if ($reg !=
expected_value)
SEVL // arm monitor
do {
WFE // waits for write to that memory address
LDXR memaddr, $reg
} while ($reg !=expected_value);Quoted 6 lines
Looks pretty similar to what
rte_power_monitor()does, except you don't have a loop for checking the new value.
Plusrte_power_monitor()provides extra options to the user - timestamp and power save mode to enter.
Also I don't know what is the granularity of such events on ARM, is it a cache-line or more/less.
Quoted 3 lines
As I understand it, Granularity is per the cache-line.
ie. Load-exclusive(LDXR) followed by WFE will wait in a low-power state until the cache line is written.
Architecture allows for 16B to 2048B space. Typically, implementations use cache-line granularity.
Quoted 9 lines
But I see UMONITOR bit different, Where _without_ other core signaling to wakeup from wait state, it can wake on TSC expiry. I think, that's is the main primitive on this feature. Right?
WFE can also wake based on Timer stream events(kind of TSC in x86 analogy) but it has a configuration
bit that needs to allow for this scheme in userspace(EL0) or not? defined by EL1(Linux kernel).
Timer stream events are not per CPU core. They are system wide streams.
Quoted 10 lines
I am planning to spend time on this after understanding the value addition of the feature/usecase[1] [1] http://mails.dpdk.org/archives/dev/2020-
May/168888.htmlQuoted 2 lines
Might be ARM people can comment/correct me here.
Konstantin
Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics
Quoted 69 lines · Honnappa Nagarahalli
<snip>
Quoted 56 lines
Quoted 10 lines · Konstantin
Quoted 10 lines
Quoted 10 lines
Hi Anatoly,
Quoted 6 lines
Add two new power management intrinsics, and provide an implementation in eal/x86 based on UMONITOR/UMWAIT instructions. The instructions are implemented as raw byte opcodes because there is not yet widespread compiler support for these instructions.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
The power management instructions provide an architecture-specific function to either wait until a specified TSC timestamp is reached, or optionally wait until either a TSC timestamp is reached or a memory location is written to. The monitor function also provides an optional comparison, to avoid sleeping when the expected write has already happened, and no more
writes are expected.
Quoted 29 lines
Quoted 11 lines
Quoted 6 lines
Recently ARM guys introduced new generic API for similar (as I understand) purposes:
rte_wait_until_equal_(16|32|64). Probably would make sense to unite both APIs into something common and HW transparent.
KonstantinHi Konstantin,
That's not really similar purpose. This is monitoring a cacheline for writes, not waiting on a specific value.
I understand that.
Quoted 4 lines
The "expected" value is there
as basically a hack to get around the race condition due to the fact that by the time you enter monitoring state, the write you're waiting for may have already happened.AFAIK, current rte_wait_until_equal_* does pretty much the same thing:
LDXR memaddr, $reg // an address to monitor for if ($reg !=
expected_value)
SEVL // arm monitor
do {
WFE // waits for write to that memory address
LDXR memaddr, $reg
} while ($reg !=expected_value);Quoted 6 lines
Looks pretty similar to what
rte_power_monitor()does, except you don't have a loop for checking the new value.
Plusrte_power_monitor()provides extra options to the user - timestamp and power save mode to enter.
Also I don't know what is the granularity of such events on ARM, is it a cache-line or more/less.Quoted 3 lines
As I understand it, Granularity is per the cache-line.
ie. Load-exclusive(LDXR) followed by WFE will wait in a low-power state until the cache line is written.Architecture allows for 16B to 2048B space. Typically, implementations use cache-line granularity.
Quoted 5 lines
But I see UMONITOR bit different, Where _without_ other core signaling to wakeup from wait state, it can wake on TSC expiry. I think, that's is the main primitive on this feature. Right?
Quoted 5 lines
Quoted 4 lines
WFE can also wake based on Timer stream events(kind of TSC in x86 analogy) but it has a configuration
bit that needs to allow for this scheme in userspace(EL0) or not? defined by EL1(Linux kernel).Timer stream events are not per CPU core. They are system wide streams.
We may not need per core support to implement this use case.
I think, currently, kernel configured to have a WFE signal on every 100us.(System-wide).
do while{} loop can check if it is passing the requested timestamp after WFE. But minimum granularity will be 100us.(i.e 100us worth of ticks)
Quoted 11 lines
Quoted 11 lines
I am planning to spend time on this after understanding the value addition of the feature/usecase[1] [1] http://mails.dpdk.org/archives/dev/2020-
May/168888.htmlQuoted 2 lines
Might be ARM people can comment/correct me here.
Konstantin
v11 cover Add PMD power mgmt
In reply to Anatoly BurakovThis patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive, along with a set of generic intrinsics that facilitate that. This is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on it.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism
used: we're counting empty polls, and if a certain threshold is reached, we get the address of next RX ring descriptor from the NIC driver, arm the monitoring hardware, and enter a power-optimized state. We will then wake up when either a timeout happens, or a write happens (or generally whenever CPU feels like waking up - this is platform- specific), and proceed as normal. The empty poll counter is reset whenever we actually get packets, so we only go to sleep when we know nothing is going on. The mechanism is generic which can be used for any write back descriptor.
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it wit minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. UMWAIT/PAUSE/Frequency_Scale
- Power management is enabled per-queue
- The API doesn't extend to other device types
Liang Ma (6):
ethdev: add simple power management API
power: add PMD power management API and callback net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API examples/l3fwd-power: enable PMD power mgmt
doc/guides/prog_guide/power_man.rst | 51 +++ doc/guides/rel_notes/release_20_11.rst | 17 + .../sample_app_ug/l3_forward_power_man.rst | 14 + drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 26 ++ drivers/net/i40e/i40e_rxtx.h | 2 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++ drivers/net/ice/ice_rxtx.h | 2 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 ++ drivers/net/ixgbe/ixgbe_rxtx.h | 2 + examples/l3fwd-power/main.c | 46 ++- lib/librte_ethdev/rte_ethdev.c | 23 ++ lib/librte_ethdev/rte_ethdev.h | 41 +++ lib/librte_ethdev/rte_ethdev_driver.h | 28 ++ lib/librte_ethdev/version.map | 1 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 320 ++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 92 +++++ lib/librte_power/version.map | 4 + 21 files changed, 725 insertions(+), 3 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
Signature · 1 line
2.17.1
PATCH v11 1/6 ethdev: add simple power management API
Add a simple API to allow getting address of getting notification information from the PMD, as well as release notes information.
---
Notes:
v11:
- Rework the API Doxygen documentation
v10:
- Address minor issue on comments and release notes
v8:
- Rename version map file name
v7:
- Fixed race condition (Konstantin)
- Slight rework of the structure of monitor code
- Added missing inline for wakeup
v6:
- Added wakeup mechanism for UMWAIT
- Removed memory allocation (everything is now allocated statically)
- Fixed various typos and comments
- Check for invalid queue ID
- Moved release notes to this patch
v5:
- Make error checking more robust
- Prevent initializing scaling if ACPI or PSTATE env wasn't set
- Prevent initializing UMWAIT path if PMD doesn't support get_wake_addr
- Add some debug logging
- Replace x86-specific code path to generic path using the intrinsic check
--- doc/guides/rel_notes/release_20_11.rst | 4 +++ lib/librte_ethdev/rte_ethdev.c | 23 +++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 41 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 28 ++++++++++++++++++ lib/librte_ethdev/version.map | 1 + 5 files changed, 97 insertions(+)
doc/guides/rel_notes/release_20_11.rst +4 -0
@@ -148,6 +148,10 @@ New Features Hairpin Tx part flow rules can be inserted explicitly. New API is added to get the hairpin peer ports list. +* **ethdev: added 1 new API for PMD power management.** + + * ``rte_eth_get_wake_addr()`` is added to get the wake up address from device. + * **Updated Broadcom bnxt driver.** Updated the Broadcom bnxt driver with new features and improvements, including:
lib/librte_ethdev/rte_ethdev.c +23 -0
@@ -5138,6 +5138,29 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_wake_addr (uint16_t port_id,uint16_t queue_id, +volatile void **wake_addr,uint64_t *expected,uint64_t *mask, +uint8_t *data_sz) +{ +struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_wake_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_rx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid RX queue_id=%u\n" , queue_id); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_wake_addr (dev->data->rx_queues[queue_id], + wake_addr, expected, mask, data_sz)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,
lib/librte_ethdev/rte_ethdev.h +41 -0
47 lines not on this page · raw message
lib/librte_ethdev/rte_ethdev_driver.h +28 -0
@@ -752,6 +752,32 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received. +* +* @param rxq +* Ethdev queue pointer. +* @param tail_desc_addr +* The pointer point to where the address will be stored. +* @param expected +* The pointer point to value to be expected when descriptor is set. +* @param mask +* The pointer point to comparison bitmask for the expected value. +* @param data_sz +* Data size for the expected value (can be 1, 2, 4, or 8 bytes) +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_wake_addr_t )(void *rxq,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
@@ -910,6 +936,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_wake_addr_t get_wake_addr; +/**< Get next RX queue ring entry address. */ };/**
lib/librte_ethdev/version.map +1 -0
@@ -235,6 +235,7 @@ EXPERIMENTAL { rte_eth_fec_get_capability; rte_eth_fec_get; rte_eth_fec_set; + rte_eth_get_wake_addr; rte_flow_shared_action_create; rte_flow_shared_action_destroy; rte_flow_shared_action_query;
Signature · 1 line
2.17.1
Re: [dpdk-dev] [PATCH v11 1/6] ethdev: add simple power management API
Quoted 37 lines · Liang Ma
Add a simple API to allow getting address of getting notification information from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v11:
- Rework the API Doxygen documentationv10:
- Address minor issue on comments and release notesv8:
- Rename version map file namev7:
- Fixed race condition (Konstantin)
- Slight rework of the structure of monitor code
- Added missing inline for wakeupv6:
- Added wakeup mechanism for UMWAIT
- Removed memory allocation (everything is now allocated statically)
- Fixed various typos and comments
- Check for invalid queue ID
- Moved release notes to this patchv5:
- Make error checking more robust
- Prevent initializing scaling if ACPI or PSTATE env wasn't set
- Prevent initializing UMWAIT path if PMD doesn't supportget_wake_addr
- Add some debug logging
- Replace x86-specific code path to generic path using the intrinsic check
---
<snip>
Quoted 13 lines
int
rte_eth_dev_set_mc_addr_list(uint16_tport_id,
structrte_ether_addr*mc_addr_set,lib/librte_ethdev/rte_ethdev.h +2
-0@@ -4364,6 +4364,47 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* In order to make use of some PMD Power Management schemes, the user might
lib/librte_ethdev/rte_ethdev.h __rte_experimental · line 4378
Quoted 10 lines
lib/librte_ethdev/rte_ethdev.h +10
-0+* want to wait (sleep/poll) until new packets arrive. This function +* retrieves the necessary information from the PMD to enter that wait/sleep +* state. The main parameter provided is the address to monitor while waiting +* to wake up. In addition to this wake address, the function also provides +* extra information including expected value, selection mask and data size to +* monitor. The user is expected to use this information to enter low power +* mode using the rte_power_monitor API, and the core will exit low power mode +* upon reaching the expected condition: +* (((uint64_t)read_mem(wake_addr, data_sz)) & mask) == expected). +* @note The low power mode can also exit in other cases, e.g. interrupt.
Could we maybe have some paragraphs and spacing here, instead of one solid block of text? Suggested formatting:
* In order to make use of some PMD Power Management schemes, the user
might
* want to wait (sleep/poll) until new packets arrive. This function
retrieves
* the necessary information from the PMD to enter that wait/sleep state. *
* The main parameter provided is the address to monitor while waiting
to wake
* up. In addition to this wake address, the function also provides extra
* information including expected value, selection mask and data size to
* monitor.
*
* The user is expected to use this information to enter low power mode
using
* the rte_power_monitor API, and the core will exit low power mode upon
* reaching the expected condition:
*
* (((uint64_t)read_mem(wake_addr, data_sz)) & mask) == expected)
*
* @note The low power mode can also exit in other cases, e.g. interrupt.
Quoted 4 lines
lib/librte_ethdev/rte_ethdev.h +4
-0+* +* @param[in] port_id +* The port identifier of the Ethernet device. +* @param[in] queue_id
lib/librte_ethdev/rte_ethdev.h __rte_experimental · line 4392
Quoted 10 lines
lib/librte_ethdev/rte_ethdev.h +10
-0+* The Rx queue on the Ethernet device for which information will be +* retrieved. +* @param[out] wake_addr +* The pointer to the address which will be monitored. +* @param[out] expected +* The pointer to the expected value to allow wakeup condition. +* @param[out] mask +* The pointer to comparison bitmask for the expected value. +* @note a mask value of zero should be treated as: +* “no special wakeup values for provided address from the driver”.
Wrong quotes.
lib/librte_ethdev/rte_ethdev.h __rte_experimental · line 4395
Quoted 3 lines
lib/librte_ethdev/rte_ethdev.h +3
-0+* @param[out] data_sz +* The pointer to data size for the expected value (in bytes) +* @note valid values are 1,2,4,8
Shouldn't @note be under @param, i.e. indented to the right at the same level the text is? Also, everywhere else in this file, the indentation of the text is two spaces, not one. So, should be e.g.
* @param[out] data_sz
* The pointer to data......
* @note valid values are.....
Quoted 74 lines
lib/librte_ethdev/rte_ethdev.h +12
-0+* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_wake_addr(uint16_t port_id, uint16_t queue_id, +volatile void **wake_addr,uint64_t *expected,uint64_t *mask, +uint8_t *data_sz); +/** * Retrieve device registers and register attributes (number of registers and * register size) lib/librte_ethdev/rte_ethdev_driver.h +28
-0@@ -752,6 +752,32 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received. +* +* @param rxq +* Ethdev queue pointer. +* @param tail_desc_addr +* The pointer point to where the address will be stored. +* @param expected +* The pointer point to value to be expected when descriptor is set. +* @param mask +* The pointer point to comparison bitmask for the expected value. +* @param data_sz +* Data size for the expected value (can be 1, 2, 4, or 8 bytes) +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_wake_addr_t )(void *rxq,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz); +/** * @internal A structure containing the functions exported by an Ethernet driver. */ @@ -910,6 +936,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_wake_addr_t get_wake_addr; +/**< Get next RX queue ring entry address. */ };/** lib/librte_ethdev/version.map +1
-0@@ -235,6 +235,7 @@ EXPERIMENTAL { rte_eth_fec_get_capability; rte_eth_fec_get; rte_eth_fec_set; + rte_eth_get_wake_addr; rte_flow_shared_action_create; rte_flow_shared_action_destroy; rte_flow_shared_action_query;
Signature · 2 lines
Thanks, Anatoly
PATCH v11 2/6 power: add PMD power management API and callback
In reply to Liang MaAdd a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
The following are the available schemes:
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. Pause instruction
Instead of move the core into deeper C state, this method uses the pause instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.
---
Notes:
v11:
- Updated power library document
- Updated release notes
v10:
- Updated power library document
v8:
- Rename version map file name
v7:
- Fixed race condition (Konstantin)
- Slight rework of the structure of monitor code
- Added missing inline for wakeup
v6:
- Added wakeup mechanism for UMWAIT
- Removed memory allocation (everything is now allocated statically)
- Fixed various typos and comments
- Check for invalid queue ID
- Moved release notes to this patch
v5:
- Make error checking more robust
- Prevent initializing scaling if ACPI or PSTATE env wasn't set
- Prevent initializing UMWAIT path if PMD doesn't support get_wake_addr
- Add some debug logging
- Replace x86-specific code path to generic path using the intrinsic check
--- doc/guides/prog_guide/power_man.rst | 51 ++++ doc/guides/rel_notes/release_20_11.rst | 15 +- lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 320 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 92 +++++++ lib/librte_power/version.map | 4 + 6 files changed, 484 insertions(+), 3 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
doc/guides/prog_guide/power_man.rst +51 -0
@@ -192,6 +192,54 @@ User Cases ---------- The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA. +PMD Power Management API +------------------------ + +Abstract +~~~~~~~~ + +Existing Power Management mechanisms require developers to change the design of +an application or change code to make use of it. The PMD Power Management API +provides a convenient alternative by use Ethernet PMD RX callbacks, and +triggering power saving whenever the empty poll count reaches a certain number. + +There are multiple power saving schemes available for the developer to choose. +Although the developer can configure each queue with different scheme, It's +strongly recommended to configure the queue within the same port with the same +scheme. + +The following are the available schemes: + + * UMWAIT/UMONITOR + + This power saving scheme will put the core into an optimized power state and + use the UMWAIT/UMONITOR instructions to monitor the Ethernet PMD RX + descriptor address, and wake the CPU up whenever there's new traffic. + + * Pause + + This power saving scheme will use the "rte_pause" function to reduce the impact + of busy polling. + + * Frequency scaling + + This power saving scheme will use existing power library functionality to + scale the core frequency up/down depending on traffic volume. + + +.. note:: + + Currently, this Power Management API is limited to mapping of 1 queue to 1 + core (multiple queues are supported, but they must be polled from different + cores). + +API Overview for PMD Power Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* **Queue Enable**: Enable a specific power scheme for a certain queue/port/core + +* **Queue Disable**: Disable power scheme for certain queue/port/core + References ----------
@@ -200,3 +248,6 @@ References * The :doc:`../sample_app_ug/vm_power_management` chapter in the :doc:`../sample_app_ug/index` section. + +* The :doc:`../sample_app_ug/rxtx_callbacks` + chapter in the :doc:`../sample_app_ug/index` section.
doc/guides/rel_notes/release_20_11.rst +14 -1
@@ -150,7 +150,9 @@ New Features * **ethdev: added 1 new API for PMD power management.** - * ``rte_eth_get_wake_addr()`` is added to get the wake up address from device. + * ``rte_eth_get_wake_addr()`` function has been added to allow applications to + fetch the wake up information from the device. Processor need that information + to wake up from the low power state. * **Updated Broadcom bnxt driver.**
@@ -362,6 +364,17 @@ New Features * Replaced ``--scalar`` command-line option with ``--alg=<value>``, to allow the user to select the desired classify method. +* **Added PMD power management mechanism** + + The new Ethernet PMD Power Management mechanisms have been added through the + existing RX callback infrastructure. + + * Added power saving scheme based on UMWAIT instruction (x86 only) + * Added power saving scheme based on ``rte_pause()`` + * Added power saving scheme based on frequency scaling through the power library + * Added new EXPERIMENTAL API ``rte_power_pmd_mgmt_queue_enable()`` + * Added new EXPERIMENTAL API ``rte_power_pmd_mgmt_queue_disable()`` + Removed Items -------------
lib/librte_power/meson.build +3 -2
@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c', 'power_kvm_vm.c', 'guest_channel.c', 'rte_power_empty_poll.c', 'power_pstate_cpufreq.c', + 'rte_power_pmd_mgmt.c', 'power_common.c') -headers = files('rte_power.h','rte_power_empty_poll.h') -deps += ['timer'] +headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h') +deps += ['timer' ,'ethdev']
lib/librte_power/rte_power_pmd_mgmt.c new file +320 -0
320 lines not on this page · raw message
lib/librte_power/rte_power_pmd_mgmt.h new file +92 -0
92 lines not on this page · raw message
lib/librte_power/version.map +4 -0
@@ -34,4 +34,8 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + # added in 20.11 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + };
Signature · 1 line
2.17.1
PATCH v11 3/6 net/ixgbe: implement power management API
In reply to Liang MaImplement support for the power management API by implementing a get_wake_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_rxtx.h | 2 ++ 3 files changed, 28 insertions(+)
drivers/net/ixgbe/ixgbe_ethdev.c +1 -0
@@ -588,6 +588,7 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .udp_tunnel_port_del = ixgbe_dev_udp_tunnel_port_del, .tm_ops_get = ixgbe_tm_ops_get, .tx_done_cleanup = ixgbe_dev_tx_done_cleanup, + .get_wake_addr = ixgbe_get_wake_addr, };/*
drivers/net/ixgbe/ixgbe_rxtx.c +25 -0
@@ -1369,6 +1369,31 @@ const uint32_t RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP, }; +int ixgbe_get_wake_addr (void *rx_queue,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz) +{ +volatile union ixgbe_adv_rx_desc *rxdp; +struct ixgbe_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + *tail_desc_addr = &rxdp->wb.upper.status_error; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + *expected =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + *mask =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + +/* the registers are 32-bit */ + *data_sz =4 ; + +return 0 ; +} +/* @note: fix ixgbe_dev_supported_ptypes_get() if any change here. */ static inline uint32_t ixgbe_rxd_pkt_info_to_pkt_type (uint32_t pkt_info,uint16_t ptype_mask)
drivers/net/ixgbe/ixgbe_rxtx.h +2 -0
@@ -299,5 +299,7 @@ uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_queue_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_port_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_tx_queue_offloads (struct rte_eth_dev *dev); +int ixgbe_get_wake_addr (void *rx_queue,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz);#endif /* _IXGBE_RXTX_H_ */
Signature · 1 line
2.17.1
PATCH v11 4/6 net/i40e: implement power management API
In reply to Liang MaImplement support for the power management API by implementing a get_wake_addr function that will return an address of an RX ring's status bit.
--- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/i40e/i40e_rxtx.h | 2 ++ 3 files changed, 29 insertions(+)
drivers/net/i40e/i40e_ethdev.c +1 -0
@@ -513,6 +513,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = { .mtu_set = i40e_dev_mtu_set, .tm_ops_get = i40e_tm_ops_get, .tx_done_cleanup = i40e_tx_done_cleanup, + .get_wake_addr = i40e_get_wake_addr, };/* store statistics names and its offset in stats structure */
drivers/net/i40e/i40e_rxtx.c +26 -0
@@ -72,6 +72,32 @@#define I40E_TX_OFFLOAD_NOTSUP_MASK \ (PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_MASK) +int +i40e_get_wake_addr (void *rx_queue,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz) +{ +struct i40e_rx_queue *rxq = rx_queue; +volatile union i40e_rx_desc *rxdp; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + *tail_desc_addr = &rxdp->wb.qword1.status_error_len; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + *expected =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + *mask =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + +/* registers are 64-bit */ + *data_sz =8 ; + +return 0 ; +} +static inline void i40e_rxd_to_vlan_tci (struct rte_mbuf *mb,volatile union i40e_rx_desc *rxdp) {
drivers/net/i40e/i40e_rxtx.h +2 -0
@@ -248,6 +248,8 @@ uint16_t i40e_recv_scattered_pkts_vec_avx2(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);uint16_t i40e_xmit_pkts_vec_avx2 (void *tx_queue,struct rte_mbuf **tx_pkts,uint16_t nb_pkts); +int i40e_get_wake_addr (void *rx_queue,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *value,uint8_t *data_sz);/* For each value it means, datasheet of hardware can tell more details *
Signature · 1 line
2.17.1
PATCH v11 5/6 net/ice: implement power management API
In reply to Liang MaImplement support for the power management API by implementing a get_wake_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/ice/ice_rxtx.h | 2 ++ 3 files changed, 29 insertions(+)
drivers/net/ice/ice_ethdev.c +1 -0
@@ -216,6 +216,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .udp_tunnel_port_add = ice_dev_udp_tunnel_port_add, .udp_tunnel_port_del = ice_dev_udp_tunnel_port_del, .tx_done_cleanup = ice_tx_done_cleanup, + .get_wake_addr = ice_get_wake_addr, };/* store statistics names and its offset in stats structure */
drivers/net/ice/ice_rxtx.c +26 -0
@@ -26,6 +26,32 @@ uint64_t rte_net_ice_dynflag_proto_xtr_ipv6_flow_mask;uint64_t rte_net_ice_dynflag_proto_xtr_tcp_mask;uint64_t rte_net_ice_dynflag_proto_xtr_ip_offset_mask; +int ice_get_wake_addr (void *rx_queue,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz) +{ +volatile union ice_rx_flex_desc *rxdp; +struct ice_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + *tail_desc_addr = &rxdp->wb.status_error0; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + *expected =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + *mask =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + +/* register is 16-bit */ + *data_sz =2 ; + +return 0 ; +} + +static inline uint8_t ice_proto_xtr_type_to_rxdid (uint8_t xtr_type) {
drivers/net/ice/ice_rxtx.h +2 -0
@@ -250,6 +250,8 @@ uint16_t ice_xmit_pkts_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);int ice_fdir_programming (struct ice_pf *pf,struct ice_fltr_desc *fdir_desc);int ice_tx_done_cleanup (void *txq,uint32_t free_cnt); +int ice_get_wake_addr (void *rx_queue,volatile void **tail_desc_addr, +uint64_t *expected,uint64_t *mask,uint8_t *data_sz);#define FDIR_PARSING_ENABLE_PER_QUEUE (ad, on) do { \ int i; \
Signature · 1 line
2.17.1
PATCH v11 6/6 examples/l3fwd-power: enable PMD power mgmt
In reply to Liang MaAdd PMD power management feature support to l3fwd-power sample app.
---
Notes:
v11:
- Update l3fwd-power documentation
v8:
- Add return status check for queue enable
v6:
- Fixed typos in documentation
--- .../sample_app_ug/l3_forward_power_man.rst | 14 ++++++ examples/l3fwd-power/main.c | 46 ++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-)
doc/guides/sample_app_ug/l3_forward_power_man.rst +14 -0
@@ -109,6 +109,8 @@ where, * --telemetry: Telemetry mode. +* --pmd-mgmt: PMD power management mode. + See :doc:`l3_forward` for details. The L3fwd-power example reuses the L3fwd command line options.
@@ -455,3 +457,15 @@ reference cycles and accordingly busy rate is set to either 0% or The new stats ``empty_poll`` , ``full_poll`` and ``busy_percent`` can be viewed by running the script ``/usertools/dpdk-telemetry-client.py`` and selecting the menu option ``Send for global Metrics``. + +PMD power management Mode +------------------------- + +The PMD Power Management mode support for ``l3fwd-power`` is a standalone mode, in this mode +``l3fwd-power`` does simple l3fwding along with enabling the power saving scheme on a specific +port/queue/lcore. The main purpose for this mode is to demonstrate how to use the PMD power +management API. + +.. code-block:: console + + ./build/examples/dpdk-l3fwd-power -l 1-3 -- --pmd-mgmt -p 0x0f --config="(0,0,2),(0,1,3)"
examples/l3fwd-power/main.c +45 -1
100 lines not on this page · raw message
Signature · 1 line
2.17.1
v12 cover Add PMD power management
In reply to Liang MaThis patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. This is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on it.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we get the address of next RX ring descriptor from the NIC driver, arm the monitoring hardware, and enter a power-optimized state. We will then wake up when either a timeout happens, or a write happens (or generally whenever CPU feels like waking up - this is platform-specific), and proceed as normal. The empty poll counter is reset whenever we actually get packets, so we only go to sleep when we know nothing is going on. The mechanism is generic which can be used for any write back descriptor.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway, not on a hotpath).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it wit minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
Anatoly Burakov (5):
eal: uninline power intrinsics
eal: avoid invalid API usage in power intrinsics
eal: change API of power intrinsics
eal: remove sync version of power monitor
eal: add monitor wakeup function
Liang Ma (6):
ethdev: add simple power management API
power: add PMD power management API and callback net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API examples/l3fwd-power: enable PMD power mgmt
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 14 + .../sample_app_ug/l3_forward_power_man.rst | 35 ++ drivers/event/dlb/dlb.c | 10 +- drivers/event/dlb2/dlb2.c | 10 +- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 ++ drivers/net/i40e/i40e_rxtx.h | 1 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++ drivers/net/ice/ice_rxtx.h | 1 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 ++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + examples/l3fwd-power/main.c | 89 ++++- .../arm/include/rte_power_intrinsics.h | 39 +- .../include/generic/rte_power_intrinsics.h | 78 ++-- .../ppc/include/rte_power_intrinsics.h | 39 +- lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ------ lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 169 +++++++++ lib/librte_ethdev/rte_ethdev.c | 28 ++ lib/librte_ethdev/rte_ethdev.h | 25 ++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++ lib/librte_ethdev/version.map | 3 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 349 ++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++ lib/librte_power/version.map | 5 + 30 files changed, 1028 insertions(+), 229 deletions(-) create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
Signature · 1 line
2.17.1
PATCH v12 1/11 eal: uninline power intrinsics
Currently, power intrinsics are inline functions. Make them part of the ABI so that we can have various internal data associated with them without exposing said data to the outside world.
--- .../arm/include/rte_power_intrinsics.h | 6 +- .../include/generic/rte_power_intrinsics.h | 6 +- .../ppc/include/rte_power_intrinsics.h | 6 +- lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ----------------- lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 120 ++++++++++++++++++ 7 files changed, 135 insertions(+), 124 deletions(-) create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c
lib/librte_eal/arm/include/rte_power_intrinsics.h +3 -3
@@ -16,7 +16,7 @@ extern "C" {/** * This function is not supported on ARM. */ -static inline void +void rte_power_monitor (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz)
@@ -31,7 +31,7 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on ARM. */ -static inline void +void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck)
@@ -47,7 +47,7 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on ARM. */ -static inline void +void rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp);
lib/librte_eal/include/generic/rte_power_intrinsics.h +3 -3
@@ -52,7 +52,7 @@* to undefined result. */ __rte_experimental -static inline void rte_power_monitor(const volatile void *p, +void rte_power_monitor(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz);
@@ -97,7 +97,7 @@ static inline void rte_power_monitor(const volatile void *p,* wakes up. */ __rte_experimental -static inline void rte_power_monitor_sync(const volatile void *p, +void rte_power_monitor_sync(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck);
@@ -118,6 +118,6 @@ static inline void rte_power_monitor_sync(const volatile void *p,* architecture-dependent. */ __rte_experimental -static inline void rte_power_pause(const uint64_t tsc_timestamp); +void rte_power_pause(const uint64_t tsc_timestamp);#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/ppc/include/rte_power_intrinsics.h +3 -3
@@ -16,7 +16,7 @@ extern "C" {/** * This function is not supported on PPC64. */ -static inline void +void rte_power_monitor (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz)
@@ -31,7 +31,7 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on PPC64. */ -static inline void +void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck)
@@ -47,7 +47,7 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on PPC64. */ -static inline void +void rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp);
lib/librte_eal/version.map +5 -0
@@ -403,6 +403,11 @@ EXPERIMENTAL { rte_service_lcore_may_be_active; rte_vect_get_max_simd_bitwidth; rte_vect_set_max_simd_bitwidth; + + # added in 21.02 + rte_power_monitor; + rte_power_monitor_sync; + rte_power_pause; }; INTERNAL {
lib/librte_eal/x86/include/rte_power_intrinsics.h +0 -115
121 lines not on this page · raw message
lib/librte_eal/x86/meson.build +1 -0
@@ -8,4 +8,5 @@ sources += files( 'rte_cycles.c', 'rte_hypervisor.c', 'rte_spinlock.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/x86/rte_power_intrinsics.c new file +120 -0
120 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v12 2/11 eal: avoid invalid API usage in power intrinsics
In reply to Anatoly BurakovCurrently, the API documentation mandates that if the user wants to use the power management intrinsics, they need to call the rte_cpu_get_intrinsics_support API and check support for specific intrinsics.
However, if the user does not do that, it is possible to get illegal instruction error because we're using raw instruction opcodes, which may or may not be supported at runtime.
Now that we have everything in a C file, we can check for support at startup and prevent the user from possibly encountering illegal instruction errors.
--- .../include/generic/rte_power_intrinsics.h | 3 -- lib/librte_eal/x86/rte_power_intrinsics.c | 31 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-)
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -3
@@ -34,7 +34,6 @@* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -75,7 +74,6 @@ void rte_power_monitor(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -111,7 +109,6 @@ void rte_power_monitor_sync(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param tsc_timestamp * Maximum TSC timestamp to wait for. Note that the wait behavior is
lib/librte_eal/x86/rte_power_intrinsics.c +28 -3
@@ -4,6 +4,8 @@#include "rte_power_intrinsics.h" +static uint8_t wait_supported; +static inline uint64_t __get_umwait_val (const volatile void *p,const uint8_t sz) {
@@ -35,6 +37,11 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -72,6 +79,11 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -112,9 +124,22 @@ rte_power_pause(const uint64_t tsc_timestamp)const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* execute TPAUSE */ asmvolatile (".byte 0x66, 0x0f, 0xae, 0xf7;" - :/* ignore rflags */ - :"D" (0 ),/* enter C0.2 */ -"a" (tsc_l),"d" (tsc_h)); + :/* ignore rflags */ + :"D" (0 ),/* enter C0.2 */ +"a" (tsc_l),"d" (tsc_h)); +} + +RTE_INIT (rte_power_intrinsics_init) { +struct rte_cpu_intrinsics i; + +rte_cpu_get_intrinsics_support (&i); + +if (i.power_monitor && i.power_pause) + wait_supported =1 ; }
Signature · 1 line
2.17.1
PATCH v12 3/11 eal: change API of power intrinsics
In reply to Anatoly BurakovInstead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
--- drivers/event/dlb/dlb.c | 10 ++-- drivers/event/dlb2/dlb2.c | 10 ++-- .../arm/include/rte_power_intrinsics.h | 20 +++----- .../include/generic/rte_power_intrinsics.h | 49 ++++++++----------- .../ppc/include/rte_power_intrinsics.h | 20 +++----- lib/librte_eal/x86/rte_power_intrinsics.c | 32 ++++++------ 6 files changed, 62 insertions(+), 79 deletions(-)
drivers/event/dlb/dlb.c +7 -3
@@ -3161,6 +3161,7 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,/* Interrupts not supported by PF PMD */ return 1 ; } else if (dlb->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -3181,9 +3182,12 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
drivers/event/dlb2/dlb2.c +7 -3
@@ -2870,6 +2870,7 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,if (elapsed_ticks >= timeout) {return 1 ; }else if (dlb2->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb2_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -2890,9 +2891,12 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB2_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
lib/librte_eal/arm/include/rte_power_intrinsics.h +6 -14
@@ -17,31 +17,23 @@ extern "C" {* This function is not supported on ARM. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on ARM. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/**
lib/librte_eal/include/generic/rte_power_intrinsics.h +20 -29
81 lines not on this page · raw message
lib/librte_eal/ppc/include/rte_power_intrinsics.h +6 -14
@@ -17,31 +17,23 @@ extern "C" {* This function is not supported on PPC64. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on PPC64. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/**
lib/librte_eal/x86/rte_power_intrinsics.c +16 -16
@@ -31,9 +31,8 @@ __get_umwait_val(const volatile void *p, const uint8_t sz)* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -50,14 +49,15 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }/* execute UMWAIT */
@@ -73,9 +73,8 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -92,14 +91,15 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }rte_spinlock_unlock (lck);
Signature · 1 line
2.17.1
PATCH v12 4/11 eal: remove sync version of power monitor
In reply to Anatoly BurakovCurrently, the "sync" version of power monitor intrinsic is supposed to be used for purposes of waking up a sleeping core. However, there are better ways to achieve the same result, so remove the unneeded function.
--- .../arm/include/rte_power_intrinsics.h | 12 ----- .../include/generic/rte_power_intrinsics.h | 34 -------------- .../ppc/include/rte_power_intrinsics.h | 12 ----- lib/librte_eal/version.map | 1 - lib/librte_eal/x86/rte_power_intrinsics.c | 46 ------------------- 5 files changed, 105 deletions(-)
lib/librte_eal/arm/include/rte_power_intrinsics.h +0 -12
@@ -24,18 +24,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on ARM. */
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -34
@@ -57,40 +57,6 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); -/** -* @warning -* @b EXPERIMENTAL: this API may change without prior notice -* -* Monitor specific address for changes. This will cause the CPU to enter an -* architecture-defined optimized power state until either the specified -* memory address is written to, a certain TSC timestamp is reached, or other -* reasons cause the CPU to wake up. -* -* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If -* mask is non-zero, the current value pointed to by the `p` pointer will be -* checked against the expected value, and if they match, the entering of -* optimized power state may be aborted. -* -* This call will also lock a spinlock on entering sleep, and release it on -* waking up the CPU. -* -* @warning It is responsibility of the user to check if this function is -* supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* -* @param pmc -* The monitoring condition structure. -* @param tsc_timestamp -* Maximum TSC timestamp to wait for. Note that the wait behavior is -* architecture-dependent. -* @param lck -* A spinlock that must be locked before entering the function, will be -* unlocked while the CPU is sleeping, and will be locked again once the CPU -* wakes up. -*/ -__rte_experimental -void rte_power_monitor_sync(const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck); -/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/include/rte_power_intrinsics.h +0 -12
@@ -24,18 +24,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on PPC64. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on PPC64. */
lib/librte_eal/version.map +0 -1
@@ -406,7 +406,6 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; - rte_power_monitor_sync; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +0 -46
52 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v12 5/11 eal: add monitor wakeup function
In reply to Anatoly BurakovNow that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from rte_power_pause won't work.
--- .../arm/include/rte_power_intrinsics.h | 9 +++ .../include/generic/rte_power_intrinsics.h | 16 +++++ .../ppc/include/rte_power_intrinsics.h | 9 +++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 70 +++++++++++++++++++ 5 files changed, 105 insertions(+)
lib/librte_eal/arm/include/rte_power_intrinsics.h +9 -0
@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif
lib/librte_eal/include/generic/rte_power_intrinsics.h +16 -0
@@ -57,6 +57,22 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/include/rte_power_intrinsics.h +9 -0
@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif
lib/librte_eal/version.map +1 -0
@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; + rte_power_monitor_wakeup; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +70 -0
98 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v12 6/11 ethdev: add simple power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
---
Notes:
v6:
- Rebase on top of latest main
- Ensure the API checks queue ID (Konstantin)
- Removed accidental inclusion of unrelated release notes
v5:
- Bring function format in line with other functions in the file
- Ensure the API is supported by the driver before calling it (Konstantin)
doc/guides/rel_notes/release_21_02.rst | 4 ++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 82 insertions(+)
doc/guides/rel_notes/release_21_02.rst +4 -0
@@ -55,6 +55,10 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added 1 new API for PMD power management** + + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's. Removed Items -------------
lib/librte_ethdev/rte_ethdev.c +28 -0
@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{ +struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_tx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid TX queue_id=%u\n" , queue_id); +return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,
lib/librte_ethdev/rte_ethdev.h +25 -0
@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h"
@@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size)
lib/librte_ethdev/rte_ethdev_driver.h +22 -0
@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an RX queue. +* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get next RX queue ring entry address. */ };/**
lib/librte_ethdev/version.map +3 -0
@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Signature · 1 line
2.17.1
Re: [dpdk-dev] [PATCH v12 06/11] ethdev: add simple power management API
Quoted 32 lines · Anatoly Burakov
From: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v6:
- Rebase on top of latest main
- Ensure the API checks queue ID (Konstantin)
- Removed accidental inclusion of unrelated release notes
v5:
- Bring function format in line with other functions in the file
- Ensure the API is supported by the driver before calling it (Konstantin)doc/guides/rel_notes/release_21_02.rst | 4 ++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 82 insertions(+)doc/guides/rel_notes/release_21_02.rst +0
-0
doc/guides/rel_notes/release_21_02.rst New Features · line 58
Quoted 4 lines
doc/guides/rel_notes/release_21_02.rst +1
-0@@ -55,6 +55,10 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added 1 new API for PMD power management**
"1 new API" sounds a bit confusing. May be just "a new API"?
doc/guides/rel_notes/release_21_02.rst New Features · line 61
Quoted 3 lines
doc/guides/rel_notes/release_21_02.rst +3
-0+ + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's.
Missing extra empty line here.
Quoted 15 lines
doc/guides/rel_notes/release_21_02.rst +0
-0Removed Items -------------lib/librte_ethdev/rte_ethdev.c +4
-0@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{
lib/librte_ethdev/rte_ethdev.c rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, · line 5131
Quoted 10 lines
lib/librte_ethdev/rte_ethdev.c +10
-0+struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_tx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid TX queue_id=%u\n" , queue_id);
Why is Tx queue checked and logged here, but Rx queue is used below? I guess Rx should be used here as well.
I.e. TX -> Rx
Quoted 65 lines
lib/librte_ethdev/rte_ethdev.c +14
-0+return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,lib/librte_ethdev/rte_ethdev.h +25
-0@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h" @@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size) lib/librte_ethdev/rte_ethdev_driver.h +0
-0
lib/librte_ethdev/rte_ethdev_driver.h typedef int (*eth_hairpin_queue_peer_unbind_t) · line 769
Quoted 7 lines
lib/librte_ethdev/rte_ethdev_driver.h +4
-0@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an RX queue.
RX -> Rx
Quoted 20 lines
lib/librte_ethdev/rte_ethdev_driver.h +16
-0+* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
lib/librte_ethdev/rte_ethdev_driver.h struct eth_dev_ops { · line 941
Quoted 5 lines
lib/librte_ethdev/rte_ethdev_driver.h +2
-0@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get next RX queue ring entry address. */
RX -> Rx
Quoted 18 lines
lib/librte_ethdev/rte_ethdev_driver.h +0
-0};/** lib/librte_ethdev/version.map +3
-0@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Re: [dpdk-dev] [PATCH v12 06/11] ethdev: add simple power management API
Quoted 11 lines · Andrew Rybchenko
Quoted 10 lines · Anatoly Burakov
From: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
Hi Andrew,
Thanks for your review!
Quoted 8 lines
doc/guides/rel_notes/release_21_02.rst New Features · line 58
Quoted 5 lines
doc/guides/rel_notes/release_21_02.rst +1
-0@@ -55,6 +55,10 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added 1 new API for PMD power management**"1 new API" sounds a bit confusing. May be just "a new API"?
Quoted 6 lines
doc/guides/rel_notes/release_21_02.rst New Features · line 61
Quoted 3 lines
doc/guides/rel_notes/release_21_02.rst +3
-0+ + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's.Missing extra empty line here.
Will fix.
Quoted 29 lines
Quoted 15 lines
doc/guides/rel_notes/release_21_02.rst +0
-0Removed Items -------------lib/librte_ethdev/rte_ethdev.c +4
-0@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{lib/librte_ethdev/rte_ethdev.c rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, · line 5131
Quoted 10 lines
lib/librte_ethdev/rte_ethdev.c +10
-0+struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_tx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid TX queue_id=%u\n" , queue_id);Why is Tx queue checked and logged here, but Rx queue is used below? I guess Rx should be used here as well.
Quoted 2 lines
I.e. TX -> Rx
Yep, fixed already.
Quoted 68 lines
Quoted 68 lines
lib/librte_ethdev/rte_ethdev.c +14
-0+return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,lib/librte_ethdev/rte_ethdev.h +25
-0@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h" @@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size) lib/librte_ethdev/rte_ethdev_driver.h +0
-0@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */
Quoted 7 lines
lib/librte_ethdev/rte_ethdev_driver.h typedef int (*eth_hairpin_queue_peer_unbind_t) · line 769
Quoted 4 lines
lib/librte_ethdev/rte_ethdev_driver.h +4
-0+/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an RX queue. RX -> Rx
Will fix.
Quoted 19 lines
Quoted 19 lines
lib/librte_ethdev/rte_ethdev_driver.h +16
-0+* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
Quoted 9 lines
lib/librte_ethdev/rte_ethdev_driver.h struct eth_dev_ops { · line 941
Quoted 6 lines
lib/librte_ethdev/rte_ethdev_driver.h +2
-0@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get next RX queue ring entry address. */ RX -> Rx
Will fix.
Quoted 19 lines
Quoted 18 lines
lib/librte_ethdev/rte_ethdev_driver.h +0
-0};/** lib/librte_ethdev/version.map +3
-0@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v12 06/11] ethdev: add simple power management API
In reply to Anatoly BurakovQuoted 11 lines · Anatoly Burakov
From: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
<snip>
lib/librte_ethdev/rte_ethdev_driver.h struct eth_dev_ops { · line 943
Quoted 11 lines
/**
* @internal A structure containing the functions exported by an Ethernet driver. */lib/librte_ethdev/rte_ethdev_driver.h +2
-0@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get next RX queue ring entry address. */ };
The implementation of get_monitor_addr will have much in common with the rx_descriptor_status API in struct rte_eth_dev, including the property that it will likely not make sense for it to be called concurrently with rx_pkt_burst on a given queue. Might it make more sense to have this API in struct rte_eth_dev instead of struct eth_dev_ops?
Re: [dpdk-dev] [PATCH v12 06/11] ethdev: add simple power management API
Quoted 23 lines · Lance Richardson
Quoted 11 lines · Anatoly Burakov
From: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
<snip>
Quoted 9 lines
/**
* @internal A structure containing the functions exported by an Ethernet driver. */lib/librte_ethdev/rte_ethdev_driver.h +2
-0@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get next RX queue ring entry address. */
Quoted 9 lines
Review struct eth_dev_ops { · line 943
Quoted 2 lines
(quoted diff) +0
-0};The implementation of
get_monitor_addrwill have much in common with therx_descriptor_statusAPI in structrte_eth_dev, including the property that it will likely not make sense for it to be called concurrently withrx_pkt_burston a given queue. Might it make more sense to have this API in structrte_eth_devinstead of structeth_dev_ops?
I don't have an opinion on this as this code isn't really my area of expertise. I'm fine with wherever the community thinks this code should be. Any other opinions?
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v12 06/11] ethdev: add simple power management API
Quoted 36 lines · Lance Richardson
Quoted 23 lines
Quoted 11 lines · Anatoly Burakov
From: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
<snip>
Quoted 9 lines
/**
* @internal A structure containing the functions exported by an Ethernet driver. */lib/librte_ethdev/rte_ethdev_driver.h +2
-0@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get next RX queue ring entry address. */ Quoted 9 lines
Review struct eth_dev_ops { · line 943
Quoted 2 lines
(quoted diff) +0
-0};The implementation of
get_monitor_addrwill have much in common with therx_descriptor_statusAPI in structrte_eth_dev, including the property that it will likely not make sense for it to be called concurrently withrx_pkt_burston a given queue. Might it make more sense to have this API in structrte_eth_devinstead of structeth_dev_ops?
Quoted 4 lines
I don't have an opinion on this as this code isn't really my area of expertise. I'm fine with wherever the community thinks this code should be. Any other opinions?
I don't think it is a good idea to push new members into rte_eth_dev. It either means an ABI breakage or wasting of one of our reserved fields. IMO this function is not that performance critical to justify such insersion. In fact, I think we should look in different direction -
remove rx/tx_descriptor_status() functions from rte_eth_dev,
or even better make rte_eth_dev an opaque pointer.
PATCH v12 7/11 power: add PMD power management API and callback
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.
---
Notes:
v5:
- Moved l3fwd-power update to the l3fwd-power-related commit
- Some rewordings and clarifications
doc/guides/prog_guide/power_man.rst | 44 ++++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 349 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 501 insertions(+), 2 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
doc/guides/prog_guide/power_man.rst +44 -0
@@ -192,6 +192,47 @@ User Cases ---------- The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA. +PMD Power Management API +------------------------ + +Abstract +~~~~~~~~ +Existing power management mechanisms require developers to change application +design or change code to make use of it. The PMD power management API provides a +convenient alternative by utilizing Ethernet PMD RX callbacks, and triggering +power saving whenever empty poll count reaches a certain number. + + * Monitor + + This power saving scheme will put the CPU into optimized power state and use + the ``rte_power_monitor()`` function to monitor the Ethernet PMD RX + descriptor address, and wake the CPU up whenever there's new traffic. + + * Pause + + This power saving scheme will avoid busy polling by either entering + power-optimized sleep state with ``rte_power_pause()`` function, or, if it's + not available, use ``rte_pause()``. + + * Frequency scaling + + This power saving scheme will use existing ``librte_power`` library + functionality to scale the core frequency up/down depending on traffic + volume. + + +.. note:: + + Currently, this power management API is limited to mandatory mapping of 1 + queue to 1 core (multiple queues are supported, but they must be polled from + different cores). + +API Overview for PMD Power Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* **Queue Enable**: Enable specific power scheme for certain queue/port/core + +* **Queue Disable**: Disable power scheme for certain queue/port/core + References ----------
@@ -200,3 +241,6 @@ References * The :doc:`../sample_app_ug/vm_power_management` chapter in the :doc:`../sample_app_ug/index` section. + +* The :doc:`../sample_app_ug/rxtx_callbacks` + chapter in the :doc:`../sample_app_ug/index` section.
doc/guides/rel_notes/release_21_02.rst +10 -0
@@ -60,6 +60,16 @@ New Features * ``rte_eth_get_monitor_addr()``, to be used in conjunction with ``rte_power_monitor()`` to enable automatic power management for PMD's. +* **Add PMD power management helper API** + + A new helper API has been added to make using Ethernet PMD power management + easier for the user: ``rte_power_pmd_mgmt_queue_enable()``. Three power + management schemes are supported initially: + + * Power saving based on UMWAIT instruction (x86 only) + * Power saving based on ``rte_pause()`` + * Power saving based on frequency scaling through the ``librte_power`` library + Removed Items -------------
lib/librte_power/meson.build +3 -2
@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c', 'power_kvm_vm.c', 'guest_channel.c', 'rte_power_empty_poll.c', 'power_pstate_cpufreq.c', + 'rte_power_pmd_mgmt.c', 'power_common.c') -headers = files('rte_power.h','rte_power_empty_poll.h') -deps += ['timer'] +headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h') +deps += ['timer' ,'ethdev']
lib/librte_power/rte_power_pmd_mgmt.c new file +349 -0
349 lines not on this page · raw message
lib/librte_power/rte_power_pmd_mgmt.h new file +90 -0
90 lines not on this page · raw message
lib/librte_power/version.map +5 -0
@@ -34,4 +34,9 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + + # added in 21.02 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + };
Signature · 1 line
2.17.1
PATCH v12 8/11 net/ixgbe: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/ixgbe/ixgbe_ethdev.c +1 -0
@@ -560,6 +560,7 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .udp_tunnel_port_del = ixgbe_dev_udp_tunnel_port_del, .tm_ops_get = ixgbe_tm_ops_get, .tx_done_cleanup = ixgbe_dev_tx_done_cleanup, + .get_monitor_addr = ixgbe_get_monitor_addr, };/*
drivers/net/ixgbe/ixgbe_rxtx.c +25 -0
@@ -1369,6 +1369,31 @@ const uint32_t RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP, }; +int +ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ixgbe_adv_rx_desc *rxdp; +struct ixgbe_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.upper.status_error; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + pmc->mask =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + +/* the registers are 32-bit */ + pmc->data_sz =sizeof (uint32_t ); + +return 0 ; +} +/* @note: fix ixgbe_dev_supported_ptypes_get() if any change here. */ static inline uint32_t ixgbe_rxd_pkt_info_to_pkt_type (uint32_t pkt_info,uint16_t ptype_mask)
drivers/net/ixgbe/ixgbe_rxtx.h +1 -0
@@ -299,5 +299,6 @@ uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_queue_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_port_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_tx_queue_offloads (struct rte_eth_dev *dev); +int ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#endif /* _IXGBE_RXTX_H_ */
Signature · 1 line
2.17.1
PATCH v12 9/11 net/i40e: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/i40e/i40e_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/i40e/i40e_ethdev.c +1 -0
@@ -510,6 +510,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = { .mtu_set = i40e_dev_mtu_set, .tm_ops_get = i40e_tm_ops_get, .tx_done_cleanup = i40e_tx_done_cleanup, + .get_monitor_addr = i40e_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/i40e/i40e_rxtx.c +25 -0
@@ -72,6 +72,31 @@#define I40E_TX_OFFLOAD_NOTSUP_MASK \ (PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_MASK) +int +i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +struct i40e_rx_queue *rxq = rx_queue; +volatile union i40e_rx_desc *rxdp; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.qword1.status_error_len; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + pmc->mask =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + +/* registers are 64-bit */ + pmc->data_sz =sizeof (uint64_t ); + +return 0 ; +} +static inline void i40e_rxd_to_vlan_tci (struct rte_mbuf *mb,volatile union i40e_rx_desc *rxdp) {
drivers/net/i40e/i40e_rxtx.h +1 -0
@@ -248,6 +248,7 @@ uint16_t i40e_recv_scattered_pkts_vec_avx2(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);uint16_t i40e_xmit_pkts_vec_avx2 (void *tx_queue,struct rte_mbuf **tx_pkts,uint16_t nb_pkts); +int i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);/* For each value it means, datasheet of hardware can tell more details *
Signature · 1 line
2.17.1
PATCH v12 10/11 net/ice: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/ice/ice_rxtx.h | 1 + 3 files changed, 28 insertions(+)
drivers/net/ice/ice_ethdev.c +1 -0
@@ -216,6 +216,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .udp_tunnel_port_add = ice_dev_udp_tunnel_port_add, .udp_tunnel_port_del = ice_dev_udp_tunnel_port_del, .tx_done_cleanup = ice_tx_done_cleanup, + .get_monitor_addr = ice_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/ice/ice_rxtx.c +26 -0
@@ -26,6 +26,32 @@ uint64_t rte_net_ice_dynflag_proto_xtr_ipv6_flow_mask;uint64_t rte_net_ice_dynflag_proto_xtr_tcp_mask;uint64_t rte_net_ice_dynflag_proto_xtr_ip_offset_mask; +int +ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ice_rx_flex_desc *rxdp; +struct ice_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.status_error0; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + pmc->mask =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + +/* register is 16-bit */ + pmc->data_sz =sizeof (uint16_t ); + +return 0 ; +} + +static inline uint8_t ice_proto_xtr_type_to_rxdid (uint8_t xtr_type) {
drivers/net/ice/ice_rxtx.h +1 -0
@@ -263,6 +263,7 @@ uint16_t ice_xmit_pkts_vec_avx512(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);int ice_fdir_programming (struct ice_pf *pf,struct ice_fltr_desc *fdir_desc);int ice_tx_done_cleanup (void *txq,uint32_t free_cnt); +int ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#define FDIR_PARSING_ENABLE_PER_QUEUE (ad, on) do { \ int i; \
Signature · 1 line
2.17.1
PATCH v12 11/11 examples/l3fwd-power: enable PMD power mgmt
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add PMD power management feature support to l3fwd-power sample app.
---
Notes:
v6:
- Fixed typos in documentation
.../sample_app_ug/l3_forward_power_man.rst | 35 ++++++++ examples/l3fwd-power/main.c | 89 ++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-)
doc/guides/sample_app_ug/l3_forward_power_man.rst +35 -0
@@ -109,6 +109,8 @@ where, * --telemetry: Telemetry mode. +* --pmd-mgmt: PMD power management mode. + See :doc:`l3_forward` for details. The L3fwd-power example reuses the L3fwd command line options.
@@ -456,3 +458,36 @@ reference cycles and accordingly busy rate is set to either 0% or The new stats ``empty_poll`` , ``full_poll`` and ``busy_percent`` can be viewed by running the script ``/usertools/dpdk-telemetry-client.py`` and selecting the menu option ``Send for global Metrics``. + +PMD power management Mode +------------------------- + +The PMD power management mode support for ``l3fwd-power`` is a standalone mode, in this mode +``l3fwd-power`` does simple l3fwding along with enable the power saving scheme on specific +port/queue/lcore. Main purpose for this mode is to demonstrate how to use the PMD power management API. + +.. code-block:: console + + ./build/examples/dpdk-l3fwd-power -l 1-3 -- --pmd-mgmt -p 0x0f --config="(0,0,2),(0,1,3)" + +PMD Power Management Mode +------------------------- +There is also a traffic-aware operating mode that, instead of using explicit +power management, will use automatic PMD power management. This mode is limited +to one queue per core, and has three available power management schemes: + +* ``monitor`` - this will use ``rte_power_monitor()`` function to enter a + power-optimized state (subject to platform support). + +* ``pause`` - this will use ``rte_power_pause()`` or ``rte_pause()`` to avoid + busy looping when there is no traffic. + +* ``scale`` - this will use frequency scaling routines available in the + ``librte_power`` library. + +See :doc:`Power Management<../prog_guide/power_man>` chapter in the DPDK +Programmer's Guide for more details on PMD power management. + +.. code-block:: console + + ./<build_dir>/examples/dpdk-l3fwd-power -l 1-3 -- -p 0x0f --config="(0,0,2),(0,1,3)" --pmd-mgmt=scale
examples/l3fwd-power/main.c +87 -2
165 lines not on this page · raw message
Signature · 1 line
2.17.1
Re: [dpdk-dev] [PATCH v12 00/11] Add PMD power management
In reply to Anatoly BurakovQuoted 38 lines · Anatoly Burakov
This patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. This is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on it.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we get the address of next RX ring descriptor from the NIC driver, arm the monitoring hardware, and enter a power-optimized state. We will then wake up when either a timeout happens, or a write happens (or generally whenever CPU feels like waking up - this is platform-specific), and proceed as normal. The empty poll counter is reset whenever we actually get packets, so we only go to sleep when we know nothing is going on. The mechanism is generic which can be used for any write back descriptor.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway, not on a hotpath).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it wit minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Quoted 5 lines
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
Fyi, ovsrobot Travis being KO, you probably missed that GHA CI caught this:
https://github.com/ovsrobot/dpdk/runs/1571056574?check_suite_focus=true#step:13:16082
We will have to put an exception on driver only ABI.
Signature · 1 line
David Marchand
Re: [dpdk-dev] [PATCH v12 00/11] Add PMD power management
Quoted 46 lines · David Marchand
Quoted 38 lines · Anatoly Burakov
This patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. This is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on it.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we get the address of next RX ring descriptor from the NIC driver, arm the monitoring hardware, and enter a power-optimized state. We will then wake up when either a timeout happens, or a write happens (or generally whenever CPU feels like waking up - this is platform-specific), and proceed as normal. The empty poll counter is reset whenever we actually get packets, so we only go to sleep when we know nothing is going on. The mechanism is generic which can be used for any write back descriptor.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway, not on a hotpath).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it wit minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Quoted 5 lines
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
Quoted 6 lines
Fyi, ovsrobot Travis being KO, you probably missed that GHA CI caught this:
https://github.com/ovsrobot/dpdk/runs/1571056574?check_suite_focus=true#step:13:16082We will have to put an exception on driver only ABI.
Why does aarch64 build fail there? The functions in question are in the version map file, but the build complains that they aren't.
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v12 00/11] Add PMD power management
Quoted 51 lines · Anatoly
Quoted 49 lines · David Marchand
Quoted 38 lines · Anatoly Burakov
This patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. This is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on it.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we get the address of next RX ring descriptor from the NIC driver, arm the monitoring hardware, and enter a power-optimized state. We will then wake up when either a timeout happens, or a write happens (or generally whenever CPU feels like waking up - this is platform-specific), and proceed as normal. The empty poll counter is reset whenever we actually get packets, so we only go to sleep when we know nothing is going on. The mechanism is generic which can be used for any write back descriptor.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway, not on a hotpath).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it wit minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Quoted 5 lines
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device typesFyi, ovsrobot Travis being KO, you probably missed that GHA CI caught this:
https://github.com/ovsrobot/dpdk/runs/1571056574?check_suite_focus=true#step:13:16082
Quoted 6 lines
Quoted 3 lines
We will have to put an exception on driver only ABI.
Why does aarch64 build fail there? The functions in question are in the version map file, but the build complains that they aren't.
From what I can see, this series puts rte_power_* symbols in a .h. So it will be seen as symbols exported by any library including such a header.
The check then complains about this as it sees exported symbols unknown of the library version.map.
Signature · 1 line
David Marchand
Re: [dpdk-dev] [PATCH v12 00/11] Add PMD power management
Quoted 11 lines · David Marchand
Quoted 2 lines · Anatoly
Why does aarch64 build fail there? The functions in question are in the version map file, but the build complains that they aren't.
From what I can see, this series puts rte_power_* symbols in a .h. So it will be seen as symbols exported by any library including such a header.
The check then complains about this as it sees exported symbols unknown of the library version.map.
Quick fix:
(quoted diff) +0 -0
b/lib/librte_eal/arm/include/rte_power_intrinsics.h index 39e49cc45b..9e498e9ebf 100644
lib/librte_eal/arm/include/rte_power_intrinsics.h +0 -29
@@ -13,35 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -} - -/** -* This function is not supported on ARM. -*/ -void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor_wakeup (const unsigned int lcore_id) -{ -RTE_SET_USED (lcore_id); -} - #ifdef __cplusplus }#endif
lib/librte_eal/arm/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
(quoted diff) +0 -0
b/lib/librte_eal/arm/rte_power_intrinsics.c
new file mode 100644
index 0000000000..998f9898ad
lib/librte_eal/arm/rte_power_intrinsics.c new file +31 -0
@@ -0,0 +1,31 @@ +#include <rte_common.h> +#include <rte_power_intrinsics.h> + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) +{ +RTE_SET_USED (pmc); +RTE_SET_USED (tsc_timestamp); +} + +/** +* This function is not supported on ARM. +*/ +void +rte_power_pause (const uint64_t tsc_timestamp) +{ +RTE_SET_USED (tsc_timestamp); +} + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
HTH.
Signature · 1 line
David Marchand
Re: [dpdk-dev] [PATCH v12 00/11] Add PMD power management
Quoted 107 lines · David Marchand
Quoted 11 lines · David Marchand
Quoted 2 lines · Anatoly
Why does aarch64 build fail there? The functions in question are in the version map file, but the build complains that they aren't.
From what I can see, this series puts rte_power_* symbols in a .h.
So it will be seen as symbols exported by any library including such a header.The check then complains about this as it sees exported symbols unknown of the library version.map.
Quick fix:
lib/librte_eal/arm/include/rte_power_intrinsics.h +0
-29@@ -13,35 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -} - -/** -* This function is not supported on ARM. -*/ -void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor_wakeup (const unsigned int lcore_id) -{ -RTE_SET_USED (lcore_id); -} - #ifdef __cplusplus }#endif lib/librte_eal/arm/meson.build +1
-0@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )lib/librte_eal/arm/rte_power_intrinsics.c new file +31
-0@@ -0,0 +1,31 @@ +#include <rte_common.h> +#include <rte_power_intrinsics.h> + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) +{ +RTE_SET_USED (pmc); +RTE_SET_USED (tsc_timestamp); +} + +/** +* This function is not supported on ARM. +*/ +void +rte_power_pause (const uint64_t tsc_timestamp) +{ +RTE_SET_USED (tsc_timestamp); +} + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
Quoted 3 lines
HTH.
OK, will add into v14 so. Thanks!
Signature · 2 lines
Thanks, Anatoly
v13 cover Add PMD power management
In reply to Anatoly BurakovThis patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. There are multiple proposed mechanisms to achieve said power
savings: simple frequency scaling, idle loop, and monitoring the Rx queue for incoming packages. The latter is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on that address.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we employ one of the suggested power management schemes automatically, from within a Rx callback inside the PMD. Once there's traffic again, the empty poll counter is reset.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it with minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Things of note:
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
v13:
- Reworked the librte_power code to require less locking and handle invalid parameters better
- Fix numerous rebase errors present in v12
v12:
- Rebase on top of 21.02
- Rework of power intrinsics code
Anatoly Burakov (5):
eal: uninline power intrinsics
eal: avoid invalid API usage in power intrinsics
eal: change API of power intrinsics
eal: remove sync version of power monitor
eal: add monitor wakeup function
Liang Ma (6):
ethdev: add simple power management API
power: add PMD power management API and callback net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API examples/l3fwd-power: enable PMD power mgmt
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 15 + .../sample_app_ug/l3_forward_power_man.rst | 35 ++ drivers/event/dlb/dlb.c | 10 +- drivers/event/dlb2/dlb2.c | 10 +- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 ++ drivers/net/i40e/i40e_rxtx.h | 1 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++ drivers/net/ice/ice_rxtx.h | 1 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 ++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + examples/l3fwd-power/main.c | 89 ++++- .../arm/include/rte_power_intrinsics.h | 39 +- .../include/generic/rte_power_intrinsics.h | 78 ++-- .../ppc/include/rte_power_intrinsics.h | 39 +- lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ------ lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 184 +++++++++ lib/librte_ethdev/rte_ethdev.c | 28 ++ lib/librte_ethdev/rte_ethdev.h | 25 ++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++ lib/librte_ethdev/version.map | 3 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 360 ++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++ lib/librte_power/version.map | 5 + 30 files changed, 1055 insertions(+), 229 deletions(-) create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
Signature · 1 line
2.25.1
PATCH v13 1/11 eal: uninline power intrinsics
Currently, power intrinsics are inline functions. Make them part of the ABI so that we can have various internal data associated with them without exposing said data to the outside world.
--- .../arm/include/rte_power_intrinsics.h | 6 +- .../include/generic/rte_power_intrinsics.h | 6 +- .../ppc/include/rte_power_intrinsics.h | 6 +- lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ----------------- lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 120 ++++++++++++++++++ 7 files changed, 135 insertions(+), 124 deletions(-) create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c
lib/librte_eal/arm/include/rte_power_intrinsics.h +3 -3
@@ -16,7 +16,7 @@ extern "C" {/** * This function is not supported on ARM. */ -static inline void +void rte_power_monitor (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz)
@@ -31,7 +31,7 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on ARM. */ -static inline void +void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck)
@@ -47,7 +47,7 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on ARM. */ -static inline void +void rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp);
lib/librte_eal/include/generic/rte_power_intrinsics.h +3 -3
@@ -52,7 +52,7 @@* to undefined result. */ __rte_experimental -static inline void rte_power_monitor(const volatile void *p, +void rte_power_monitor(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz);
@@ -97,7 +97,7 @@ static inline void rte_power_monitor(const volatile void *p,* wakes up. */ __rte_experimental -static inline void rte_power_monitor_sync(const volatile void *p, +void rte_power_monitor_sync(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck);
@@ -118,6 +118,6 @@ static inline void rte_power_monitor_sync(const volatile void *p,* architecture-dependent. */ __rte_experimental -static inline void rte_power_pause(const uint64_t tsc_timestamp); +void rte_power_pause(const uint64_t tsc_timestamp);#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/ppc/include/rte_power_intrinsics.h +3 -3
@@ -16,7 +16,7 @@ extern "C" {/** * This function is not supported on PPC64. */ -static inline void +void rte_power_monitor (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz)
@@ -31,7 +31,7 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on PPC64. */ -static inline void +void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck)
@@ -47,7 +47,7 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/** * This function is not supported on PPC64. */ -static inline void +void rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp);
lib/librte_eal/version.map +5 -0
@@ -403,6 +403,11 @@ EXPERIMENTAL { rte_service_lcore_may_be_active; rte_vect_get_max_simd_bitwidth; rte_vect_set_max_simd_bitwidth; + + # added in 21.02 + rte_power_monitor; + rte_power_monitor_sync; + rte_power_pause; }; INTERNAL {
lib/librte_eal/x86/include/rte_power_intrinsics.h +0 -115
121 lines not on this page · raw message
lib/librte_eal/x86/meson.build +1 -0
@@ -8,4 +8,5 @@ sources += files( 'rte_cycles.c', 'rte_hypervisor.c', 'rte_spinlock.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/x86/rte_power_intrinsics.c new file +120 -0
120 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v13 01/11] eal: uninline power intrinsics
Quoted 13 lines
-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Friday, January 8, 2021 5:42 PM
To: [email protected]
Cc: Jan Viktorin <[email protected]>; Ruifeng Wang <[email protected]>; Jerin Jacob <[email protected]>; David Christensen <[email protected]>; Ray Kinsella <[email protected]>; Neil Horman <[email protected]>; Richardson, Bruce <[email protected]>; Ananyev, Konstantin <[email protected]>; [email protected]; [email protected]; McDaniel, Timothy <[email protected]>; Hunt, David <[email protected]>; Macnamara, Chris <[email protected]>
Subject: [PATCH v13 01/11] eal: uninline power intrinsics
Quoted 6 lines
Currently, power intrinsics are inline functions. Make them part of the ABI so that we can have various internal data associated with them without exposing said data to the outside world.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 2 lines
--
2.25.1
PATCH v13 2/11 eal: avoid invalid API usage in power intrinsics
In reply to Anatoly BurakovCurrently, the API documentation mandates that if the user wants to use the power management intrinsics, they need to call the rte_cpu_get_intrinsics_support API and check support for specific intrinsics.
However, if the user does not do that, it is possible to get illegal instruction error because we're using raw instruction opcodes, which may or may not be supported at runtime.
Now that we have everything in a C file, we can check for support at startup and prevent the user from possibly encountering illegal instruction errors.
--- .../include/generic/rte_power_intrinsics.h | 3 -- lib/librte_eal/x86/rte_power_intrinsics.c | 31 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-)
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -3
@@ -34,7 +34,6 @@* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -75,7 +74,6 @@ void rte_power_monitor(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -111,7 +109,6 @@ void rte_power_monitor_sync(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param tsc_timestamp * Maximum TSC timestamp to wait for. Note that the wait behavior is
lib/librte_eal/x86/rte_power_intrinsics.c +28 -3
@@ -4,6 +4,8 @@#include "rte_power_intrinsics.h" +static uint8_t wait_supported; +static inline uint64_t __get_umwait_val (const volatile void *p,const uint8_t sz) {
@@ -35,6 +37,11 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -72,6 +79,11 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -112,9 +124,22 @@ rte_power_pause(const uint64_t tsc_timestamp)const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* execute TPAUSE */ asmvolatile (".byte 0x66, 0x0f, 0xae, 0xf7;" - :/* ignore rflags */ - :"D" (0 ),/* enter C0.2 */ -"a" (tsc_l),"d" (tsc_h)); + :/* ignore rflags */ + :"D" (0 ),/* enter C0.2 */ +"a" (tsc_l),"d" (tsc_h)); +} + +RTE_INIT (rte_power_intrinsics_init) { +struct rte_cpu_intrinsics i; + +rte_cpu_get_intrinsics_support (&i); + +if (i.power_monitor && i.power_pause) + wait_supported =1 ; }
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v13 02/11] eal: avoid invalid API usage in power intrinsics
Quoted 1 line · On Fri, 8 Jan 2021 17:42:05 +0000 Anatoly Burakov <[email protected]>…
+static
uint8_twait_supported;
Since it is being used as a flag, bool is more common usage.
Re: [dpdk-dev] [PATCH v13 02/11] eal: avoid invalid API usage in power intrinsics
Quoted 7 lines · Stephen Hemminger
Quoted 1 line · On Fri, 8 Jan 2021 17:42:05 +0000 Anatoly Burakov <[email protected]>…
+static
uint8_twait_supported;Since it is being used as a flag, bool is more common usage.
Will fix in v14, thanks!
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v13 02/11] eal: avoid invalid API usage in power intrinsics
In reply to Anatoly BurakovQuoted 101 lines
Currently, the API documentation mandates that if the user wants to use the power management intrinsics, they need to call the
rte_cpu_get_intrinsics_supportAPI and check support for specific intrinsics.However, if the user does not do that, it is possible to get illegal instruction error because we're using raw instruction opcodes, which may or may not be supported at runtime.
Now that we have everything in a C file, we can check for support at startup and prevent the user from possibly encountering illegal instruction errors.
Signed-off-by Anatoly Burakov <[email protected]>--- .../include/generic/rte_power_intrinsics.h | 3 -- lib/librte_eal/x86/rte_power_intrinsics.c | 31 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-)lib/librte_eal/include/generic/rte_power_intrinsics.h +0
-3@@ -34,7 +34,6 @@* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes. @@ -75,7 +74,6 @@ void rte_power_monitor(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes. @@ -111,7 +109,6 @@ void rte_power_monitor_sync(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param tsc_timestamp * Maximum TSC timestamp to wait for. Note that the wait behavior is lib/librte_eal/x86/rte_power_intrinsics.c +20
-3@@ -4,6 +4,8 @@#include "rte_power_intrinsics.h" +static uint8_t wait_supported; +static inline uint64_t __get_umwait_val (const volatile void *p,const uint8_t sz) {@@ -35,6 +37,11 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively. @@ -72,6 +79,11 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively. @@ -112,9 +124,22 @@ rte_power_pause(const uint64_t tsc_timestamp)const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* execute TPAUSE */ asmvolatile (".byte 0x66, 0x0f, 0xae, 0xf7;" - :/* ignore rflags */ - :"D" (0 ),/* enter C0.2 */ -"a" (tsc_l),"d" (tsc_h)); + :/* ignore rflags */ + :"D" (0 ),/* enter C0.2 */ +"a" (tsc_l),"d" (tsc_h)); +}
lib/librte_eal/x86/rte_power_intrinsics.c rte_power_pause(const uint64_t tsc_timestamp)
Quoted 10 lines
lib/librte_eal/x86/rte_power_intrinsics.c +8
-1+ +RTE_INIT (rte_power_intrinsics_init) { +struct rte_cpu_intrinsics i; + +rte_cpu_get_intrinsics_support (&i); + +if (i.power_monitor && i.power_pause) + wait_supported =1 ; } --
Quoted 1 line
2.25.1
PATCH v13 3/11 eal: change API of power intrinsics
In reply to Anatoly BurakovInstead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
--- drivers/event/dlb/dlb.c | 10 ++-- drivers/event/dlb2/dlb2.c | 10 ++-- .../arm/include/rte_power_intrinsics.h | 20 +++----- .../include/generic/rte_power_intrinsics.h | 49 ++++++++----------- .../ppc/include/rte_power_intrinsics.h | 20 +++----- lib/librte_eal/x86/rte_power_intrinsics.c | 32 ++++++------ 6 files changed, 62 insertions(+), 79 deletions(-)
drivers/event/dlb/dlb.c +7 -3
@@ -3161,6 +3161,7 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,/* Interrupts not supported by PF PMD */ return 1 ; } else if (dlb->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -3181,9 +3182,12 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
drivers/event/dlb2/dlb2.c +7 -3
@@ -2870,6 +2870,7 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,if (elapsed_ticks >= timeout) {return 1 ; }else if (dlb2->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb2_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -2890,9 +2891,12 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB2_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
lib/librte_eal/arm/include/rte_power_intrinsics.h +6 -14
@@ -17,31 +17,23 @@ extern "C" {* This function is not supported on ARM. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on ARM. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/**
lib/librte_eal/include/generic/rte_power_intrinsics.h +20 -29
81 lines not on this page · raw message
lib/librte_eal/ppc/include/rte_power_intrinsics.h +6 -14
@@ -17,31 +17,23 @@ extern "C" {* This function is not supported on PPC64. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on PPC64. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/**
lib/librte_eal/x86/rte_power_intrinsics.c +16 -16
@@ -31,9 +31,8 @@ __get_umwait_val(const volatile void *p, const uint8_t sz)* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -50,14 +49,15 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }/* execute UMWAIT */
@@ -73,9 +73,8 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -92,14 +91,15 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }rte_spinlock_unlock (lck);
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v13 03/11] eal: change API of power intrinsics
Quoted 7 lines
Instead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 2 lines
--
2.25.1
PATCH v13 4/11 eal: remove sync version of power monitor
In reply to Anatoly BurakovCurrently, the "sync" version of power monitor intrinsic is supposed to be used for purposes of waking up a sleeping core. However, there are better ways to achieve the same result, so remove the unneeded function.
--- .../arm/include/rte_power_intrinsics.h | 12 ----- .../include/generic/rte_power_intrinsics.h | 34 -------------- .../ppc/include/rte_power_intrinsics.h | 12 ----- lib/librte_eal/version.map | 1 - lib/librte_eal/x86/rte_power_intrinsics.c | 46 ------------------- 5 files changed, 105 deletions(-)
lib/librte_eal/arm/include/rte_power_intrinsics.h +0 -12
@@ -24,18 +24,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on ARM. */
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -34
@@ -57,40 +57,6 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); -/** -* @warning -* @b EXPERIMENTAL: this API may change without prior notice -* -* Monitor specific address for changes. This will cause the CPU to enter an -* architecture-defined optimized power state until either the specified -* memory address is written to, a certain TSC timestamp is reached, or other -* reasons cause the CPU to wake up. -* -* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If -* mask is non-zero, the current value pointed to by the `p` pointer will be -* checked against the expected value, and if they match, the entering of -* optimized power state may be aborted. -* -* This call will also lock a spinlock on entering sleep, and release it on -* waking up the CPU. -* -* @warning It is responsibility of the user to check if this function is -* supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* -* @param pmc -* The monitoring condition structure. -* @param tsc_timestamp -* Maximum TSC timestamp to wait for. Note that the wait behavior is -* architecture-dependent. -* @param lck -* A spinlock that must be locked before entering the function, will be -* unlocked while the CPU is sleeping, and will be locked again once the CPU -* wakes up. -*/ -__rte_experimental -void rte_power_monitor_sync(const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck); -/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/include/rte_power_intrinsics.h +0 -12
@@ -24,18 +24,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on PPC64. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on PPC64. */
lib/librte_eal/version.map +0 -1
@@ -406,7 +406,6 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; - rte_power_monitor_sync; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +0 -46
52 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v13 04/11] eal: remove sync version of power monitor
Quoted 9 lines
Currently, the "sync" version of power monitor intrinsic is supposed to be used for purposes of waking up a sleeping core. However, there are better ways to achieve the same result, so remove the unneeded function.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 2 lines
--
2.25.1
PATCH v13 5/11 eal: add monitor wakeup function
In reply to Anatoly BurakovNow that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from rte_power_pause won't work.
---
Notes:
v13:
- Add comments around wakeup code to explain what it does
- Add lcore_id parameter checking to prevent buffer overrun
.../arm/include/rte_power_intrinsics.h | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 ++++ .../ppc/include/rte_power_intrinsics.h | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 85 +++++++++++++++++++ 5 files changed, 120 insertions(+)
lib/librte_eal/arm/include/rte_power_intrinsics.h +9 -0
@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif
lib/librte_eal/include/generic/rte_power_intrinsics.h +16 -0
@@ -57,6 +57,22 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/include/rte_power_intrinsics.h +9 -0
@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif
lib/librte_eal/version.map +1 -0
@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; + rte_power_monitor_wakeup; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +85 -0
113 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v13 05/11] eal: add monitor wakeup function
Quoted 151 lines
Now that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from
rte_power_pausewon't work.Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v13:
- Add comments around wakeup code to explain what it does
- Addlcore_idparameter checking to prevent buffer overrun.../arm/include/rte_power_intrinsics.h | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 ++++ .../ppc/include/rte_power_intrinsics.h | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 85 +++++++++++++++++++ 5 files changed, 120 insertions(+)lib/librte_eal/arm/include/rte_power_intrinsics.h +9
-0@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif lib/librte_eal/include/generic/rte_power_intrinsics.h +16
-0@@ -57,6 +57,22 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice lib/librte_eal/ppc/include/rte_power_intrinsics.h +9
-0@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif lib/librte_eal/version.map +1
-0@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; + rte_power_monitor_wakeup; rte_power_pause; };lib/librte_eal/x86/rte_power_intrinsics.c +29
-0@@ -2,8 +2,31 @@* Copyright(c) 2020 Intel Corporation */ +#include <rte_common.h> +#include <rte_lcore.h> +#include <rte_spinlock.h> +#include "rte_power_intrinsics.h" +/* +* Per-lcore structure holding current status of C0.2 sleeps. +*/ +static struct power_wait_status { +rte_spinlock_t lock; +volatile void *monitor_addr;/**< NULL if not currently sleeping */ +} __rte_cache_aligned wait_status[RTE_MAX_LCORE]; + +static inline void +__umwait_wakeup (volatile void *addr) +{ +uint64_t val; + +/* trigger a write but don't change the value */ + val =__atomic_load_n ((volatile uint64_t *)addr, __ATOMIC_RELAXED); +__atomic_compare_exchange_n ((volatile uint64_t *)addr, &val, val,0 , + __ATOMIC_RELAXED, __ATOMIC_RELAXED); +} +static uint8_t wait_supported; static inline uint64_t@@ -36,6 +59,12 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +const unsigned int lcore_id =rte_lcore_id (); +struct power_wait_status *s; + +/* prevent non-EAL thread from using this API */ +if (lcore_id >=RTE_MAX_LCORE ) +return ;/* prevent user from running this instruction if it's not supported */ if (!wait_supported)
lib/librte_eal/x86/rte_power_intrinsics.c rte_power_monitor(const struct rte_power_monitor_cond *pmc, · line 98
Quoted 10 lines
lib/librte_eal/x86/rte_power_intrinsics.c +7
-0@@ -60,11 +89,24 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,if (masked == pmc->val)return ; } + + s = &wait_status[lcore_id]; + +/* update sleep address */ +rte_spinlock_lock (&s->lock); + s->monitor_addr = pmc->addr; +rte_spinlock_unlock (&s->lock);
It was a while, since I looked at it last time,
but shouldn't we grab the lock before monitor()?
I.E:lock();monitor();
addr=...;unlock();umwait();
Quoted 63 lines
lib/librte_eal/x86/rte_power_intrinsics.c +49
-1+/* execute UMWAIT */ asmvolatile (".byte 0xf2, 0x0f, 0xae, 0xf7;" :/* ignore rflags */ :"D" (0 ),/* enter C0.2 */ "a" (tsc_l),"d" (tsc_h)); + +/* erase sleep address */ +rte_spinlock_lock (&s->lock); + s->monitor_addr =NULL ; +rte_spinlock_unlock (&s->lock); }/** @@ -97,3 +139,46 @@ RTE_INIT(rte_power_intrinsics_init) {if (i.power_monitor && i.power_pause) wait_supported =1 ; } + +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +struct power_wait_status *s; + +/* prevent buffer overrun */ +if (lcore_id >=RTE_MAX_LCORE ) +return ; + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; + + s = &wait_status[lcore_id]; + +/* +* There is a race condition between sleep, wakeup and locking, but we +* don't need to handle it. +* +* Possible situations: +* +* 1. T1 locks, sets address, unlocks +* 2. T2 locks, triggers wakeup, unlocks +* 3. T1 sleeps +* +* In this case, because T1 has already set the address for monitoring, +* we will wake up immediately even if T2 triggers wakeup before T1 +* goes to sleep. +* +* 1. T1 locks, sets address, unlocks, goes to sleep, and wakes up +* 2. T2 locks, triggers wakeup, and unlocks +* 3. T1 locks, erases address, and unlocks +* +* In this case, since we've already woken up, the "wakeup" was +* unneeded, and since T1 is still waiting on T2 releasing the lock, the +* wakeup address is still valid so it's perfectly safe to write it. +*/ +rte_spinlock_lock (&s->lock); +if (s->monitor_addr !=NULL ) +__umwait_wakeup (s->monitor_addr); +rte_spinlock_unlock (&s->lock); +} --2.25.1
Re: [dpdk-dev] [PATCH v13 05/11] eal: add monitor wakeup function
Quoted 162 lines · Konstantin
Quoted 151 lines
Now that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from
rte_power_pausewon't work.Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v13:
- Add comments around wakeup code to explain what it does
- Addlcore_idparameter checking to prevent buffer overrun.../arm/include/rte_power_intrinsics.h | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 ++++ .../ppc/include/rte_power_intrinsics.h | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 85 +++++++++++++++++++ 5 files changed, 120 insertions(+)lib/librte_eal/arm/include/rte_power_intrinsics.h +9
-0@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif lib/librte_eal/include/generic/rte_power_intrinsics.h +16
-0@@ -57,6 +57,22 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice lib/librte_eal/ppc/include/rte_power_intrinsics.h +9
-0@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp)RTE_SET_USED (tsc_timestamp); } +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus }#endif lib/librte_eal/version.map +1
-0@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; +rte_power_monitor_wakeup; rte_power_pause; };lib/librte_eal/x86/rte_power_intrinsics.c +29
-0@@ -2,8 +2,31 @@* Copyright(c) 2020 Intel Corporation */ +#include <rte_common.h> +#include <rte_lcore.h> +#include <rte_spinlock.h> +#include "rte_power_intrinsics.h" +/* +* Per-lcore structure holding current status of C0.2 sleeps. +*/ +static struct power_wait_status { +rte_spinlock_t lock; +volatile void *monitor_addr;/**< NULL if not currently sleeping */ +} __rte_cache_aligned wait_status[RTE_MAX_LCORE]; + +static inline void +__umwait_wakeup (volatile void *addr) +{ +uint64_t val; + +/* trigger a write but don't change the value */ +val =__atomic_load_n ((volatile uint64_t *)addr, __ATOMIC_RELAXED); +__atomic_compare_exchange_n ((volatile uint64_t *)addr, &val, val,0 , +__ATOMIC_RELAXED, __ATOMIC_RELAXED); +} +static uint8_t wait_supported; static inline uint64_t@@ -36,6 +59,12 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +const unsigned int lcore_id =rte_lcore_id (); +struct power_wait_status *s; + +/* prevent non-EAL thread from using this API */ +if (lcore_id >=RTE_MAX_LCORE ) +return ;/* prevent user from running this instruction if it's not supported */ if (!wait_supported)lib/librte_eal/x86/rte_power_intrinsics.c rte_power_monitor(const struct rte_power_monitor_cond *pmc, · line 98
Quoted 10 lines
lib/librte_eal/x86/rte_power_intrinsics.c +7
-0@@ -60,11 +89,24 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,if (masked == pmc->val)return ; } + +s = &wait_status[lcore_id]; + +/* update sleep address */ +rte_spinlock_lock (&s->lock); +s->monitor_addr = pmc->addr; +rte_spinlock_unlock (&s->lock);
Quoted 9 lines
It was a while, since I looked at it last time,
but shouldn't we grab the lock beforemonitor()?
I.E:lock();monitor();
addr=...;unlock();umwait();
I don't believe so.
The idea here is to only store the address when we are looking to sleep, and avoid the locks entirely if we already know we aren't going to sleep. I mean, technically we could lock unconditionally, then unlock when we're done, but there's very little practical difference between the two because the moment we are interested in (sleep) happens the same way whether we lock before or after monitor().
Quoted 63 lines
Quoted 63 lines
lib/librte_eal/x86/rte_power_intrinsics.c +49
-1+/* execute UMWAIT */ asmvolatile (".byte 0xf2, 0x0f, 0xae, 0xf7;" :/* ignore rflags */ :"D" (0 ),/* enter C0.2 */ "a" (tsc_l),"d" (tsc_h)); + +/* erase sleep address */ +rte_spinlock_lock (&s->lock); +s->monitor_addr =NULL ; +rte_spinlock_unlock (&s->lock); }/** @@ -97,3 +139,46 @@ RTE_INIT(rte_power_intrinsics_init) {if (i.power_monitor && i.power_pause) wait_supported =1 ; } + +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +struct power_wait_status *s; + +/* prevent buffer overrun */ +if (lcore_id >=RTE_MAX_LCORE ) +return ; + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; + +s = &wait_status[lcore_id]; + +/* +* There is a race condition between sleep, wakeup and locking, but we +* don't need to handle it. +* +* Possible situations: +* +* 1. T1 locks, sets address, unlocks +* 2. T2 locks, triggers wakeup, unlocks +* 3. T1 sleeps +* +* In this case, because T1 has already set the address for monitoring, +* we will wake up immediately even if T2 triggers wakeup before T1 +* goes to sleep. +* +* 1. T1 locks, sets address, unlocks, goes to sleep, and wakes up +* 2. T2 locks, triggers wakeup, and unlocks +* 3. T1 locks, erases address, and unlocks +* +* In this case, since we've already woken up, the "wakeup" was +* unneeded, and since T1 is still waiting on T2 releasing the lock, the +* wakeup address is still valid so it's perfectly safe to write it. +*/ +rte_spinlock_lock (&s->lock); +if (s->monitor_addr !=NULL ) +__umwait_wakeup (s->monitor_addr); +rte_spinlock_unlock (&s->lock); +} --2.25.1
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v13 05/11] eal: add monitor wakeup function
Quoted 177 lines · Anatoly
Quoted 166 lines · Konstantin
Quoted 155 lines
Now that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from
rte_power_pausewon't work.Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v13:
- Add comments around wakeup code to explain what it does
- Addlcore_idparameter checking to prevent buffer overrun.../arm/include/rte_power_intrinsics.h | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 ++++ .../ppc/include/rte_power_intrinsics.h | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 85 +++++++++++++++++++
5 files changed, 120 insertions(+)lib/librte_eal/arm/include/rte_power_intrinsics.h +9
-0@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp) RTE_SET_USED(tsc_timestamp); } +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus } #endiflib/librte_eal/include/generic/rte_power_intrinsics.h +5
-0@@ -57,6 +57,22 @@ __rte_experimental void rte_power_monitor(const struct rte_power_monitor_cond *pmc, const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring
+ * an address.
+ *
+ * @note This function will *not* wake up a core that is in a power optimized
+ * state due to callingrte_power_pause.
+ *
+ * @paramlcore_id
+ * Lcore ID of a sleeping thread.
+ */lib/librte_eal/include/generic/rte_power_intrinsics.h +3
-0+__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/**
* @warning
* @b EXPERIMENTAL: this API may change without prior noticelib/librte_eal/ppc/include/rte_power_intrinsics.h +9
-0@@ -33,6 +33,15 @@ rte_power_pause(const uint64_t tsc_timestamp) RTE_SET_USED(tsc_timestamp); } +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +} + #ifdef __cplusplus } #endiflib/librte_eal/version.map +1
-0@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; +rte_power_monitor_wakeup; rte_power_pause; };lib/librte_eal/x86/rte_power_intrinsics.c +23
-0@@ -2,8 +2,31 @@* Copyright(c) 2020 Intel Corporation */ +#include <rte_common.h> +#include <rte_lcore.h> +#include <rte_spinlock.h> + #include "rte_power_intrinsics.h" +/* +* Per-lcore structure holding current status of C0.2 sleeps. +*/ +static struct power_wait_status { +rte_spinlock_t lock; +volatile void *monitor_addr;/**< NULL if not currently sleeping */ +} __rte_cache_aligned wait_status[RTE_MAX_LCORE]; + +static inline void +__umwait_wakeup (volatile void *addr) +{ +uint64_t val; + +/* trigger a write but don't change the value */ +val =__atomic_load_n ((volatile uint64_t *)addr, __ATOMIC_RELAXED); +__atomic_compare_exchange_n ((volatile uint64_t *)addr, &val, val,0 , +__ATOMIC_RELAXED, __ATOMIC_RELAXED); +} + static uint8_t wait_supported;static inline
uint64_tlib/librte_eal/x86/rte_power_intrinsics.c +6
-0@@ -36,6 +59,12 @@ rte_power_monitor(const structrte_power_monitor_cond *pmc, { const uint32_t tsc_l = (uint32_t)tsc_timestamp; const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32); +const unsigned int lcore_id =rte_lcore_id (); +struct power_wait_status *s; + +/* prevent non-EAL thread from using this API */ +if (lcore_id >=RTE_MAX_LCORE ) +return ; /* prevent user from running this instruction if it's not supported */if (!wait_supported)
lib/librte_eal/x86/rte_power_intrinsics.c +0
-0@@ -60,11 +89,24 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,lib/librte_eal/x86/rte_power_intrinsics.c rte_power_monitor(const struct · line 99
Quoted 10 lines
lib/librte_eal/x86/rte_power_intrinsics.c +7
-0if (masked == pmc->val) return; } + +s = &wait_status[lcore_id]; + +/* update sleep address */ +rte_spinlock_lock (&s->lock); +s->monitor_addr = pmc->addr; +rte_spinlock_unlock (&s->lock);Quoted 9 lines
It was a while, since I looked at it last time,
but shouldn't we grab the lock beforemonitor()?
I.E:lock();monitor();
addr=...;unlock();umwait();
Quoted 3 lines
I don't believe so.
The idea here is to only store the address when we are looking to sleep, and avoid the locks entirely if we already know we aren't going to sleep. I mean, technically we could lock unconditionally, then unlock when we're done, but there's very little practical difference between the two because the moment we are interested in (sleep) happens the same way whether we lock before or after
monitor().
On another thought, putting the lock before monitor() and unlocking afterwards allows us to ask for a wakeup earlier, without necessarily waiting for a sleep. So think i'll take your suggestion on board anyway, thanks!
Quoted 67 lines
Quoted 65 lines
Quoted 65 lines
lib/librte_eal/x86/rte_power_intrinsics.c +1
-0+ /* execute UMWAIT */asm volatile(".byte
0xf2,0x0f,0xae,0xf7;"
: /* ignore rflags */
: "D"(0), /* enter C0.2 */
"a"(tsc_l), "d"(tsc_h));lib/librte_eal/x86/rte_power_intrinsics.c +5
-0+ +/* erase sleep address */ +rte_spinlock_lock (&s->lock); +s->monitor_addr =NULL ; +rte_spinlock_unlock (&s->lock);}
/**
lib/librte_eal/x86/rte_power_intrinsics.c +36
-0@@ -97,3 +139,46 @@ RTE_INIT(rte_power_intrinsics_init) { if (i.power_monitor && i.power_pause) wait_supported = 1; } + +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +struct power_wait_status *s; + +/* prevent buffer overrun */ +if (lcore_id >=RTE_MAX_LCORE ) +return ; + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; + +s = &wait_status[lcore_id]; + +/* +* There is a race condition between sleep, wakeup and locking, but we +* don't need to handle it. +* +* Possible situations: +* +* 1. T1 locks, sets address, unlocks +* 2. T2 locks, triggers wakeup, unlocks +* 3. T1 sleeps +* +* In this case, because T1 has already set the address for monitoring, +* we will wake up immediately even if T2 triggers wakeup before T1 +* goes to sleep. +* +* 1. T1 locks, sets address, unlocks, goes to sleep, and wakes up +* 2. T2 locks, triggers wakeup, and unlocks +* 3. T1 locks, erases address, and unlocks +* +* In this case, since we've already woken up, the "wakeup" was +* unneeded, and since T1 is still waiting on T2 releasing the lock, the
+ * wakeup address is still valid so it's perfectly safe to write it.
+ */lib/librte_eal/x86/rte_power_intrinsics.c +5
-0+rte_spinlock_lock (&s->lock); +if (s->monitor_addr !=NULL ) +__umwait_wakeup (s->monitor_addr); +rte_spinlock_unlock (&s->lock); +}Signature · 1 line
2.25.1
Signature · 2 lines
Thanks, Anatoly
PATCH v13 6/11 ethdev: add simple power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
---
Notes:
v13:
- Fix typos and issues raised by Andrew
doc/guides/rel_notes/release_21_02.rst | 5 +++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 83 insertions(+)
doc/guides/rel_notes/release_21_02.rst +5 -0
@@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added new API for PMD power management** + + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's. + Removed Items -------------
lib/librte_ethdev/rte_ethdev.c +28 -0
@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{ +struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_rx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid Rx queue_id=%u\n" , queue_id); +return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,
lib/librte_ethdev/rte_ethdev.h +25 -0
@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h"
@@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size)
lib/librte_ethdev/rte_ethdev_driver.h +22 -0
@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an Rx queue. +* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get power monitoring condition for Rx queue. */ };/**
lib/librte_ethdev/version.map +3 -0
@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v13 06/11] ethdev: add simple power management API
Quoted 5 lines · Anatoly Burakov
From: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the RX queues from the PMD, as well as release notes information.
RX -> Rx
Quoted 3 lines
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>
PATCH v13 7/11 power: add PMD power management API and callback
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.
---
Notes:
v13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Rework n_rx_queues access to not go through internal PMD structures and use public API instead
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 360 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 512 insertions(+), 2 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
doc/guides/prog_guide/power_man.rst +44 -0
@@ -192,6 +192,47 @@ User Cases ---------- The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA. +PMD Power Management API +------------------------ + +Abstract +~~~~~~~~ +Existing power management mechanisms require developers to change application +design or change code to make use of it. The PMD power management API provides a +convenient alternative by utilizing Ethernet PMD RX callbacks, and triggering +power saving whenever empty poll count reaches a certain number. + + * Monitor + + This power saving scheme will put the CPU into optimized power state and use + the ``rte_power_monitor()`` function to monitor the Ethernet PMD RX + descriptor address, and wake the CPU up whenever there's new traffic. + + * Pause + + This power saving scheme will avoid busy polling by either entering + power-optimized sleep state with ``rte_power_pause()`` function, or, if it's + not available, use ``rte_pause()``. + + * Frequency scaling + + This power saving scheme will use existing ``librte_power`` library + functionality to scale the core frequency up/down depending on traffic + volume. + + +.. note:: + + Currently, this power management API is limited to mandatory mapping of 1 + queue to 1 core (multiple queues are supported, but they must be polled from + different cores). + +API Overview for PMD Power Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* **Queue Enable**: Enable specific power scheme for certain queue/port/core + +* **Queue Disable**: Disable power scheme for certain queue/port/core + References ----------
@@ -200,3 +241,6 @@ References * The :doc:`../sample_app_ug/vm_power_management` chapter in the :doc:`../sample_app_ug/index` section. + +* The :doc:`../sample_app_ug/rxtx_callbacks` + chapter in the :doc:`../sample_app_ug/index` section.
doc/guides/rel_notes/release_21_02.rst +10 -0
@@ -60,6 +60,16 @@ New Features * ``rte_eth_get_monitor_addr()``, to be used in conjunction with ``rte_power_monitor()`` to enable automatic power management for PMD's. +* **Add PMD power management helper API** + + A new helper API has been added to make using Ethernet PMD power management + easier for the user: ``rte_power_pmd_mgmt_queue_enable()``. Three power + management schemes are supported initially: + + * Power saving based on UMWAIT instruction (x86 only) + * Power saving based on ``rte_pause()`` (generic) or TPAUSE instruction (x86 only) + * Power saving based on frequency scaling through the ``librte_power`` library + Removed Items -------------
lib/librte_power/meson.build +3 -2
@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c', 'power_kvm_vm.c', 'guest_channel.c', 'rte_power_empty_poll.c', 'power_pstate_cpufreq.c', + 'rte_power_pmd_mgmt.c', 'power_common.c') -headers = files('rte_power.h','rte_power_empty_poll.h') -deps += ['timer'] +headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h') +deps += ['timer' ,'ethdev']
lib/librte_power/rte_power_pmd_mgmt.c new file +360 -0
360 lines not on this page · raw message
lib/librte_power/rte_power_pmd_mgmt.h new file +90 -0
90 lines not on this page · raw message
lib/librte_power/version.map +5 -0
@@ -34,4 +34,9 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + + # added in 21.02 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + };
Signature · 1 line
2.25.1
PATCH v13 8/11 net/ixgbe: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/ixgbe/ixgbe_ethdev.c +1 -0
@@ -560,6 +560,7 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .udp_tunnel_port_del = ixgbe_dev_udp_tunnel_port_del, .tm_ops_get = ixgbe_tm_ops_get, .tx_done_cleanup = ixgbe_dev_tx_done_cleanup, + .get_monitor_addr = ixgbe_get_monitor_addr, };/*
drivers/net/ixgbe/ixgbe_rxtx.c +25 -0
@@ -1369,6 +1369,31 @@ const uint32_t RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP, }; +int +ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ixgbe_adv_rx_desc *rxdp; +struct ixgbe_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.upper.status_error; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + pmc->mask =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + +/* the registers are 32-bit */ + pmc->data_sz =sizeof (uint32_t ); + +return 0 ; +} +/* @note: fix ixgbe_dev_supported_ptypes_get() if any change here. */ static inline uint32_t ixgbe_rxd_pkt_info_to_pkt_type (uint32_t pkt_info,uint16_t ptype_mask)
drivers/net/ixgbe/ixgbe_rxtx.h +1 -0
@@ -299,5 +299,6 @@ uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_queue_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_port_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_tx_queue_offloads (struct rte_eth_dev *dev); +int ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#endif /* _IXGBE_RXTX_H_ */
Signature · 1 line
2.25.1
PATCH v13 9/11 net/i40e: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/i40e/i40e_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/i40e/i40e_ethdev.c +1 -0
@@ -510,6 +510,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = { .mtu_set = i40e_dev_mtu_set, .tm_ops_get = i40e_tm_ops_get, .tx_done_cleanup = i40e_tx_done_cleanup, + .get_monitor_addr = i40e_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/i40e/i40e_rxtx.c +25 -0
@@ -72,6 +72,31 @@#define I40E_TX_OFFLOAD_NOTSUP_MASK \ (PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_MASK) +int +i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +struct i40e_rx_queue *rxq = rx_queue; +volatile union i40e_rx_desc *rxdp; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.qword1.status_error_len; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + pmc->mask =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + +/* registers are 64-bit */ + pmc->data_sz =sizeof (uint64_t ); + +return 0 ; +} +static inline void i40e_rxd_to_vlan_tci (struct rte_mbuf *mb,volatile union i40e_rx_desc *rxdp) {
drivers/net/i40e/i40e_rxtx.h +1 -0
@@ -248,6 +248,7 @@ uint16_t i40e_recv_scattered_pkts_vec_avx2(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);uint16_t i40e_xmit_pkts_vec_avx2 (void *tx_queue,struct rte_mbuf **tx_pkts,uint16_t nb_pkts); +int i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);/* For each value it means, datasheet of hardware can tell more details *
Signature · 1 line
2.25.1
PATCH v13 10/11 net/ice: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/ice/ice_rxtx.h | 1 + 3 files changed, 28 insertions(+)
drivers/net/ice/ice_ethdev.c +1 -0
@@ -216,6 +216,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .udp_tunnel_port_add = ice_dev_udp_tunnel_port_add, .udp_tunnel_port_del = ice_dev_udp_tunnel_port_del, .tx_done_cleanup = ice_tx_done_cleanup, + .get_monitor_addr = ice_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/ice/ice_rxtx.c +26 -0
@@ -26,6 +26,32 @@ uint64_t rte_net_ice_dynflag_proto_xtr_ipv6_flow_mask;uint64_t rte_net_ice_dynflag_proto_xtr_tcp_mask;uint64_t rte_net_ice_dynflag_proto_xtr_ip_offset_mask; +int +ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ice_rx_flex_desc *rxdp; +struct ice_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.status_error0; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + pmc->mask =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + +/* register is 16-bit */ + pmc->data_sz =sizeof (uint16_t ); + +return 0 ; +} + +static inline uint8_t ice_proto_xtr_type_to_rxdid (uint8_t xtr_type) {
drivers/net/ice/ice_rxtx.h +1 -0
@@ -263,6 +263,7 @@ uint16_t ice_xmit_pkts_vec_avx512(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);int ice_fdir_programming (struct ice_pf *pf,struct ice_fltr_desc *fdir_desc);int ice_tx_done_cleanup (void *txq,uint32_t free_cnt); +int ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#define FDIR_PARSING_ENABLE_PER_QUEUE (ad, on) do { \ int i; \
Signature · 1 line
2.25.1
PATCH v13 11/11 examples/l3fwd-power: enable PMD power mgmt
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add PMD power management feature support to l3fwd-power sample app.
---
Notes:
v12:
- Allow selecting PMD power management scheme from command-line
- Enforce 1 core 1 queue rule
.../sample_app_ug/l3_forward_power_man.rst | 35 ++++++++ examples/l3fwd-power/main.c | 89 ++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-)
doc/guides/sample_app_ug/l3_forward_power_man.rst +35 -0
@@ -109,6 +109,8 @@ where, * --telemetry: Telemetry mode. +* --pmd-mgmt: PMD power management mode. + See :doc:`l3_forward` for details. The L3fwd-power example reuses the L3fwd command line options.
@@ -456,3 +458,36 @@ reference cycles and accordingly busy rate is set to either 0% or The new stats ``empty_poll`` , ``full_poll`` and ``busy_percent`` can be viewed by running the script ``/usertools/dpdk-telemetry-client.py`` and selecting the menu option ``Send for global Metrics``. + +PMD power management Mode +------------------------- + +The PMD power management mode support for ``l3fwd-power`` is a standalone mode, in this mode +``l3fwd-power`` does simple l3fwding along with enable the power saving scheme on specific +port/queue/lcore. Main purpose for this mode is to demonstrate how to use the PMD power management API. + +.. code-block:: console + + ./build/examples/dpdk-l3fwd-power -l 1-3 -- --pmd-mgmt -p 0x0f --config="(0,0,2),(0,1,3)" + +PMD Power Management Mode +------------------------- +There is also a traffic-aware operating mode that, instead of using explicit +power management, will use automatic PMD power management. This mode is limited +to one queue per core, and has three available power management schemes: + +* ``monitor`` - this will use ``rte_power_monitor()`` function to enter a + power-optimized state (subject to platform support). + +* ``pause`` - this will use ``rte_power_pause()`` or ``rte_pause()`` to avoid + busy looping when there is no traffic. + +* ``scale`` - this will use frequency scaling routines available in the + ``librte_power`` library. + +See :doc:`Power Management<../prog_guide/power_man>` chapter in the DPDK +Programmer's Guide for more details on PMD power management. + +.. code-block:: console + + ./<build_dir>/examples/dpdk-l3fwd-power -l 1-3 -- -p 0x0f --config="(0,0,2),(0,1,3)" --pmd-mgmt=scale
examples/l3fwd-power/main.c +87 -2
165 lines not on this page · raw message
Signature · 1 line
2.25.1
v14 cover Add PMD power management
In reply to Anatoly BurakovThis patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. There are multiple proposed mechanisms to achieve said power
savings: simple frequency scaling, idle loop, and monitoring the Rx queue for incoming packages. The latter is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on that address.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we employ one of the suggested power management schemes automatically, from within a Rx callback inside the PMD. Once there's traffic again, the empty poll counter is reset.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it with minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Things of note:
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
14:
- Fixed ARM/PPC builds
- Addressed various review comments
v13:
- Reworked the librte_power code to require less locking and handle invalid parameters better
- Fix numerous rebase errors present in v12
v12:
- Rebase on top of 21.02
- Rework of power intrinsics code
Anatoly Burakov (5):
eal: uninline power intrinsics
eal: avoid invalid API usage in power intrinsics
eal: change API of power intrinsics
eal: remove sync version of power monitor
eal: add monitor wakeup function
Liang Ma (6):
ethdev: add simple power management API
power: add PMD power management API and callback net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API examples/l3fwd-power: enable PMD power mgmt
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 15 + .../sample_app_ug/l3_forward_power_man.rst | 35 ++ drivers/event/dlb/dlb.c | 10 +- drivers/event/dlb2/dlb2.c | 10 +- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 ++ drivers/net/i40e/i40e_rxtx.h | 1 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++ drivers/net/ice/ice_rxtx.h | 1 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 ++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + examples/l3fwd-power/main.c | 89 ++++- .../arm/include/rte_power_intrinsics.h | 40 -- lib/librte_eal/arm/meson.build | 1 + lib/librte_eal/arm/rte_power_intrinsics.c | 34 ++ .../include/generic/rte_power_intrinsics.h | 78 ++-- .../ppc/include/rte_power_intrinsics.h | 40 -- lib/librte_eal/ppc/meson.build | 1 + lib/librte_eal/ppc/rte_power_intrinsics.c | 34 ++ lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ------ lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 184 +++++++++ lib/librte_ethdev/rte_ethdev.c | 28 ++ lib/librte_ethdev/rte_ethdev.h | 25 ++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++ lib/librte_ethdev/version.map | 3 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 360 ++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++ lib/librte_power/version.map | 5 + 34 files changed, 1097 insertions(+), 259 deletions(-) create mode 100644 lib/librte_eal/arm/rte_power_intrinsics.c create mode 100644 lib/librte_eal/ppc/rte_power_intrinsics.c create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
Signature · 1 line
2.25.1
PATCH v14 1/11 eal: uninline power intrinsics
Currently, power intrinsics are inline functions. Make them part of the ABI so that we can have various internal data associated with them without exposing said data to the outside world.
---
Notes:
v14:
- Fix compile issues on ARM and PPC64 by moving implementations to .c files
.../arm/include/rte_power_intrinsics.h | 40 ------ lib/librte_eal/arm/meson.build | 1 + lib/librte_eal/arm/rte_power_intrinsics.c | 42 ++++++ .../include/generic/rte_power_intrinsics.h | 6 +- .../ppc/include/rte_power_intrinsics.h | 40 ------ lib/librte_eal/ppc/meson.build | 1 + lib/librte_eal/ppc/rte_power_intrinsics.c | 42 ++++++ lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ----------------- lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 120 ++++++++++++++++++ 11 files changed, 215 insertions(+), 198 deletions(-) create mode 100644 lib/librte_eal/arm/rte_power_intrinsics.c create mode 100644 lib/librte_eal/ppc/rte_power_intrinsics.c create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c
lib/librte_eal/arm/include/rte_power_intrinsics.h +0 -40
@@ -13,46 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - #ifdef __cplusplus }#endif
lib/librte_eal/arm/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/arm/rte_power_intrinsics.c new file +42 -0
42 lines not on this page · raw message
lib/librte_eal/include/generic/rte_power_intrinsics.h +3 -3
@@ -52,7 +52,7 @@* to undefined result. */ __rte_experimental -static inline void rte_power_monitor(const volatile void *p, +void rte_power_monitor(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz);
@@ -97,7 +97,7 @@ static inline void rte_power_monitor(const volatile void *p,* wakes up. */ __rte_experimental -static inline void rte_power_monitor_sync(const volatile void *p, +void rte_power_monitor_sync(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck);
@@ -118,6 +118,6 @@ static inline void rte_power_monitor_sync(const volatile void *p,* architecture-dependent. */ __rte_experimental -static inline void rte_power_pause(const uint64_t tsc_timestamp); +void rte_power_pause(const uint64_t tsc_timestamp);#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/ppc/include/rte_power_intrinsics.h +0 -40
@@ -13,46 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - #ifdef __cplusplus }#endif
lib/librte_eal/ppc/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/ppc/rte_power_intrinsics.c new file +42 -0
42 lines not on this page · raw message
lib/librte_eal/version.map +5 -0
@@ -403,6 +403,11 @@ EXPERIMENTAL { rte_service_lcore_may_be_active; rte_vect_get_max_simd_bitwidth; rte_vect_set_max_simd_bitwidth; + + # added in 21.02 + rte_power_monitor; + rte_power_monitor_sync; + rte_power_pause; }; INTERNAL {
lib/librte_eal/x86/include/rte_power_intrinsics.h +0 -115
121 lines not on this page · raw message
lib/librte_eal/x86/meson.build +1 -0
@@ -8,4 +8,5 @@ sources += files( 'rte_cycles.c', 'rte_hypervisor.c', 'rte_spinlock.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/x86/rte_power_intrinsics.c new file +120 -0
120 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v14 2/11 eal: avoid invalid API usage in power intrinsics
In reply to Anatoly BurakovCurrently, the API documentation mandates that if the user wants to use the power management intrinsics, they need to call the rte_cpu_get_intrinsics_support API and check support for specific intrinsics.
However, if the user does not do that, it is possible to get illegal instruction error because we're using raw instruction opcodes, which may or may not be supported at runtime.
Now that we have everything in a C file, we can check for support at startup and prevent the user from possibly encountering illegal instruction errors.
---
Notes:
v14:
- Replace uint8_t with bool
.../include/generic/rte_power_intrinsics.h | 3 -- lib/librte_eal/x86/rte_power_intrinsics.c | 31 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-)
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -3
@@ -34,7 +34,6 @@* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -75,7 +74,6 @@ void rte_power_monitor(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -111,7 +109,6 @@ void rte_power_monitor_sync(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param tsc_timestamp * Maximum TSC timestamp to wait for. Note that the wait behavior is
lib/librte_eal/x86/rte_power_intrinsics.c +28 -3
@@ -4,6 +4,8 @@#include "rte_power_intrinsics.h" +static bool wait_supported; +static inline uint64_t __get_umwait_val (const volatile void *p,const uint8_t sz) {
@@ -35,6 +37,11 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -72,6 +79,11 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -112,9 +124,22 @@ rte_power_pause(const uint64_t tsc_timestamp)const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* execute TPAUSE */ asmvolatile (".byte 0x66, 0x0f, 0xae, 0xf7;" - :/* ignore rflags */ - :"D" (0 ),/* enter C0.2 */ -"a" (tsc_l),"d" (tsc_h)); + :/* ignore rflags */ + :"D" (0 ),/* enter C0.2 */ +"a" (tsc_l),"d" (tsc_h)); +} + +RTE_INIT (rte_power_intrinsics_init) { +struct rte_cpu_intrinsics i; + +rte_cpu_get_intrinsics_support (&i); + +if (i.power_monitor && i.power_pause) + wait_supported =1 ; }
Signature · 1 line
2.25.1
PATCH v14 3/11 eal: change API of power intrinsics
In reply to Anatoly BurakovInstead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
--- drivers/event/dlb/dlb.c | 10 ++-- drivers/event/dlb2/dlb2.c | 10 ++-- lib/librte_eal/arm/rte_power_intrinsics.c | 25 ++++------ .../include/generic/rte_power_intrinsics.h | 49 ++++++++----------- lib/librte_eal/ppc/rte_power_intrinsics.c | 25 ++++------ lib/librte_eal/x86/rte_power_intrinsics.c | 32 ++++++------ 6 files changed, 70 insertions(+), 81 deletions(-)
drivers/event/dlb/dlb.c +7 -3
@@ -3161,6 +3161,7 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,/* Interrupts not supported by PF PMD */ return 1 ; } else if (dlb->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -3181,9 +3182,12 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
drivers/event/dlb2/dlb2.c +7 -3
@@ -2870,6 +2870,7 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,if (elapsed_ticks >= timeout) {return 1 ; }else if (dlb2->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb2_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -2890,9 +2891,12 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB2_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
lib/librte_eal/arm/rte_power_intrinsics.c +10 -15
@@ -7,36 +7,31 @@/** * This function is not supported on ARM. */ -void rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +void +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on ARM. */ -void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +void +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/** * This function is not supported on ARM. */ -void rte_power_pause (const uint64_t tsc_timestamp) +void +rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); }
lib/librte_eal/include/generic/rte_power_intrinsics.h +20 -29
81 lines not on this page · raw message
lib/librte_eal/ppc/rte_power_intrinsics.c +10 -15
@@ -7,36 +7,31 @@/** * This function is not supported on PPC64. */ -void rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +void +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on PPC64. */ -void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +void +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/** * This function is not supported on PPC64. */ -void rte_power_pause (const uint64_t tsc_timestamp) +void +rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); }
lib/librte_eal/x86/rte_power_intrinsics.c +16 -16
@@ -31,9 +31,8 @@ __get_umwait_val(const volatile void *p, const uint8_t sz)* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -50,14 +49,15 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }/* execute UMWAIT */
@@ -73,9 +73,8 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -92,14 +91,15 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }rte_spinlock_unlock (lck);
Signature · 1 line
2.25.1
PATCH v14 4/11 eal: remove sync version of power monitor
In reply to Anatoly BurakovCurrently, the "sync" version of power monitor intrinsic is supposed to be used for purposes of waking up a sleeping core. However, there are better ways to achieve the same result, so remove the unneeded function.
--- lib/librte_eal/arm/rte_power_intrinsics.c | 12 ----- .../include/generic/rte_power_intrinsics.h | 34 -------------- lib/librte_eal/ppc/rte_power_intrinsics.c | 12 ----- lib/librte_eal/version.map | 1 - lib/librte_eal/x86/rte_power_intrinsics.c | 46 ------------------- 5 files changed, 105 deletions(-)
lib/librte_eal/arm/rte_power_intrinsics.c +0 -12
@@ -15,18 +15,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on ARM. */
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -34
@@ -57,40 +57,6 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); -/** -* @warning -* @b EXPERIMENTAL: this API may change without prior notice -* -* Monitor specific address for changes. This will cause the CPU to enter an -* architecture-defined optimized power state until either the specified -* memory address is written to, a certain TSC timestamp is reached, or other -* reasons cause the CPU to wake up. -* -* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If -* mask is non-zero, the current value pointed to by the `p` pointer will be -* checked against the expected value, and if they match, the entering of -* optimized power state may be aborted. -* -* This call will also lock a spinlock on entering sleep, and release it on -* waking up the CPU. -* -* @warning It is responsibility of the user to check if this function is -* supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* -* @param pmc -* The monitoring condition structure. -* @param tsc_timestamp -* Maximum TSC timestamp to wait for. Note that the wait behavior is -* architecture-dependent. -* @param lck -* A spinlock that must be locked before entering the function, will be -* unlocked while the CPU is sleeping, and will be locked again once the CPU -* wakes up. -*/ -__rte_experimental -void rte_power_monitor_sync(const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck); -/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/rte_power_intrinsics.c +0 -12
@@ -15,18 +15,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on PPC64. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on PPC64. */
lib/librte_eal/version.map +0 -1
@@ -406,7 +406,6 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; - rte_power_monitor_sync; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +0 -46
52 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v14 5/11 eal: add monitor wakeup function
In reply to Anatoly BurakovNow that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from rte_power_pause won't work.
---
Notes:
v13:
- Add comments around wakeup code to explain what it does
- Add lcore_id parameter checking to prevent buffer overrun
lib/librte_eal/arm/rte_power_intrinsics.c | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 ++++ lib/librte_eal/ppc/rte_power_intrinsics.c | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 85 +++++++++++++++++++ 5 files changed, 120 insertions(+)
lib/librte_eal/arm/rte_power_intrinsics.c +9 -0
@@ -23,3 +23,12 @@ rte_power_pause(const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); } + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
lib/librte_eal/include/generic/rte_power_intrinsics.h +16 -0
@@ -57,6 +57,22 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/rte_power_intrinsics.c +9 -0
@@ -23,3 +23,12 @@ rte_power_pause(const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); } + +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
lib/librte_eal/version.map +1 -0
@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; + rte_power_monitor_wakeup; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +85 -0
113 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v14 6/11 ethdev: add simple power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the Rx queues from the PMD, as well as release notes information.
---
Notes:
v13:
- Fix typos and issues raised by Andrew
doc/guides/rel_notes/release_21_02.rst | 5 +++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 83 insertions(+)
doc/guides/rel_notes/release_21_02.rst +5 -0
@@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added new API for PMD power management** + + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's. + Removed Items -------------
lib/librte_ethdev/rte_ethdev.c +28 -0
@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{ +struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_rx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid Rx queue_id=%u\n" , queue_id); +return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,
lib/librte_ethdev/rte_ethdev.h +25 -0
@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h"
@@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size)
lib/librte_ethdev/rte_ethdev_driver.h +22 -0
@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an Rx queue. +* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get power monitoring condition for Rx queue. */ };/**
lib/librte_ethdev/version.map +3 -0
@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Signature · 1 line
2.25.1
PATCH v14 7/11 power: add PMD power management API and callback
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.
---
Notes:
v13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Rework n_rx_queues access to not go through internal PMD structures and use public API instead
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 360 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 512 insertions(+), 2 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
doc/guides/prog_guide/power_man.rst +44 -0
@@ -192,6 +192,47 @@ User Cases ---------- The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA. +PMD Power Management API +------------------------ + +Abstract +~~~~~~~~ +Existing power management mechanisms require developers to change application +design or change code to make use of it. The PMD power management API provides a +convenient alternative by utilizing Ethernet PMD RX callbacks, and triggering +power saving whenever empty poll count reaches a certain number. + + * Monitor + + This power saving scheme will put the CPU into optimized power state and use + the ``rte_power_monitor()`` function to monitor the Ethernet PMD RX + descriptor address, and wake the CPU up whenever there's new traffic. + + * Pause + + This power saving scheme will avoid busy polling by either entering + power-optimized sleep state with ``rte_power_pause()`` function, or, if it's + not available, use ``rte_pause()``. + + * Frequency scaling + + This power saving scheme will use existing ``librte_power`` library + functionality to scale the core frequency up/down depending on traffic + volume. + + +.. note:: + + Currently, this power management API is limited to mandatory mapping of 1 + queue to 1 core (multiple queues are supported, but they must be polled from + different cores). + +API Overview for PMD Power Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* **Queue Enable**: Enable specific power scheme for certain queue/port/core + +* **Queue Disable**: Disable power scheme for certain queue/port/core + References ----------
@@ -200,3 +241,6 @@ References * The :doc:`../sample_app_ug/vm_power_management` chapter in the :doc:`../sample_app_ug/index` section. + +* The :doc:`../sample_app_ug/rxtx_callbacks` + chapter in the :doc:`../sample_app_ug/index` section.
doc/guides/rel_notes/release_21_02.rst +10 -0
@@ -60,6 +60,16 @@ New Features * ``rte_eth_get_monitor_addr()``, to be used in conjunction with ``rte_power_monitor()`` to enable automatic power management for PMD's. +* **Add PMD power management helper API** + + A new helper API has been added to make using Ethernet PMD power management + easier for the user: ``rte_power_pmd_mgmt_queue_enable()``. Three power + management schemes are supported initially: + + * Power saving based on UMWAIT instruction (x86 only) + * Power saving based on ``rte_pause()`` (generic) or TPAUSE instruction (x86 only) + * Power saving based on frequency scaling through the ``librte_power`` library + Removed Items -------------
lib/librte_power/meson.build +3 -2
@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c', 'power_kvm_vm.c', 'guest_channel.c', 'rte_power_empty_poll.c', 'power_pstate_cpufreq.c', + 'rte_power_pmd_mgmt.c', 'power_common.c') -headers = files('rte_power.h','rte_power_empty_poll.h') -deps += ['timer'] +headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h') +deps += ['timer' ,'ethdev']
lib/librte_power/rte_power_pmd_mgmt.c new file +360 -0
360 lines not on this page · raw message
lib/librte_power/rte_power_pmd_mgmt.h new file +90 -0
90 lines not on this page · raw message
lib/librte_power/version.map +5 -0
@@ -34,4 +34,9 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + + # added in 21.02 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + };
Signature · 1 line
2.25.1
PATCH v14 8/11 net/ixgbe: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/ixgbe/ixgbe_ethdev.c +1 -0
@@ -560,6 +560,7 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .udp_tunnel_port_del = ixgbe_dev_udp_tunnel_port_del, .tm_ops_get = ixgbe_tm_ops_get, .tx_done_cleanup = ixgbe_dev_tx_done_cleanup, + .get_monitor_addr = ixgbe_get_monitor_addr, };/*
drivers/net/ixgbe/ixgbe_rxtx.c +25 -0
@@ -1369,6 +1369,31 @@ const uint32_t RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP, }; +int +ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ixgbe_adv_rx_desc *rxdp; +struct ixgbe_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.upper.status_error; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + pmc->mask =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + +/* the registers are 32-bit */ + pmc->data_sz =sizeof (uint32_t ); + +return 0 ; +} +/* @note: fix ixgbe_dev_supported_ptypes_get() if any change here. */ static inline uint32_t ixgbe_rxd_pkt_info_to_pkt_type (uint32_t pkt_info,uint16_t ptype_mask)
drivers/net/ixgbe/ixgbe_rxtx.h +1 -0
@@ -299,5 +299,6 @@ uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_queue_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_port_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_tx_queue_offloads (struct rte_eth_dev *dev); +int ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#endif /* _IXGBE_RXTX_H_ */
Signature · 1 line
2.25.1
PATCH v14 9/11 net/i40e: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/i40e/i40e_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/i40e/i40e_ethdev.c +1 -0
@@ -510,6 +510,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = { .mtu_set = i40e_dev_mtu_set, .tm_ops_get = i40e_tm_ops_get, .tx_done_cleanup = i40e_tx_done_cleanup, + .get_monitor_addr = i40e_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/i40e/i40e_rxtx.c +25 -0
@@ -72,6 +72,31 @@#define I40E_TX_OFFLOAD_NOTSUP_MASK \ (PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_MASK) +int +i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +struct i40e_rx_queue *rxq = rx_queue; +volatile union i40e_rx_desc *rxdp; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.qword1.status_error_len; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + pmc->mask =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + +/* registers are 64-bit */ + pmc->data_sz =sizeof (uint64_t ); + +return 0 ; +} +static inline void i40e_rxd_to_vlan_tci (struct rte_mbuf *mb,volatile union i40e_rx_desc *rxdp) {
drivers/net/i40e/i40e_rxtx.h +1 -0
@@ -248,6 +248,7 @@ uint16_t i40e_recv_scattered_pkts_vec_avx2(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);uint16_t i40e_xmit_pkts_vec_avx2 (void *tx_queue,struct rte_mbuf **tx_pkts,uint16_t nb_pkts); +int i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);/* For each value it means, datasheet of hardware can tell more details *
Signature · 1 line
2.25.1
PATCH v14 10/11 net/ice: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/ice/ice_rxtx.h | 1 + 3 files changed, 28 insertions(+)
drivers/net/ice/ice_ethdev.c +1 -0
@@ -216,6 +216,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .udp_tunnel_port_add = ice_dev_udp_tunnel_port_add, .udp_tunnel_port_del = ice_dev_udp_tunnel_port_del, .tx_done_cleanup = ice_tx_done_cleanup, + .get_monitor_addr = ice_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/ice/ice_rxtx.c +26 -0
@@ -26,6 +26,32 @@ uint64_t rte_net_ice_dynflag_proto_xtr_ipv6_flow_mask;uint64_t rte_net_ice_dynflag_proto_xtr_tcp_mask;uint64_t rte_net_ice_dynflag_proto_xtr_ip_offset_mask; +int +ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ice_rx_flex_desc *rxdp; +struct ice_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.status_error0; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + pmc->mask =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + +/* register is 16-bit */ + pmc->data_sz =sizeof (uint16_t ); + +return 0 ; +} + +static inline uint8_t ice_proto_xtr_type_to_rxdid (uint8_t xtr_type) {
drivers/net/ice/ice_rxtx.h +1 -0
@@ -263,6 +263,7 @@ uint16_t ice_xmit_pkts_vec_avx512(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);int ice_fdir_programming (struct ice_pf *pf,struct ice_fltr_desc *fdir_desc);int ice_tx_done_cleanup (void *txq,uint32_t free_cnt); +int ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#define FDIR_PARSING_ENABLE_PER_QUEUE (ad, on) do { \ int i; \
Signature · 1 line
2.25.1
PATCH v14 11/11 examples/l3fwd-power: enable PMD power mgmt
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add PMD power management feature support to l3fwd-power sample app.
---
Notes:
v12:
- Allow selecting PMD power management scheme from command-line
- Enforce 1 core 1 queue rule
.../sample_app_ug/l3_forward_power_man.rst | 35 ++++++++ examples/l3fwd-power/main.c | 89 ++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-)
doc/guides/sample_app_ug/l3_forward_power_man.rst +35 -0
@@ -109,6 +109,8 @@ where, * --telemetry: Telemetry mode. +* --pmd-mgmt: PMD power management mode. + See :doc:`l3_forward` for details. The L3fwd-power example reuses the L3fwd command line options.
@@ -456,3 +458,36 @@ reference cycles and accordingly busy rate is set to either 0% or The new stats ``empty_poll`` , ``full_poll`` and ``busy_percent`` can be viewed by running the script ``/usertools/dpdk-telemetry-client.py`` and selecting the menu option ``Send for global Metrics``. + +PMD power management Mode +------------------------- + +The PMD power management mode support for ``l3fwd-power`` is a standalone mode, in this mode +``l3fwd-power`` does simple l3fwding along with enable the power saving scheme on specific +port/queue/lcore. Main purpose for this mode is to demonstrate how to use the PMD power management API. + +.. code-block:: console + + ./build/examples/dpdk-l3fwd-power -l 1-3 -- --pmd-mgmt -p 0x0f --config="(0,0,2),(0,1,3)" + +PMD Power Management Mode +------------------------- +There is also a traffic-aware operating mode that, instead of using explicit +power management, will use automatic PMD power management. This mode is limited +to one queue per core, and has three available power management schemes: + +* ``monitor`` - this will use ``rte_power_monitor()`` function to enter a + power-optimized state (subject to platform support). + +* ``pause`` - this will use ``rte_power_pause()`` or ``rte_pause()`` to avoid + busy looping when there is no traffic. + +* ``scale`` - this will use frequency scaling routines available in the + ``librte_power`` library. + +See :doc:`Power Management<../prog_guide/power_man>` chapter in the DPDK +Programmer's Guide for more details on PMD power management. + +.. code-block:: console + + ./<build_dir>/examples/dpdk-l3fwd-power -l 1-3 -- -p 0x0f --config="(0,0,2),(0,1,3)" --pmd-mgmt=scale
examples/l3fwd-power/main.c +87 -2
165 lines not on this page · raw message
Signature · 1 line
2.25.1
v15 cover Add PMD power management
In reply to Anatoly BurakovThis patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. There are multiple proposed mechanisms to achieve said power
savings: simple frequency scaling, idle loop, and monitoring the Rx queue for incoming packages. The latter is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on that address.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we employ one of the suggested power management schemes automatically, from within a Rx callback inside the PMD. Once there's traffic again, the empty poll counter is reset.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it with minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Things of note:
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
v15:
- Fixed incorrect check in UMWAIT callback
- Fixed accidental whitespace changes
v14:
- Fixed ARM/PPC builds
- Addressed various review comments
v13:
- Reworked the librte_power code to require less locking and handle invalid parameters better
- Fix numerous rebase errors present in v12
v12:
- Rebase on top of 21.02
- Rework of power intrinsics code
Anatoly Burakov (5):
eal: uninline power intrinsics
eal: avoid invalid API usage in power intrinsics
eal: change API of power intrinsics
eal: remove sync version of power monitor
eal: add monitor wakeup function
Liang Ma (6):
ethdev: add simple power management API
power: add PMD power management API and callback net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API examples/l3fwd-power: enable PMD power mgmt
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 15 + .../sample_app_ug/l3_forward_power_man.rst | 35 ++ drivers/event/dlb/dlb.c | 10 +- drivers/event/dlb2/dlb2.c | 10 +- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 ++ drivers/net/i40e/i40e_rxtx.h | 1 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++ drivers/net/ice/ice_rxtx.h | 1 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 ++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + examples/l3fwd-power/main.c | 89 ++++- .../arm/include/rte_power_intrinsics.h | 40 -- lib/librte_eal/arm/meson.build | 1 + lib/librte_eal/arm/rte_power_intrinsics.c | 34 ++ .../include/generic/rte_power_intrinsics.h | 78 ++-- .../ppc/include/rte_power_intrinsics.h | 40 -- lib/librte_eal/ppc/meson.build | 1 + lib/librte_eal/ppc/rte_power_intrinsics.c | 34 ++ lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ------ lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 184 +++++++++ lib/librte_ethdev/rte_ethdev.c | 28 ++ lib/librte_ethdev/rte_ethdev.h | 25 ++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++ lib/librte_ethdev/version.map | 3 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 ++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++ lib/librte_power/version.map | 5 + 34 files changed, 1096 insertions(+), 259 deletions(-) create mode 100644 lib/librte_eal/arm/rte_power_intrinsics.c create mode 100644 lib/librte_eal/ppc/rte_power_intrinsics.c create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
Signature · 1 line
2.25.1
PATCH v15 1/11 eal: uninline power intrinsics
Currently, power intrinsics are inline functions. Make them part of the ABI so that we can have various internal data associated with them without exposing said data to the outside world.
---
Notes:
v14:
- Fix compile issues on ARM and PPC64 by moving implementations to .c files
.../arm/include/rte_power_intrinsics.h | 40 ------ lib/librte_eal/arm/meson.build | 1 + lib/librte_eal/arm/rte_power_intrinsics.c | 42 ++++++ .../include/generic/rte_power_intrinsics.h | 6 +- .../ppc/include/rte_power_intrinsics.h | 40 ------ lib/librte_eal/ppc/meson.build | 1 + lib/librte_eal/ppc/rte_power_intrinsics.c | 42 ++++++ lib/librte_eal/version.map | 5 + .../x86/include/rte_power_intrinsics.h | 115 ----------------- lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 120 ++++++++++++++++++ 11 files changed, 215 insertions(+), 198 deletions(-) create mode 100644 lib/librte_eal/arm/rte_power_intrinsics.c create mode 100644 lib/librte_eal/ppc/rte_power_intrinsics.c create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c
lib/librte_eal/arm/include/rte_power_intrinsics.h +0 -40
@@ -13,46 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - #ifdef __cplusplus }#endif
lib/librte_eal/arm/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/arm/rte_power_intrinsics.c new file +42 -0
42 lines not on this page · raw message
lib/librte_eal/include/generic/rte_power_intrinsics.h +3 -3
@@ -52,7 +52,7 @@* to undefined result. */ __rte_experimental -static inline void rte_power_monitor(const volatile void *p, +void rte_power_monitor(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz);
@@ -97,7 +97,7 @@ static inline void rte_power_monitor(const volatile void *p,* wakes up. */ __rte_experimental -static inline void rte_power_monitor_sync(const volatile void *p, +void rte_power_monitor_sync(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck);
@@ -118,6 +118,6 @@ static inline void rte_power_monitor_sync(const volatile void *p,* architecture-dependent. */ __rte_experimental -static inline void rte_power_pause(const uint64_t tsc_timestamp); +void rte_power_pause(const uint64_t tsc_timestamp);#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/ppc/include/rte_power_intrinsics.h +0 -40
@@ -13,46 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - #ifdef __cplusplus }#endif
lib/librte_eal/ppc/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/ppc/rte_power_intrinsics.c new file +42 -0
42 lines not on this page · raw message
lib/librte_eal/version.map +5 -0
@@ -403,6 +403,11 @@ EXPERIMENTAL { rte_service_lcore_may_be_active; rte_vect_get_max_simd_bitwidth; rte_vect_set_max_simd_bitwidth; + + # added in 21.02 + rte_power_monitor; + rte_power_monitor_sync; + rte_power_pause; }; INTERNAL {
lib/librte_eal/x86/include/rte_power_intrinsics.h +0 -115
121 lines not on this page · raw message
lib/librte_eal/x86/meson.build +1 -0
@@ -8,4 +8,5 @@ sources += files( 'rte_cycles.c', 'rte_hypervisor.c', 'rte_spinlock.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/x86/rte_power_intrinsics.c new file +120 -0
120 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v15 01/11] eal: uninline power intrinsics
Quoted 19 lines
lib/librte_eal/x86/rte_power_intrinsics.c new file +12
-0@@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2020 Intel Corporation +*/ + +#include "rte_power_intrinsics.h" + +static inline uint64_t +__get_umwait_val (const volatile void *p,const uint8_t sz) +{ +switch (sz) { +case sizeof (uint8_t ): +return *(const volatile uint8_t *)p;
lib/librte_eal/x86/rte_power_intrinsics.c line 22
Quoted 10 lines
lib/librte_eal/x86/rte_power_intrinsics.c new file +10
-0+case sizeof (uint16_t ): +return *(const volatile uint16_t *)p; +case sizeof (uint32_t ): +return *(const volatile uint32_t *)p; +case sizeof (uint64_t ): +return *(const volatile uint64_t *)p; +default : +/* this is an intrinsic, so we can't have any error handling */ +RTE_ASSERT (0 ); +return 0 ;
Nearly forgot - as now this function is not inline anymore, we can probably get rid of assert and return some error code instead?
Quoted 3 lines
lib/librte_eal/x86/rte_power_intrinsics.c new file +3
-0+ } +} +
Re: [dpdk-dev] [PATCH v15 01/11] eal: uninline power intrinsics
Quoted 24 lines · Konstantin
Quoted 24 lines
lib/librte_eal/x86/rte_power_intrinsics.c new file +17
-0@@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2020 Intel Corporation +*/ + +#include "rte_power_intrinsics.h" + +static inline uint64_t +__get_umwait_val (const volatile void *p,const uint8_t sz) +{ +switch (sz) { +case sizeof (uint8_t ): +return *(const volatile uint8_t *)p; +case sizeof (uint16_t ): +return *(const volatile uint16_t *)p; +case sizeof (uint32_t ): +return *(const volatile uint32_t *)p; +case sizeof (uint64_t ):
Quoted 9 lines
lib/librte_eal/x86/rte_power_intrinsics.c line 22
Quoted 5 lines
lib/librte_eal/x86/rte_power_intrinsics.c new file +5
-0+return *(const volatile uint64_t *)p; +default : +/* this is an intrinsic, so we can't have any error handling */ +RTE_ASSERT (0 ); +return 0 ;Nearly forgot - as now this function is not inline anymore, we can probably get rid of assert and return some error code instead?
Well, this would necessitate a change of API to include return values. Which i think is OK at this point, because it's a fully fledged API (rather than an intrinsic) at this point anyway.
Quoted 3 lines
Quoted 3 lines
lib/librte_eal/x86/rte_power_intrinsics.c new file +3
-0+} +} +
Signature · 2 lines
Thanks, Anatoly
PATCH v15 2/11 eal: avoid invalid API usage in power intrinsics
In reply to Anatoly BurakovCurrently, the API documentation mandates that if the user wants to use the power management intrinsics, they need to call the rte_cpu_get_intrinsics_support API and check support for specific intrinsics.
However, if the user does not do that, it is possible to get illegal instruction error because we're using raw instruction opcodes, which may or may not be supported at runtime.
Now that we have everything in a C file, we can check for support at startup and prevent the user from possibly encountering illegal instruction errors.
---
Notes:
v15:
- Remove accidental whitespace changes
v14:
- Replace uint8_t with bool
v14:
- Replace uint8_t with bool
.../include/generic/rte_power_intrinsics.h | 3 --- lib/librte_eal/x86/rte_power_intrinsics.c | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-)
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -3
@@ -34,7 +34,6 @@* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -75,7 +74,6 @@ void rte_power_monitor(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -111,7 +109,6 @@ void rte_power_monitor_sync(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param tsc_timestamp * Maximum TSC timestamp to wait for. Note that the wait behavior is
lib/librte_eal/x86/rte_power_intrinsics.c +25 -0
@@ -4,6 +4,8 @@#include "rte_power_intrinsics.h" +static bool wait_supported; +static inline uint64_t __get_umwait_val (const volatile void *p,const uint8_t sz) {
@@ -35,6 +37,11 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -72,6 +79,11 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value, {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); + +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* * we're using raw byte codes for now as only the newest compiler * versions support this instruction natively.
@@ -112,9 +124,22 @@ rte_power_pause(const uint64_t tsc_timestamp)const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 ); +/* prevent user from running this instruction if it's not supported */ +if (!wait_supported) +return ; +/* execute TPAUSE */ asmvolatile (".byte 0x66, 0x0f, 0xae, 0xf7;" :/* ignore rflags */ :"D" (0 ),/* enter C0.2 */ "a" (tsc_l),"d" (tsc_h)); } + +RTE_INIT (rte_power_intrinsics_init) { +struct rte_cpu_intrinsics i; + +rte_cpu_get_intrinsics_support (&i); + +if (i.power_monitor && i.power_pause) + wait_supported =1 ; +}
Signature · 1 line
2.25.1
PATCH v15 3/11 eal: change API of power intrinsics
In reply to Anatoly BurakovInstead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
--- drivers/event/dlb/dlb.c | 10 ++-- drivers/event/dlb2/dlb2.c | 10 ++-- lib/librte_eal/arm/rte_power_intrinsics.c | 25 ++++------ .../include/generic/rte_power_intrinsics.h | 49 ++++++++----------- lib/librte_eal/ppc/rte_power_intrinsics.c | 25 ++++------ lib/librte_eal/x86/rte_power_intrinsics.c | 32 ++++++------ 6 files changed, 70 insertions(+), 81 deletions(-)
drivers/event/dlb/dlb.c +7 -3
@@ -3161,6 +3161,7 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,/* Interrupts not supported by PF PMD */ return 1 ; } else if (dlb->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -3181,9 +3182,12 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
drivers/event/dlb2/dlb2.c +7 -3
@@ -2870,6 +2870,7 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,if (elapsed_ticks >= timeout) {return 1 ; }else if (dlb2->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb2_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -2890,9 +2891,12 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB2_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
lib/librte_eal/arm/rte_power_intrinsics.c +10 -15
@@ -7,36 +7,31 @@/** * This function is not supported on ARM. */ -void rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +void +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on ARM. */ -void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +void +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/** * This function is not supported on ARM. */ -void rte_power_pause (const uint64_t tsc_timestamp) +void +rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); }
lib/librte_eal/include/generic/rte_power_intrinsics.h +20 -29
81 lines not on this page · raw message
lib/librte_eal/ppc/rte_power_intrinsics.c +10 -15
@@ -7,36 +7,31 @@/** * This function is not supported on PPC64. */ -void rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +void +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); }/** * This function is not supported on PPC64. */ -void rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +void +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz); }/** * This function is not supported on PPC64. */ -void rte_power_pause (const uint64_t tsc_timestamp) +void +rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); }
lib/librte_eal/x86/rte_power_intrinsics.c +16 -16
@@ -31,9 +31,8 @@ __get_umwait_val(const volatile void *p, const uint8_t sz)* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -50,14 +49,15 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }/* execute UMWAIT */
@@ -73,9 +73,8 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,* Intel(R) 64 and IA-32 Architectures Software Developer's Manual. */ void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) {const uint32_t tsc_l = (uint32_t )tsc_timestamp;const uint32_t tsc_h = (uint32_t )(tsc_timestamp >>32 );
@@ -92,14 +91,15 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,/* set address for UMONITOR */ asmvolatile (".byte 0xf3, 0x0f, 0xae, 0xf7;" : - :"D" (p)); + :"D" (pmc->addr)); -if (value_mask) { -const uint64_t cur_value =__get_umwait_val (p, data_sz); -const uint64_t masked = cur_value & value_mask; +if (pmc->mask) { +const uint64_t cur_value =__get_umwait_val ( + pmc->addr, pmc->data_sz); +const uint64_t masked = cur_value & pmc->mask;/* if the masked value is already matching, abort */ -if (masked == expected_value) +if (masked == pmc->val)return ; }rte_spinlock_unlock (lck);
Signature · 1 line
2.25.1
PATCH v15 4/11 eal: remove sync version of power monitor
In reply to Anatoly BurakovCurrently, the "sync" version of power monitor intrinsic is supposed to be used for purposes of waking up a sleeping core. However, there are better ways to achieve the same result, so remove the unneeded function.
--- lib/librte_eal/arm/rte_power_intrinsics.c | 12 ----- .../include/generic/rte_power_intrinsics.h | 34 -------------- lib/librte_eal/ppc/rte_power_intrinsics.c | 12 ----- lib/librte_eal/version.map | 1 - lib/librte_eal/x86/rte_power_intrinsics.c | 46 ------------------- 5 files changed, 105 deletions(-)
lib/librte_eal/arm/rte_power_intrinsics.c +0 -12
@@ -15,18 +15,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on ARM. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on ARM. */
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -34
@@ -57,40 +57,6 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); -/** -* @warning -* @b EXPERIMENTAL: this API may change without prior notice -* -* Monitor specific address for changes. This will cause the CPU to enter an -* architecture-defined optimized power state until either the specified -* memory address is written to, a certain TSC timestamp is reached, or other -* reasons cause the CPU to wake up. -* -* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If -* mask is non-zero, the current value pointed to by the `p` pointer will be -* checked against the expected value, and if they match, the entering of -* optimized power state may be aborted. -* -* This call will also lock a spinlock on entering sleep, and release it on -* waking up the CPU. -* -* @warning It is responsibility of the user to check if this function is -* supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* -* @param pmc -* The monitoring condition structure. -* @param tsc_timestamp -* Maximum TSC timestamp to wait for. Note that the wait behavior is -* architecture-dependent. -* @param lck -* A spinlock that must be locked before entering the function, will be -* unlocked while the CPU is sleeping, and will be locked again once the CPU -* wakes up. -*/ -__rte_experimental -void rte_power_monitor_sync(const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck); -/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/rte_power_intrinsics.c +0 -12
@@ -15,18 +15,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,RTE_SET_USED (tsc_timestamp); } -/** -* This function is not supported on PPC64. -*/ -void -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -} -/** * This function is not supported on PPC64. */
lib/librte_eal/version.map +0 -1
@@ -406,7 +406,6 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; - rte_power_monitor_sync; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +0 -46
52 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v15 5/11 eal: add monitor wakeup function
In reply to Anatoly BurakovNow that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from rte_power_pause won't work.
---
Notes:
v13:
- Add comments around wakeup code to explain what it does
- Add lcore_id parameter checking to prevent buffer overrun
lib/librte_eal/arm/rte_power_intrinsics.c | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 ++++ lib/librte_eal/ppc/rte_power_intrinsics.c | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 85 +++++++++++++++++++ 5 files changed, 120 insertions(+)
lib/librte_eal/arm/rte_power_intrinsics.c +9 -0
@@ -23,3 +23,12 @@ rte_power_pause(const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); } + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
lib/librte_eal/include/generic/rte_power_intrinsics.h +16 -0
@@ -57,6 +57,22 @@ __rte_experimentalvoid rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +void rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/rte_power_intrinsics.c +9 -0
@@ -23,3 +23,12 @@ rte_power_pause(const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); } + +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
lib/librte_eal/version.map +1 -0
@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; + rte_power_monitor_wakeup; rte_power_pause; };
lib/librte_eal/x86/rte_power_intrinsics.c +85 -0
113 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v15 6/11 ethdev: add simple power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the Rx queues from the PMD, as well as release notes information.
---
Notes:
v13:
- Fix typos and issues raised by Andrew
doc/guides/rel_notes/release_21_02.rst | 5 +++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 83 insertions(+)
doc/guides/rel_notes/release_21_02.rst +5 -0
@@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added new API for PMD power management** + + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's. + Removed Items -------------
lib/librte_ethdev/rte_ethdev.c +28 -0
@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{ +struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_rx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid Rx queue_id=%u\n" , queue_id); +return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,
lib/librte_ethdev/rte_ethdev.h +25 -0
@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h"
@@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size)
lib/librte_ethdev/rte_ethdev_driver.h +22 -0
@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an Rx queue. +* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get power monitoring condition for Rx queue. */ };/**
lib/librte_ethdev/version.map +3 -0
@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Signature · 1 line
2.25.1
PATCH v15 7/11 power: add PMD power management API and callback
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.
---
Notes:
v15:
- Fix check in UMWAIT callback
v13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Rework n_rx_queues access to not go through internal PMD structures and use public API instead
v13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Rework n_rx_queues access to not go through internal PMD structures and use public API instead
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
doc/guides/prog_guide/power_man.rst +44 -0
@@ -192,6 +192,47 @@ User Cases ---------- The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA. +PMD Power Management API +------------------------ + +Abstract +~~~~~~~~ +Existing power management mechanisms require developers to change application +design or change code to make use of it. The PMD power management API provides a +convenient alternative by utilizing Ethernet PMD RX callbacks, and triggering +power saving whenever empty poll count reaches a certain number. + + * Monitor + + This power saving scheme will put the CPU into optimized power state and use + the ``rte_power_monitor()`` function to monitor the Ethernet PMD RX + descriptor address, and wake the CPU up whenever there's new traffic. + + * Pause + + This power saving scheme will avoid busy polling by either entering + power-optimized sleep state with ``rte_power_pause()`` function, or, if it's + not available, use ``rte_pause()``. + + * Frequency scaling + + This power saving scheme will use existing ``librte_power`` library + functionality to scale the core frequency up/down depending on traffic + volume. + + +.. note:: + + Currently, this power management API is limited to mandatory mapping of 1 + queue to 1 core (multiple queues are supported, but they must be polled from + different cores). + +API Overview for PMD Power Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* **Queue Enable**: Enable specific power scheme for certain queue/port/core + +* **Queue Disable**: Disable power scheme for certain queue/port/core + References ----------
@@ -200,3 +241,6 @@ References * The :doc:`../sample_app_ug/vm_power_management` chapter in the :doc:`../sample_app_ug/index` section. + +* The :doc:`../sample_app_ug/rxtx_callbacks` + chapter in the :doc:`../sample_app_ug/index` section.
doc/guides/rel_notes/release_21_02.rst +10 -0
@@ -60,6 +60,16 @@ New Features * ``rte_eth_get_monitor_addr()``, to be used in conjunction with ``rte_power_monitor()`` to enable automatic power management for PMD's. +* **Add PMD power management helper API** + + A new helper API has been added to make using Ethernet PMD power management + easier for the user: ``rte_power_pmd_mgmt_queue_enable()``. Three power + management schemes are supported initially: + + * Power saving based on UMWAIT instruction (x86 only) + * Power saving based on ``rte_pause()`` (generic) or TPAUSE instruction (x86 only) + * Power saving based on frequency scaling through the ``librte_power`` library + Removed Items -------------
lib/librte_power/meson.build +3 -2
@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c', 'power_kvm_vm.c', 'guest_channel.c', 'rte_power_empty_poll.c', 'power_pstate_cpufreq.c', + 'rte_power_pmd_mgmt.c', 'power_common.c') -headers = files('rte_power.h','rte_power_empty_poll.h') -deps += ['timer'] +headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h') +deps += ['timer' ,'ethdev']
lib/librte_power/rte_power_pmd_mgmt.c new file +359 -0
359 lines not on this page · raw message
lib/librte_power/rte_power_pmd_mgmt.h new file +90 -0
90 lines not on this page · raw message
lib/librte_power/version.map +5 -0
@@ -34,4 +34,9 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + + # added in 21.02 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + };
Signature · 1 line
2.25.1
PATCH v15 8/11 net/ixgbe: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/ixgbe/ixgbe_ethdev.c +1 -0
@@ -560,6 +560,7 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .udp_tunnel_port_del = ixgbe_dev_udp_tunnel_port_del, .tm_ops_get = ixgbe_tm_ops_get, .tx_done_cleanup = ixgbe_dev_tx_done_cleanup, + .get_monitor_addr = ixgbe_get_monitor_addr, };/*
drivers/net/ixgbe/ixgbe_rxtx.c +25 -0
@@ -1369,6 +1369,31 @@ const uint32_t RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP, }; +int +ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ixgbe_adv_rx_desc *rxdp; +struct ixgbe_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.upper.status_error; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + pmc->mask =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + +/* the registers are 32-bit */ + pmc->data_sz =sizeof (uint32_t ); + +return 0 ; +} +/* @note: fix ixgbe_dev_supported_ptypes_get() if any change here. */ static inline uint32_t ixgbe_rxd_pkt_info_to_pkt_type (uint32_t pkt_info,uint16_t ptype_mask)
drivers/net/ixgbe/ixgbe_rxtx.h +1 -0
@@ -299,5 +299,6 @@ uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_queue_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_port_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_tx_queue_offloads (struct rte_eth_dev *dev); +int ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#endif /* _IXGBE_RXTX_H_ */
Signature · 1 line
2.25.1
PATCH v15 9/11 net/i40e: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/i40e/i40e_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/i40e/i40e_ethdev.c +1 -0
@@ -510,6 +510,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = { .mtu_set = i40e_dev_mtu_set, .tm_ops_get = i40e_tm_ops_get, .tx_done_cleanup = i40e_tx_done_cleanup, + .get_monitor_addr = i40e_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/i40e/i40e_rxtx.c +25 -0
@@ -72,6 +72,31 @@#define I40E_TX_OFFLOAD_NOTSUP_MASK \ (PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_MASK) +int +i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +struct i40e_rx_queue *rxq = rx_queue; +volatile union i40e_rx_desc *rxdp; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.qword1.status_error_len; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + pmc->mask =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + +/* registers are 64-bit */ + pmc->data_sz =sizeof (uint64_t ); + +return 0 ; +} +static inline void i40e_rxd_to_vlan_tci (struct rte_mbuf *mb,volatile union i40e_rx_desc *rxdp) {
drivers/net/i40e/i40e_rxtx.h +1 -0
@@ -248,6 +248,7 @@ uint16_t i40e_recv_scattered_pkts_vec_avx2(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);uint16_t i40e_xmit_pkts_vec_avx2 (void *tx_queue,struct rte_mbuf **tx_pkts,uint16_t nb_pkts); +int i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);/* For each value it means, datasheet of hardware can tell more details *
Signature · 1 line
2.25.1
PATCH v15 10/11 net/ice: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/ice/ice_rxtx.h | 1 + 3 files changed, 28 insertions(+)
drivers/net/ice/ice_ethdev.c +1 -0
@@ -216,6 +216,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .udp_tunnel_port_add = ice_dev_udp_tunnel_port_add, .udp_tunnel_port_del = ice_dev_udp_tunnel_port_del, .tx_done_cleanup = ice_tx_done_cleanup, + .get_monitor_addr = ice_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/ice/ice_rxtx.c +26 -0
@@ -26,6 +26,32 @@ uint64_t rte_net_ice_dynflag_proto_xtr_ipv6_flow_mask;uint64_t rte_net_ice_dynflag_proto_xtr_tcp_mask;uint64_t rte_net_ice_dynflag_proto_xtr_ip_offset_mask; +int +ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ice_rx_flex_desc *rxdp; +struct ice_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.status_error0; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + pmc->mask =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + +/* register is 16-bit */ + pmc->data_sz =sizeof (uint16_t ); + +return 0 ; +} + +static inline uint8_t ice_proto_xtr_type_to_rxdid (uint8_t xtr_type) {
drivers/net/ice/ice_rxtx.h +1 -0
@@ -263,6 +263,7 @@ uint16_t ice_xmit_pkts_vec_avx512(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);int ice_fdir_programming (struct ice_pf *pf,struct ice_fltr_desc *fdir_desc);int ice_tx_done_cleanup (void *txq,uint32_t free_cnt); +int ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#define FDIR_PARSING_ENABLE_PER_QUEUE (ad, on) do { \ int i; \
Signature · 1 line
2.25.1
PATCH v15 11/11 examples/l3fwd-power: enable PMD power mgmt
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add PMD power management feature support to l3fwd-power sample app.
---
Notes:
v12:
- Allow selecting PMD power management scheme from command-line
- Enforce 1 core 1 queue rule
.../sample_app_ug/l3_forward_power_man.rst | 35 ++++++++ examples/l3fwd-power/main.c | 89 ++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-)
doc/guides/sample_app_ug/l3_forward_power_man.rst +35 -0
@@ -109,6 +109,8 @@ where, * --telemetry: Telemetry mode. +* --pmd-mgmt: PMD power management mode. + See :doc:`l3_forward` for details. The L3fwd-power example reuses the L3fwd command line options.
@@ -456,3 +458,36 @@ reference cycles and accordingly busy rate is set to either 0% or The new stats ``empty_poll`` , ``full_poll`` and ``busy_percent`` can be viewed by running the script ``/usertools/dpdk-telemetry-client.py`` and selecting the menu option ``Send for global Metrics``. + +PMD power management Mode +------------------------- + +The PMD power management mode support for ``l3fwd-power`` is a standalone mode, in this mode +``l3fwd-power`` does simple l3fwding along with enable the power saving scheme on specific +port/queue/lcore. Main purpose for this mode is to demonstrate how to use the PMD power management API. + +.. code-block:: console + + ./build/examples/dpdk-l3fwd-power -l 1-3 -- --pmd-mgmt -p 0x0f --config="(0,0,2),(0,1,3)" + +PMD Power Management Mode +------------------------- +There is also a traffic-aware operating mode that, instead of using explicit +power management, will use automatic PMD power management. This mode is limited +to one queue per core, and has three available power management schemes: + +* ``monitor`` - this will use ``rte_power_monitor()`` function to enter a + power-optimized state (subject to platform support). + +* ``pause`` - this will use ``rte_power_pause()`` or ``rte_pause()`` to avoid + busy looping when there is no traffic. + +* ``scale`` - this will use frequency scaling routines available in the + ``librte_power`` library. + +See :doc:`Power Management<../prog_guide/power_man>` chapter in the DPDK +Programmer's Guide for more details on PMD power management. + +.. code-block:: console + + ./<build_dir>/examples/dpdk-l3fwd-power -l 1-3 -- -p 0x0f --config="(0,0,2),(0,1,3)" --pmd-mgmt=scale
examples/l3fwd-power/main.c +87 -2
165 lines not on this page · raw message
Signature · 1 line
2.25.1
v16 cover Add PMD power management
In reply to Anatoly BurakovThis patchset proposes a simple API for Ethernet drivers to cause the CPU to enter a power-optimized state while waiting for packets to arrive. There are multiple proposed mechanisms to achieve said power
savings: simple frequency scaling, idle loop, and monitoring the Rx queue for incoming packages. The latter is achieved through cooperation with the NIC driver that will allow us to know address of wake up event, and wait for writes on that address.
On IA, this is achieved through using UMONITOR/UMWAIT instructions. They are used in their raw opcode form because there is no widespread compiler support for them yet. Still, the API is made generic enough to hopefully support other architectures, if they happen to implement similar instructions.
To achieve power savings, there is a very simple mechanism used: we're counting empty polls, and if a certain threshold is reached, we employ one of the suggested power management schemes automatically, from within a Rx callback inside the PMD. Once there's traffic again, the empty poll counter is reset.
This patchset also introduces a few changes into existing power management-related intrinsics, namely to provide a native way of waking up a sleeping core without application being responsible for it, as well as general robustness improvements. There's quite a bit of locking going on, but these locks are per-thread and very little (if any) contention is expected, so the performance impact shouldn't be that bad (and in any case the locking happens when we're about to sleep anyway).
Why are we putting it into ethdev as opposed to leaving this up to the application? Our customers specifically requested a way to do it with minimal changes to the application code. The current approach allows to just flip a switch and automatically have power savings.
Things of note:
- Only 1:1 core to queue mapping is supported, meaning that each lcore must at most handle RX on a single queue
- Support 3 type policies. Monitor/Pause/Frequency Scaling
- Power management is enabled per-queue
- The API doesn't extend to other device types
v16:
- Implemented Konstantin's suggestions and comments
- Added return values to the API
v15:
- Fixed incorrect check in UMWAIT callback
- Fixed accidental whitespace changes
v14:
- Fixed ARM/PPC builds
- Addressed various review comments
v13:
- Reworked the librte_power code to require less locking and handle invalid parameters better
- Fix numerous rebase errors present in v12
v12:
- Rebase on top of 21.02
- Rework of power intrinsics code
Anatoly Burakov (5):
eal: uninline power intrinsics
eal: avoid invalid API usage in power intrinsics
eal: change API of power intrinsics
eal: remove sync version of power monitor
eal: add monitor wakeup function
Liang Ma (6):
ethdev: add simple power management API
power: add PMD power management API and callback net/ixgbe: implement power management API net/i40e: implement power management API net/ice: implement power management API examples/l3fwd-power: enable PMD power mgmt
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 15 + .../sample_app_ug/l3_forward_power_man.rst | 35 ++ drivers/event/dlb/dlb.c | 10 +- drivers/event/dlb2/dlb2.c | 10 +- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 ++ drivers/net/i40e/i40e_rxtx.h | 1 + drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++ drivers/net/ice/ice_rxtx.h | 1 + drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 ++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + examples/l3fwd-power/main.c | 89 ++++- .../arm/include/rte_power_intrinsics.h | 40 -- lib/librte_eal/arm/meson.build | 1 + lib/librte_eal/arm/rte_power_intrinsics.c | 38 ++ .../include/generic/rte_power_intrinsics.h | 88 ++--- .../ppc/include/rte_power_intrinsics.h | 40 -- lib/librte_eal/ppc/meson.build | 1 + lib/librte_eal/ppc/rte_power_intrinsics.c | 38 ++ lib/librte_eal/version.map | 3 + .../x86/include/rte_power_intrinsics.h | 115 ------ lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 220 +++++++++++ lib/librte_ethdev/rte_ethdev.c | 28 ++ lib/librte_ethdev/rte_ethdev.h | 25 ++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++ lib/librte_ethdev/version.map | 3 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 ++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++ lib/librte_power/version.map | 5 + 34 files changed, 1148 insertions(+), 259 deletions(-) create mode 100644 lib/librte_eal/arm/rte_power_intrinsics.c create mode 100644 lib/librte_eal/ppc/rte_power_intrinsics.c create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
Signature · 1 line
2.25.1
PATCH v16 1/11 eal: uninline power intrinsics
Currently, power intrinsics are inline functions. Make them part of the ABI so that we can have various internal data associated with them without exposing said data to the outside world.
---
Notes:
v14:
- Fix compile issues on ARM and PPC64 by moving implementations to .c files
.../arm/include/rte_power_intrinsics.h | 40 ------ lib/librte_eal/arm/meson.build | 1 + lib/librte_eal/arm/rte_power_intrinsics.c | 45 +++++++ .../include/generic/rte_power_intrinsics.h | 6 +- .../ppc/include/rte_power_intrinsics.h | 40 ------ lib/librte_eal/ppc/meson.build | 1 + lib/librte_eal/ppc/rte_power_intrinsics.c | 45 +++++++ lib/librte_eal/version.map | 3 + .../x86/include/rte_power_intrinsics.h | 115 ----------------- lib/librte_eal/x86/meson.build | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 120 ++++++++++++++++++ 11 files changed, 219 insertions(+), 198 deletions(-) create mode 100644 lib/librte_eal/arm/rte_power_intrinsics.c create mode 100644 lib/librte_eal/ppc/rte_power_intrinsics.c create mode 100644 lib/librte_eal/x86/rte_power_intrinsics.c
lib/librte_eal/arm/include/rte_power_intrinsics.h +0 -40
@@ -13,46 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on ARM. -*/ -static inline void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - #ifdef __cplusplus }#endif
lib/librte_eal/arm/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/arm/rte_power_intrinsics.c new file +45 -0
45 lines not on this page · raw message
lib/librte_eal/include/generic/rte_power_intrinsics.h +3 -3
@@ -52,7 +52,7 @@* to undefined result. */ __rte_experimental -static inline void rte_power_monitor(const volatile void *p, +void rte_power_monitor(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz);
@@ -97,7 +97,7 @@ static inline void rte_power_monitor(const volatile void *p,* wakes up. */ __rte_experimental -static inline void rte_power_monitor_sync(const volatile void *p, +void rte_power_monitor_sync(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck);
@@ -118,6 +118,6 @@ static inline void rte_power_monitor_sync(const volatile void *p,* architecture-dependent. */ __rte_experimental -static inline void rte_power_pause(const uint64_t tsc_timestamp); +void rte_power_pause(const uint64_t tsc_timestamp);#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/ppc/include/rte_power_intrinsics.h +0 -40
@@ -13,46 +13,6 @@ extern "C" {#include "generic/rte_power_intrinsics.h" -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) -{ -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); -RTE_SET_USED (data_sz); -} - -/** -* This function is not supported on PPC64. -*/ -static inline void -rte_power_pause (const uint64_t tsc_timestamp) -{ -RTE_SET_USED (tsc_timestamp); -} - #ifdef __cplusplus }#endif
lib/librte_eal/ppc/meson.build +1 -0
@@ -7,4 +7,5 @@ sources += files( 'rte_cpuflags.c', 'rte_cycles.c', 'rte_hypervisor.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/ppc/rte_power_intrinsics.c new file +45 -0
45 lines not on this page · raw message
lib/librte_eal/version.map +3 -0
@@ -405,6 +405,9 @@ EXPERIMENTAL { rte_vect_set_max_simd_bitwidth; # added in 21.02 + rte_power_monitor; + rte_power_monitor_sync; + rte_power_pause; rte_thread_tls_key_create; rte_thread_tls_key_delete; rte_thread_tls_value_get;
lib/librte_eal/x86/include/rte_power_intrinsics.h +0 -115
121 lines not on this page · raw message
lib/librte_eal/x86/meson.build +1 -0
@@ -8,4 +8,5 @@ sources += files( 'rte_cycles.c', 'rte_hypervisor.c', 'rte_spinlock.c', + 'rte_power_intrinsics.c', )
lib/librte_eal/x86/rte_power_intrinsics.c new file +120 -0
120 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v16 2/11 eal: avoid invalid API usage in power intrinsics
In reply to Anatoly BurakovCurrently, the API documentation mandates that if the user wants to use the power management intrinsics, they need to call the rte_cpu_get_intrinsics_support API and check support for specific intrinsics.
However, if the user does not do that, it is possible to get illegal instruction error because we're using raw instruction opcodes, which may or may not be supported at runtime.
Now that we have everything in a C file, we can check for support at startup and prevent the user from possibly encountering illegal instruction errors.
We also add return values to the API's as well, because why not.
---
Notes:
v16:
- Add return values and proper error handling to the API
v15:
- Remove accidental whitespace changes
v14:
- Replace uint8_t with bool
v14:
- Replace uint8_t with bool
lib/librte_eal/arm/rte_power_intrinsics.c | 12 +++- .../include/generic/rte_power_intrinsics.h | 24 +++++-- lib/librte_eal/ppc/rte_power_intrinsics.c | 12 +++- lib/librte_eal/x86/rte_power_intrinsics.c | 64 +++++++++++++++++-- 4 files changed, 94 insertions(+), 18 deletions(-)
lib/librte_eal/arm/rte_power_intrinsics.c +9 -3
@@ -7,7 +7,7 @@/** * This function is not supported on ARM. */ -void +int rte_power_monitor (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz)
@@ -17,12 +17,14 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,RTE_SET_USED (value_mask);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (data_sz); + +return -ENOTSUP ; }/** * This function is not supported on ARM. */ -void +int rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck)
@@ -33,13 +35,17 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck);RTE_SET_USED (data_sz); + +return -ENOTSUP ; }/** * This function is not supported on ARM. */ -void +int rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); + +return -ENOTSUP ; }
lib/librte_eal/include/generic/rte_power_intrinsics.h +18 -6
@@ -34,7 +34,6 @@* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -50,9 +49,14 @@* Data size (in bytes) that will be used to compare expected value with the * memory address. Can be 1, 2, 4 or 8. Supplying any other value will lead * to undefined result. +* +* @return +* 0 on success +* -EINVAL on invalid parameters +* -ENOTSUP if unsupported */ __rte_experimental -void rte_power_monitor(const volatile void *p, +int rte_power_monitor(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz);
@@ -75,7 +79,6 @@ void rte_power_monitor(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param p * Address to monitor for changes.
@@ -95,9 +98,14 @@ void rte_power_monitor(const volatile void *p,* A spinlock that must be locked before entering the function, will be * unlocked while the CPU is sleeping, and will be locked again once the CPU * wakes up. +* +* @return +* 0 on success +* -EINVAL on invalid parameters +* -ENOTSUP if unsupported */ __rte_experimental -void rte_power_monitor_sync(const volatile void *p, +int rte_power_monitor_sync(const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck);
@@ -111,13 +119,17 @@ void rte_power_monitor_sync(const volatile void *p,* * @warning It is responsibility of the user to check if this function is * supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* Failing to do so may result in an illegal CPU instruction error. * * @param tsc_timestamp * Maximum TSC timestamp to wait for. Note that the wait behavior is * architecture-dependent. +* +* @return +* 0 on success +* -EINVAL on invalid parameters +* -ENOTSUP if unsupported */ __rte_experimental -void rte_power_pause(const uint64_t tsc_timestamp); +int rte_power_pause(const uint64_t tsc_timestamp);#endif /* _RTE_POWER_INTRINSIC_H_ */
lib/librte_eal/ppc/rte_power_intrinsics.c +9 -3
@@ -7,7 +7,7 @@/** * This function is not supported on PPC64. */ -void +int rte_power_monitor (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz)
@@ -17,12 +17,14 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,RTE_SET_USED (value_mask);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (data_sz); + +return -ENOTSUP ; }/** * This function is not supported on PPC64. */ -void +int rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value,const uint64_t value_mask,const uint64_t tsc_timestamp,const uint8_t data_sz,rte_spinlock_t *lck)
@@ -33,13 +35,17 @@ rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck);RTE_SET_USED (data_sz); + +return -ENOTSUP ; }/** * This function is not supported on PPC64. */ -void +int rte_power_pause (const uint64_t tsc_timestamp) {RTE_SET_USED (tsc_timestamp); + +return -ENOTSUP ; }
lib/librte_eal/x86/rte_power_intrinsics.c +58 -6
142 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v16 3/11 eal: change API of power intrinsics
In reply to Anatoly BurakovInstead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
---
Notes:
v16:
- Add error handling
drivers/event/dlb/dlb.c | 10 ++-- drivers/event/dlb2/dlb2.c | 10 ++-- lib/librte_eal/arm/rte_power_intrinsics.c | 20 +++----- .../include/generic/rte_power_intrinsics.h | 50 ++++++++----------- lib/librte_eal/ppc/rte_power_intrinsics.c | 20 +++----- lib/librte_eal/x86/rte_power_intrinsics.c | 42 +++++++++------- 6 files changed, 70 insertions(+), 82 deletions(-)
drivers/event/dlb/dlb.c +7 -3
@@ -3161,6 +3161,7 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,/* Interrupts not supported by PF PMD */ return 1 ; } else if (dlb->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -3181,9 +3182,12 @@ dlb_dequeue_wait(struct dlb_eventdev *dlb,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
drivers/event/dlb2/dlb2.c +7 -3
@@ -2870,6 +2870,7 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,if (elapsed_ticks >= timeout) {return 1 ; }else if (dlb2->umwait_allowed) { +struct rte_power_monitor_cond pmc;volatile struct dlb2_dequeue_qe *cq_base;union {uint64_t raw_qe[2 ];
@@ -2890,9 +2891,12 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,else expected_value =0 ; -rte_power_monitor (monitor_addr, expected_value, - qe_mask.raw_qe[1 ], timeout + start_ticks, -sizeof (uint64_t )); + pmc.addr = monitor_addr; + pmc.val = expected_value; + pmc.mask = qe_mask.raw_qe[1 ]; + pmc.data_sz =sizeof (uint64_t ); + +rte_power_monitor (&pmc, timeout + start_ticks);DLB2_INC_STAT (ev_port->stats.traffic.rx_umonitor_umwait,1 ); }else {
lib/librte_eal/arm/rte_power_intrinsics.c +6 -14
@@ -8,15 +8,11 @@* This function is not supported on ARM. */ int -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz);return -ENOTSUP ; }
@@ -25,16 +21,12 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,* This function is not supported on ARM. */ int -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz);return -ENOTSUP ; }
lib/librte_eal/include/generic/rte_power_intrinsics.h +20 -30
86 lines not on this page · raw message
lib/librte_eal/ppc/rte_power_intrinsics.c +6 -14
@@ -8,15 +8,11 @@* This function is not supported on PPC64. */ int -rte_power_monitor (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz) +rte_power_monitor (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (data_sz);return -ENOTSUP ; }
@@ -25,16 +21,12 @@ rte_power_monitor(const volatile void *p, const uint64_t expected_value,* This function is not supported on PPC64. */ int -rte_power_monitor_sync (const volatile void *p,const uint64_t expected_value, -const uint64_t value_mask,const uint64_t tsc_timestamp, -const uint8_t data_sz,rte_spinlock_t *lck) +rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, +const uint64_t tsc_timestamp,rte_spinlock_t *lck) { -RTE_SET_USED (p); -RTE_SET_USED (expected_value); -RTE_SET_USED (value_mask); +RTE_SET_USED (pmc);RTE_SET_USED (tsc_timestamp);RTE_SET_USED (lck); -RTE_SET_USED (data_sz);return -ENOTSUP ; }
lib/librte_eal/x86/rte_power_intrinsics.c +24 -18
84 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v16 03/11] eal: change API of power intrinsics
Quoted 5 lines
Instead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
Quoted 7 lines
Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v16:
- Add error handling
There are few trivial checkpatch warnings, please check
Re: [dpdk-dev] [PATCH v16 03/11] eal: change API of power intrinsics
Quoted 9 lines · Konstantin
Quoted 9 lines
Instead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 6 lines
Quoted 3 lines
Notes:
v16:
- Add error handlingThere are few trivial checkpatch warnings, please check
To paraphrase Nick Fury, I recognize that checkpatch has produced warnings, but given that i don't agree with what checkpatch has to say in this case, I've elected to ignore it :)
In particular, these warnings related to comments around struct members, which i think i've made to look nice and also took care of correct indentation in terms of code looking the same way with different tab widths. So, i don't think it should be changed, unless you're suggesting to re-layout comments on top of each member, rather than at the side (which i think is more readable).
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v16 03/11] eal: change API of power intrinsics
Quoted 22 lines · Konstantin
Quoted 9 lines
Quoted 9 lines
Instead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 6 lines
Quoted 3 lines
Notes:
v16:
- Add error handlingThere are few trivial checkpatch warnings, please check
To paraphrase Nick Fury, I recognize that checkpatch has produced warnings, but given that i don't agree with what checkpatch has to say in this case, I've elected to ignore it :)
Quoted 6 lines
In particular, these warnings related to comments around struct members, which i think i've made to look nice and also took care of correct indentation in terms of code looking the same way with different tab widths. So, i don't think it should be changed, unless you're suggesting to re-layout comments on top of each member, rather than at the side (which i think is more readable).
If top is not an option, it is possible to move comment on next after actual field lines:
uint32_t x; /**< * blah, blah * blah, blah, blah */
AFAIK that would keep checkpatch happy.
Re: [dpdk-dev] [PATCH v16 03/11] eal: change API of power intrinsics
Quoted 29 lines · Konstantin
Quoted 22 lines · Konstantin
Quoted 9 lines
Quoted 9 lines
Instead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 6 lines
Quoted 3 lines
Notes:
v16:
- Add error handlingThere are few trivial checkpatch warnings, please check
To paraphrase Nick Fury, I recognize that checkpatch has produced warnings, but given that i don't agree with what checkpatch has to say in this case, I've elected to ignore it :)
Quoted 6 lines
In particular, these warnings related to comments around struct members, which i think i've made to look nice and also took care of correct indentation in terms of code looking the same way with different tab widths. So, i don't think it should be changed, unless you're suggesting to re-layout comments on top of each member, rather than at the side (which i think is more readable).
Quoted 8 lines
If top is not an option, it is possible to move comment on next after actual field lines:
uint32_tx;
/**<
* blah, blah
* blah, blah, blah
*/
AFAIK that would keep checkpatch happy.
It's not as much "not an option" as it would look less readable to me than what there currently is. If we're going to keep comments not on the side, then on the top they go. I'd prefer to keep it as is, but if you feel strongly about it, i can change it.
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v16 03/11] eal: change API of power intrinsics
Quoted 42 lines · Konstantin
Quoted 29 lines
Quoted 22 lines · Konstantin
Quoted 9 lines
Quoted 9 lines
Instead of passing around pointers and integers, collect everything into struct. This makes API design around these intrinsics much easier.
Signed-off-by Anatoly Burakov <[email protected]>---
Quoted 6 lines
Quoted 3 lines
Notes:
v16:
- Add error handlingThere are few trivial checkpatch warnings, please check
To paraphrase Nick Fury, I recognize that checkpatch has produced warnings, but given that i don't agree with what checkpatch has to say in this case, I've elected to ignore it :)
Quoted 6 lines
In particular, these warnings related to comments around struct members, which i think i've made to look nice and also took care of correct indentation in terms of code looking the same way with different tab widths. So, i don't think it should be changed, unless you're suggesting to re-layout comments on top of each member, rather than at the side (which i think is more readable).
Quoted 8 lines
If top is not an option, it is possible to move comment on next after actual field lines:
uint32_tx;
/**<
* blah, blah
* blah, blah, blah
*/
AFAIK that would keep checkpatch happy.
Quoted 4 lines
It's not as much "not an option" as it would look less readable to me than what there currently is. If we're going to keep comments not on the side, then on the top they go. I'd prefer to keep it as is, but if you feel strongly about it, i can change it.
I don't have any preferences about comments placement. I just thought it would be good to keep checkpatch happy. Specially if the changes in question are just cosmetic ones.
PATCH v16 4/11 eal: remove sync version of power monitor
In reply to Anatoly BurakovCurrently, the "sync" version of power monitor intrinsic is supposed to be used for purposes of waking up a sleeping core. However, there are better ways to achieve the same result, so remove the unneeded function.
--- lib/librte_eal/arm/rte_power_intrinsics.c | 14 ----- .../include/generic/rte_power_intrinsics.h | 38 ------------- lib/librte_eal/ppc/rte_power_intrinsics.c | 14 ----- lib/librte_eal/version.map | 1 - lib/librte_eal/x86/rte_power_intrinsics.c | 54 ------------------- 5 files changed, 121 deletions(-)
lib/librte_eal/arm/rte_power_intrinsics.c +0 -14
@@ -17,20 +17,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,return -ENOTSUP ; } -/** -* This function is not supported on ARM. -*/ -int -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); - -return -ENOTSUP ; -} -/** * This function is not supported on ARM. */
lib/librte_eal/include/generic/rte_power_intrinsics.h +0 -38
@@ -61,44 +61,6 @@ struct rte_power_monitor_cond {__rte_experimental int rte_power_monitor(const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); -/** -* @warning -* @b EXPERIMENTAL: this API may change without prior notice -* -* Monitor specific address for changes. This will cause the CPU to enter an -* architecture-defined optimized power state until either the specified -* memory address is written to, a certain TSC timestamp is reached, or other -* reasons cause the CPU to wake up. -* -* Additionally, an `expected` 64-bit value and 64-bit mask are provided. If -* mask is non-zero, the current value pointed to by the `p` pointer will be -* checked against the expected value, and if they match, the entering of -* optimized power state may be aborted. -* -* This call will also lock a spinlock on entering sleep, and release it on -* waking up the CPU. -* -* @warning It is responsibility of the user to check if this function is -* supported at runtime using `rte_cpu_get_intrinsics_support()` API call. -* -* @param pmc -* The monitoring condition structure. -* @param tsc_timestamp -* Maximum TSC timestamp to wait for. Note that the wait behavior is -* architecture-dependent. -* @param lck -* A spinlock that must be locked before entering the function, will be -* unlocked while the CPU is sleeping, and will be locked again once the CPU -* wakes up. -* -* @return -* 0 on success -* -EINVAL on invalid parameters -* -ENOTSUP if unsupported -*/ -__rte_experimental -int rte_power_monitor_sync(const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck);/** * @warning
lib/librte_eal/ppc/rte_power_intrinsics.c +0 -14
@@ -17,20 +17,6 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,return -ENOTSUP ; } -/** -* This function is not supported on PPC64. -*/ -int -rte_power_monitor_sync (const struct rte_power_monitor_cond *pmc, -const uint64_t tsc_timestamp,rte_spinlock_t *lck) -{ -RTE_SET_USED (pmc); -RTE_SET_USED (tsc_timestamp); -RTE_SET_USED (lck); - -return -ENOTSUP ; -} -/** * This function is not supported on PPC64. */
lib/librte_eal/version.map +0 -1
@@ -406,7 +406,6 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; - rte_power_monitor_sync; rte_power_pause; rte_thread_tls_key_create; rte_thread_tls_key_delete;
lib/librte_eal/x86/rte_power_intrinsics.c +0 -54
60 lines not on this page · raw message
Signature · 1 line
2.25.1
PATCH v16 5/11 eal: add monitor wakeup function
In reply to Anatoly BurakovNow that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from rte_power_pause won't work.
---
Notes:
v16:
- Improve error handling
- Take a lock before UMONITOR
v13:
- Add comments around wakeup code to explain what it does
- Add lcore_id parameter checking to prevent buffer overrun
lib/librte_eal/arm/rte_power_intrinsics.c | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 +++ lib/librte_eal/ppc/rte_power_intrinsics.c | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 98 ++++++++++++++++++- 5 files changed, 132 insertions(+), 1 deletion(-)
lib/librte_eal/arm/rte_power_intrinsics.c +9 -0
@@ -27,3 +27,12 @@ rte_power_pause(const uint64_t tsc_timestamp)return -ENOTSUP ; } + +/** +* This function is not supported on ARM. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
lib/librte_eal/include/generic/rte_power_intrinsics.h +16 -0
@@ -62,6 +62,22 @@ __rte_experimentalint rte_power_monitor (const struct rte_power_monitor_cond *pmc,const uint64_t tsc_timestamp); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Wake up a specific lcore that is in a power optimized state and is monitoring +* an address. +* +* @note This function will *not* wake up a core that is in a power optimized +* state due to calling `rte_power_pause`. +* +* @param lcore_id +* Lcore ID of a sleeping thread. +*/ +__rte_experimental +int rte_power_monitor_wakeup(const unsigned int lcore_id); +/** * @warning * @b EXPERIMENTAL: this API may change without prior notice
lib/librte_eal/ppc/rte_power_intrinsics.c +9 -0
@@ -27,3 +27,12 @@ rte_power_pause(const uint64_t tsc_timestamp)return -ENOTSUP ; } + +/** +* This function is not supported on PPC64. +*/ +void +rte_power_monitor_wakeup (const unsigned int lcore_id) +{ +RTE_SET_USED (lcore_id); +}
lib/librte_eal/version.map +1 -0
@@ -406,6 +406,7 @@ EXPERIMENTAL { # added in 21.02 rte_power_monitor; + rte_power_monitor_wakeup; rte_power_pause; rte_thread_tls_key_create; rte_thread_tls_key_delete;
lib/librte_eal/x86/rte_power_intrinsics.c +97 -1
146 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v16 05/11] eal: add monitor wakeup function
Quoted 19 lines
Now that we have everything in a C file, we can store the information about our sleep, and have a native mechanism to wake up the sleeping core. This mechanism would however only wake up a core that's sleeping while monitoring - waking up from
rte_power_pausewon't work.Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v16:
- Improve error handling
- Take a lock before UMONITORv13:
- Add comments around wakeup code to explain what it does
- Addlcore_idparameter checking to prevent buffer overrun
Quoted 8 lines
lib/librte_eal/arm/rte_power_intrinsics.c | 9 ++ .../include/generic/rte_power_intrinsics.h | 16 +++ lib/librte_eal/ppc/rte_power_intrinsics.c | 9 ++ lib/librte_eal/version.map | 1 + lib/librte_eal/x86/rte_power_intrinsics.c | 98 ++++++++++++++++++- 5 files changed, 132 insertions(+), 1 deletion(-)--
Quoted 1 line
2.25.1
PATCH v16 6/11 ethdev: add simple power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the Rx queues from the PMD, as well as release notes information.
---
Notes:
v13:
- Fix typos and issues raised by Andrew
doc/guides/rel_notes/release_21_02.rst | 5 +++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 83 insertions(+)
doc/guides/rel_notes/release_21_02.rst +5 -0
@@ -55,6 +55,11 @@ New Features Also, make sure to start the actual text at the margin. ======================================================= +* **ethdev: added new API for PMD power management** + + * ``rte_eth_get_monitor_addr()``, to be used in conjunction with + ``rte_power_monitor()`` to enable automatic power management for PMD's. + Removed Items -------------
lib/librte_ethdev/rte_ethdev.c +28 -0
@@ -5115,6 +5115,34 @@ rte_eth_tx_burst_mode_get(uint16_t port_id, uint16_t queue_id, dev->dev_ops->tx_burst_mode_get(dev, queue_id, mode)); } +int +rte_eth_get_monitor_addr (uint16_t port_id,uint16_t queue_id, +struct rte_power_monitor_cond *pmc) +{ +struct rte_eth_dev *dev; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -ENODEV ); + + dev = &rte_eth_devices[port_id]; + +RTE_FUNC_PTR_OR_ERR_RET (*dev->dev_ops->get_monitor_addr, -ENOTSUP ); + +if (queue_id >= dev->data->nb_rx_queues) { +RTE_ETHDEV_LOG (ERR ,"Invalid Rx queue_id=%u\n" , queue_id); +return -EINVAL ; + } + +if (pmc ==NULL ) { +RTE_ETHDEV_LOG (ERR ,"Invalid power monitor condition=%p\n" , + pmc); +return -EINVAL ; + } + +return eth_err (port_id, + dev->dev_ops->get_monitor_addr (dev->data->rx_queues[queue_id], + pmc)); +} + int rte_eth_dev_set_mc_addr_list(uint16_t port_id, struct rte_ether_addr *mc_addr_set,
lib/librte_ethdev/rte_ethdev.h +25 -0
@@ -157,6 +157,7 @@ extern "C" {#include <rte_common.h> #include <rte_config.h> #include <rte_ether.h> +#include <rte_power_intrinsics.h> #include "rte_ethdev_trace_fp.h" #include "rte_dev_info.h"
@@ -4334,6 +4335,30 @@ __rte_experimentalint rte_eth_tx_burst_mode_get (uint16_t port_id,uint16_t queue_id,struct rte_eth_burst_mode *mode); +/** +* @warning +* @b EXPERIMENTAL: this API may change without prior notice +* +* Retrieve the monitor condition for a given receive queue. +* +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The Rx queue on the Ethernet device for which information +* will be retrieved. +* @param pmc +* The pointer point to power-optimized monitoring condition structure. +* +* @return +* - 0: Success. +* -ENOTSUP: Operation not supported. +* -EINVAL: Invalid parameters. +* -ENODEV: Invalid port ID. +*/ +__rte_experimental +int rte_eth_get_monitor_addr(uint16_t port_id, uint16_t queue_id, +struct rte_power_monitor_cond *pmc); +/** * Retrieve device registers and register attributes (number of registers and * register size)
lib/librte_ethdev/rte_ethdev_driver.h +22 -0
@@ -763,6 +763,26 @@ typedef int (*eth_hairpin_queue_peer_unbind_t) (struct rte_eth_dev *dev, uint16_t cur_queue, uint32_t direction);/**< @internal Unbind peer queue from the current queue. */ +/** +* @internal +* Get address of memory location whose contents will change whenever there is +* new data to be received on an Rx queue. +* +* @param rxq +* Ethdev queue pointer. +* @param pmc +* The pointer to power-optimized monitoring condition structure. +* @return +* Negative errno value on error, 0 on success. +* +* @retval 0 +* Success +* @retval -EINVAL +* Invalid parameters +*/ +typedef int (*eth_get_monitor_addr_t )(void *rxq, +struct rte_power_monitor_cond *pmc); +/** * @internal A structure containing the functions exported by an Ethernet driver. */
@@ -917,6 +937,8 @@ struct eth_dev_ops {/**< Set up the connection between the pair of hairpin queues. */ eth_hairpin_queue_peer_unbind_t hairpin_queue_peer_unbind;/**< Disconnect the hairpin queues of a pair from each other. */ +eth_get_monitor_addr_t get_monitor_addr; +/**< Get power monitoring condition for Rx queue. */ };/**
lib/librte_ethdev/version.map +3 -0
@@ -240,6 +240,9 @@ EXPERIMENTAL { rte_flow_get_restore_info; rte_flow_tunnel_action_decap_release; rte_flow_tunnel_item_release; + + # added in 21.02 + rte_eth_get_monitor_addr; }; INTERNAL {
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v16 06/11] ethdev: add simple power management API
Quoted 15 lines
Add a simple API to allow getting the monitor conditions for power-optimized monitoring of the Rx queues from the PMD, as well as release notes information.
Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>Acked-by Andrew Rybchenko <[email protected]>---
Notes:
v13:
- Fix typos and issues raised by Andrew
Quoted 7 lines
doc/guides/rel_notes/release_21_02.rst | 5 +++++ lib/librte_ethdev/rte_ethdev.c | 28 ++++++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev.h | 25 +++++++++++++++++++++++ lib/librte_ethdev/rte_ethdev_driver.h | 22 ++++++++++++++++++++ lib/librte_ethdev/version.map | 3 +++ 5 files changed, 83 insertions(+)--
Quoted 1 line
2.25.1
PATCH v16 7/11 power: add PMD power management API and callback
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.
---
Notes:
v15:
- Fix check in UMWAIT callback
v13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Rework n_rx_queues access to not go through internal PMD structures and use public API instead
v13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Rework n_rx_queues access to not go through internal PMD structures and use public API instead
doc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
doc/guides/prog_guide/power_man.rst +44 -0
@@ -192,6 +192,47 @@ User Cases ---------- The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA. +PMD Power Management API +------------------------ + +Abstract +~~~~~~~~ +Existing power management mechanisms require developers to change application +design or change code to make use of it. The PMD power management API provides a +convenient alternative by utilizing Ethernet PMD RX callbacks, and triggering +power saving whenever empty poll count reaches a certain number. + + * Monitor + + This power saving scheme will put the CPU into optimized power state and use + the ``rte_power_monitor()`` function to monitor the Ethernet PMD RX + descriptor address, and wake the CPU up whenever there's new traffic. + + * Pause + + This power saving scheme will avoid busy polling by either entering + power-optimized sleep state with ``rte_power_pause()`` function, or, if it's + not available, use ``rte_pause()``. + + * Frequency scaling + + This power saving scheme will use existing ``librte_power`` library + functionality to scale the core frequency up/down depending on traffic + volume. + + +.. note:: + + Currently, this power management API is limited to mandatory mapping of 1 + queue to 1 core (multiple queues are supported, but they must be polled from + different cores). + +API Overview for PMD Power Management +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* **Queue Enable**: Enable specific power scheme for certain queue/port/core + +* **Queue Disable**: Disable power scheme for certain queue/port/core + References ----------
@@ -200,3 +241,6 @@ References * The :doc:`../sample_app_ug/vm_power_management` chapter in the :doc:`../sample_app_ug/index` section. + +* The :doc:`../sample_app_ug/rxtx_callbacks` + chapter in the :doc:`../sample_app_ug/index` section.
doc/guides/rel_notes/release_21_02.rst +10 -0
@@ -60,6 +60,16 @@ New Features * ``rte_eth_get_monitor_addr()``, to be used in conjunction with ``rte_power_monitor()`` to enable automatic power management for PMD's. +* **Add PMD power management helper API** + + A new helper API has been added to make using Ethernet PMD power management + easier for the user: ``rte_power_pmd_mgmt_queue_enable()``. Three power + management schemes are supported initially: + + * Power saving based on UMWAIT instruction (x86 only) + * Power saving based on ``rte_pause()`` (generic) or TPAUSE instruction (x86 only) + * Power saving based on frequency scaling through the ``librte_power`` library + Removed Items -------------
lib/librte_power/meson.build +3 -2
@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c', 'power_kvm_vm.c', 'guest_channel.c', 'rte_power_empty_poll.c', 'power_pstate_cpufreq.c', + 'rte_power_pmd_mgmt.c', 'power_common.c') -headers = files('rte_power.h','rte_power_empty_poll.h') -deps += ['timer'] +headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h') +deps += ['timer' ,'ethdev']
lib/librte_power/rte_power_pmd_mgmt.c new file +359 -0
359 lines not on this page · raw message
lib/librte_power/rte_power_pmd_mgmt.h new file +90 -0
90 lines not on this page · raw message
lib/librte_power/version.map +5 -0
@@ -34,4 +34,9 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + + # added in 21.02 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + };
Signature · 1 line
2.25.1
Re: [dpdk-dev] [PATCH v16 07/11] power: add PMD power management API and callback
Quoted 70 lines
-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Tuesday, January 12, 2021 5:37 PM
To: [email protected]
Cc: Ma, Liang J <[email protected]>; Hunt, David <[email protected]>; Ray Kinsella <[email protected]>; Neil Horman <[email protected]>; [email protected]; Ananyev, Konstantin <[email protected]>; McDaniel, Timothy <[email protected]>; Richardson, Bruce <[email protected]>; Macnamara, Chris <[email protected]>
Subject: [PATCH v16 07/11] power: add PMD power management API and callbackFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v15:
- Fix check in UMWAIT callbackv13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Reworkn_rx_queuesaccess to not go through internal PMD structures and use public API insteadv13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Reworkn_rx_queuesaccess to not go through internal PMD structures and use public API insteaddoc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h
...
Quoted 17 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +17
-0+ +static uint16_t +clb_umwait(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts __rte_unused, + uint16_t nb_rx, uint16_t max_pkts __rte_unused, + void *addr __rte_unused) +{ + +struct pmd_queue_cfg *q_conf; + + q_conf = &port_cfg[port_id][qidx]; + +if (unlikely (nb_rx ==0 )) { + q_conf->empty_poll_stats++; +if (unlikely (q_conf->empty_poll_stats >EMPTYPOLL_MAX )) { +struct rte_power_monitor_cond pmc; +uint16_t ret; +
lib/librte_power/rte_power_pmd_mgmt.c line 105
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+/* +* we might get a cancellation request while being +* inside the callback, in which case the wakeup +* wouldn't work because it would've arrived too early. +* +* to get around this, we notify the other thread that +* we're sleeping, so that it can spin until we're done. +* unsolicited wakeups are perfectly safe. +*/ + q_conf->umwait_in_progress = true;
This write and subsequent read can be reordered by the cpu. I think you need rte_atomic_thread_fence(__ATOMIC_SEQ_CST) here and in disable() code-path below.
Quoted 17 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +17
-0+ +/* check if we need to cancel sleep */ +if (q_conf->pwr_mgmt_state ==PMD_MGMT_ENABLED ) { +/* use monitoring condition to sleep */ + ret =rte_eth_get_monitor_addr (port_id, qidx, + &pmc); +if (ret ==0 ) +rte_power_monitor (&pmc,-1ULL ); + } + q_conf->umwait_in_progress = false; + } + }else + q_conf->empty_poll_stats =0 ; + +return nb_rx; +} +
...
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+ +int +rte_power_pmd_mgmt_queue_disable (unsigned int lcore_id, +uint16_t port_id,uint16_t queue_id) +{ +struct pmd_queue_cfg *queue_cfg; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -EINVAL ); + +if (lcore_id >=RTE_MAX_LCORE || queue_id >=RTE_MAX_QUEUES_PER_PORT )
lib/librte_power/rte_power_pmd_mgmt.c line 320
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+return -EINVAL ; + +/* no need to check queue id as wrong queue id would not be enabled */ + queue_cfg = &port_cfg[port_id][queue_id]; + +if (queue_cfg->pwr_mgmt_state !=PMD_MGMT_ENABLED ) +return -EINVAL ; + +/* let the callback know we're shutting down */ + queue_cfg->pwr_mgmt_state =PMD_MGMT_BUSY ;
Same as above - write to pwr_mgmt_state and read from umwait_in_progress could be reordered by cpu.
Need to insert rte_atomic_thread_fence(__ATOMIC_SEQ_CST) between them.
BTW, out of curiosity - why do you need this intermediate state (PMD_MGMT_BUSY) at all?
Why not directly:queue_cfg->pwr_mgmt_state = PMD_MGMT_DISABLED;
?
Quoted 151 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +39
-0+ +switch (queue_cfg->cb_mode) { +case RTE_POWER_MGMT_TYPE_MONITOR : + { +bool exit = false; +do { +/* +* we may request cancellation while the other thread +* has just entered the callback but hasn't started +* sleeping yet, so keep waking it up until we know it's +* done sleeping. +*/ +if (queue_cfg->umwait_in_progress) +rte_power_monitor_wakeup (lcore_id); +else + exit = true; + }while (!exit); + } +/* fall-through */ +case RTE_POWER_MGMT_TYPE_PAUSE : +rte_eth_remove_rx_callback (port_id, queue_id, + queue_cfg->cur_cb); +break ; +case RTE_POWER_MGMT_TYPE_SCALE : +rte_power_freq_max (lcore_id); +rte_eth_remove_rx_callback (port_id, queue_id, + queue_cfg->cur_cb); +rte_power_exit (lcore_id); +break ; + } +/* +* we don't free the RX callback here because it is unsafe to do so +* unless we know for a fact that all data plane threads have stopped. +*/ + queue_cfg->cur_cb =NULL ; + queue_cfg->pwr_mgmt_state =PMD_MGMT_DISABLED ; + +return 0 ; +}lib/librte_power/rte_power_pmd_mgmt.h new file +90
-0@@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: BSD-3-Clause +* Copyright(c) 2010-2020 Intel Corporation +*/ + +#ifndef _RTE_POWER_PMD_MGMT_H +#define _RTE_POWER_PMD_MGMT_H + +/** +* @file +* RTE PMD Power Management +*/ +#include <stdint.h> +#include <stdbool.h> + +#include <rte_common.h> +#include <rte_byteorder.h> +#include <rte_log.h> +#include <rte_power.h> +#include <rte_atomic.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* PMD Power Management Type +*/ +enum rte_power_pmd_mgmt_type { +/** Use power-optimized monitoring to wait for incoming traffic */ +RTE_POWER_MGMT_TYPE_MONITOR =1 , +/** Use power-optimized sleep to avoid busy polling */ +RTE_POWER_MGMT_TYPE_PAUSE , +/** Use frequency scaling when traffic is low */ +RTE_POWER_MGMT_TYPE_SCALE , +}; + +/** +* @warning +* @b EXPERIMENTAL: this API may change, or be removed, without prior notice +* +* Enable power management on a specified RX queue and lcore. +* +* @note This function is not thread-safe. +* +* @param lcore_id +* lcore_id. +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The queue identifier of the Ethernet device. +* @param mode +* The power management callback function type. + +* @return +* 0 on success +* <0 on error +*/ +__rte_experimental +int +rte_power_pmd_mgmt_queue_enable (unsigned int lcore_id, +uint16_t port_id,uint16_t queue_id, +enum rte_power_pmd_mgmt_type mode); + +/** +* @warning +* @b EXPERIMENTAL: this API may change, or be removed, without prior notice +* +* Disable power management on a specified RX queue and lcore. +* +* @note This function is not thread-safe. +* +* @param lcore_id +* lcore_id. +* @param port_id +* The port identifier of the Ethernet device. +* @param queue_id +* The queue identifier of the Ethernet device. +* @return +* 0 on success +* <0 on error +*/ +__rte_experimental +int +rte_power_pmd_mgmt_queue_disable (unsigned int lcore_id, +uint16_t port_id,uint16_t queue_id); +#ifdef __cplusplus +} +#endif + +#endif lib/librte_power/version.map +5
-1@@ -34,4 +34,9 @@ EXPERIMENTAL { rte_power_guest_channel_receive_msg; rte_power_poll_stat_fetch; rte_power_poll_stat_update; + + # added in 21.02 + rte_power_pmd_mgmt_queue_enable; + rte_power_pmd_mgmt_queue_disable; + }; --2.25.1
Re: [dpdk-dev] [PATCH v16 07/11] power: add PMD power management API and callback
Quoted 150 lines · Konstantin
Quoted 70 lines
-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Tuesday, January 12, 2021 5:37 PM
To: [email protected]
Cc: Ma, Liang J <[email protected]>; Hunt, David <[email protected]>; Ray Kinsella <[email protected]>; Neil Horman <[email protected]>; [email protected]; Ananyev, Konstantin <[email protected]>; McDaniel, Timothy <[email protected]>; Richardson, Bruce <[email protected]>; Macnamara, Chris <[email protected]>
Subject: [PATCH v16 07/11] power: add PMD power management API and callbackFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v15:
- Fix check in UMWAIT callbackv13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Reworkn_rx_queuesaccess to not go through internal PMD structures and use public API insteadv13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Reworkn_rx_queuesaccess to not go through internal PMD structures and use public API insteaddoc/guides/prog_guide/power_man.rst | 44 +++ doc/guides/rel_notes/release_21_02.rst | 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 + 6 files changed, 511 insertions(+), 2 deletions(-)create mode 100644
lib/librte_power/rte_power_pmd_mgmt.ccreate mode 100644lib/librte_power/rte_power_pmd_mgmt.h...
Quoted 17 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +17
-0+ +static uint16_t +clb_umwait(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts __rte_unused, +uint16_t nb_rx, uint16_t max_pkts __rte_unused, +void *addr __rte_unused) +{ + +struct pmd_queue_cfg *q_conf; + +q_conf = &port_cfg[port_id][qidx]; + +if (unlikely (nb_rx ==0 )) { +q_conf->empty_poll_stats++; +if (unlikely (q_conf->empty_poll_stats >EMPTYPOLL_MAX )) { +struct rte_power_monitor_cond pmc; +uint16_t ret; +lib/librte_power/rte_power_pmd_mgmt.c line 105
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+/* +* we might get a cancellation request while being +* inside the callback, in which case the wakeup +* wouldn't work because it would've arrived too early. +* +* to get around this, we notify the other thread that +* we're sleeping, so that it can spin until we're done. +* unsolicited wakeups are perfectly safe. +*/ +q_conf->umwait_in_progress = true;This write and subsequent read can be reordered by the cpu. I think you need
rte_atomic_thread_fence(__ATOMIC_SEQ_CST)here and indisable()code-path below.Quoted 17 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +17
-0+ +/* check if we need to cancel sleep */ +if (q_conf->pwr_mgmt_state ==PMD_MGMT_ENABLED ) { +/* use monitoring condition to sleep */ +ret =rte_eth_get_monitor_addr (port_id, qidx, +&pmc); +if (ret ==0 ) +rte_power_monitor (&pmc,-1ULL ); +} +q_conf->umwait_in_progress = false; +} +}else +q_conf->empty_poll_stats =0 ; + +return nb_rx; +} +...
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+ +int +rte_power_pmd_mgmt_queue_disable (unsigned int lcore_id, +uint16_t port_id,uint16_t queue_id) +{ +struct pmd_queue_cfg *queue_cfg; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -EINVAL ); + +if (lcore_id >=RTE_MAX_LCORE || queue_id >=RTE_MAX_QUEUES_PER_PORT )lib/librte_power/rte_power_pmd_mgmt.c line 320
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+return -EINVAL ; + +/* no need to check queue id as wrong queue id would not be enabled */ +queue_cfg = &port_cfg[port_id][queue_id]; + +if (queue_cfg->pwr_mgmt_state !=PMD_MGMT_ENABLED ) +return -EINVAL ; + +/* let the callback know we're shutting down */ +queue_cfg->pwr_mgmt_state =PMD_MGMT_BUSY ;Same as above - write to
pwr_mgmt_stateand read fromumwait_in_progresscould be reordered by cpu.
Need to insertrte_atomic_thread_fence(__ATOMIC_SEQ_CST)between them.
Quoted 6 lines
BTW, out of curiosity - why do you need this intermediate state (
PMD_MGMT_BUSY) at all?
Why not directly:queue_cfg->pwr_mgmt_state=PMD_MGMT_DISABLED;
?
Thanks for suggestions, i'll add those.
The goal for the "intermediate" step is to prevent Rx callback from sleeping in the first place. We can't "wake up" earlier than it goes to sleep, but we may get a request to disable power management while we're at the beginning of the callback and haven't yet entered the rte_power_monitor code.
In this case, setting it to "BUSY" will prevent the callback from ever sleeping in the first place (see rte_power_pmd_mgmt:108 check), and will unset the "umwait in progress" if there was any.
So, we have three situations to handle:
1) wake up during umwait
2) "wake up" during callback after we've set the "umwait in progress" flag but before actual umwait happens - we don't wait to exit before we're sure there's nothing sleeping there
3) "wake up" during callback before we set the "umwait in progress" flag
1) is handled by the rte_power_monitor_wakeup() call, so that's taken care of. 2) is handled by the other thread waiting on "umwait in progress" becoming false. 3) is handled by having this BUSY check in the umwait thread.
Hope that made sense!
Signature · 2 lines
Thanks, Anatoly
Re: [dpdk-dev] [PATCH v16 07/11] power: add PMD power management API and callback
Quoted 167 lines · Anatoly
Quoted 151 lines · Konstantin
Quoted 70 lines
-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Tuesday, January 12, 2021 5:37 PM
To: [email protected]
Cc: Ma, Liang J <[email protected]>; Hunt, David <[email protected]>; Ray Kinsella <[email protected]>; Neil Horman <[email protected]>; [email protected]; Ananyev, Konstantin <[email protected]>; McDaniel, Timothy <[email protected]>; Richardson, Bruce <[email protected]>; Macnamara, Chris <[email protected]>
Subject: [PATCH v16 07/11] power: add PMD power management API and callbackFrom: Liang Ma <[email protected]>
Add a simple on/off switch that will enable saving power when no packets are arriving. It is based on counting the number of empty polls and, when the number reaches a certain threshold, entering an architecture-defined optimized power state that will either wait until a TSC timestamp expires, or when packets arrive.
This API mandates a core-to-single-queue mapping (that is, multiple queued per device are supported, but they have to be polled on different cores).
This design is using PMD RX callbacks.
1. UMWAIT/UMONITOR:
When a certain threshold of empty polls is reached, the core will go into a power optimized sleep while waiting on an address of next RX descriptor to be written to.
2. TPAUSE/Pause instruction
This method uses the pause (or TPAUSE, if available) instruction to avoid busy polling.
3. Frequency scaling
Reuse existing DPDK power library to scale up/down core frequency depending on traffic volume.Signed-off-by Liang Ma <[email protected]>Signed-off-by Anatoly Burakov <[email protected]>---
Notes:
v15:
- Fix check in UMWAIT callbackv13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Reworkn_rx_queuesaccess to not go through internal PMD structures and use public API insteadv13:
- Rework the synchronization mechanism to not require locking
- Add more parameter checking
- Reworkn_rx_queuesaccess to not go through internal PMD structures and use public API insteaddoc/guides/prog_guide/power_man.rst | 44 +++
doc/guides/rel_notes/release_21_02.rst| 10 + lib/librte_power/meson.build | 5 +- lib/librte_power/rte_power_pmd_mgmt.c | 359 +++++++++++++++++++++++++ lib/librte_power/rte_power_pmd_mgmt.h | 90 +++++++ lib/librte_power/version.map | 5 +6 files changed, 511 insertions(+), 2 deletions(-)create mode 100644
lib/librte_power/rte_power_pmd_mgmt.ccreate mode 100644lib/librte_power/rte_power_pmd_mgmt.h...
Quoted 18 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +3
-0+ +static uint16_t +clb_umwait(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts
__rte_unused,lib/librte_power/rte_power_pmd_mgmt.c new file +14
-0+uint16_t nb_rx, uint16_t max_pkts __rte_unused, +void *addr __rte_unused) +{ + +struct pmd_queue_cfg *q_conf; + +q_conf = &port_cfg[port_id][qidx]; + +if (unlikely (nb_rx ==0 )) { +q_conf->empty_poll_stats++; +if (unlikely (q_conf->empty_poll_stats >EMPTYPOLL_MAX )) { +struct rte_power_monitor_cond pmc; +uint16_t ret; +lib/librte_power/rte_power_pmd_mgmt.c line 105
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+/* +* we might get a cancellation request while being +* inside the callback, in which case the wakeup +* wouldn't work because it would've arrived too early. +* +* to get around this, we notify the other thread that +* we're sleeping, so that it can spin until we're done. +* unsolicited wakeups are perfectly safe. +*/ +q_conf->umwait_in_progress = true;This write and subsequent read can be reordered by the cpu. I think you need
rte_atomic_thread_fence(__ATOMIC_SEQ_CST)here and indisable()code-path below.Quoted 17 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +17
-0+ +/* check if we need to cancel sleep */ +if (q_conf->pwr_mgmt_state ==PMD_MGMT_ENABLED ) { +/* use monitoring condition to sleep */ +ret =rte_eth_get_monitor_addr (port_id, qidx, +&pmc); +if (ret ==0 ) +rte_power_monitor (&pmc,-1ULL ); +} +q_conf->umwait_in_progress = false; +} +}else +q_conf->empty_poll_stats =0 ; + +return nb_rx; +} +...
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+ +int +rte_power_pmd_mgmt_queue_disable (unsigned int lcore_id, +uint16_t port_id,uint16_t queue_id) +{ +struct pmd_queue_cfg *queue_cfg; + +RTE_ETH_VALID_PORTID_OR_ERR_RET (port_id, -EINVAL ); + +if (lcore_id >=RTE_MAX_LCORE || queue_id >=RTE_MAX_QUEUES_PER_PORT )lib/librte_power/rte_power_pmd_mgmt.c line 320
Quoted 10 lines
lib/librte_power/rte_power_pmd_mgmt.c new file +10
-0+return -EINVAL ; + +/* no need to check queue id as wrong queue id would not be enabled */ +queue_cfg = &port_cfg[port_id][queue_id]; + +if (queue_cfg->pwr_mgmt_state !=PMD_MGMT_ENABLED ) +return -EINVAL ; + +/* let the callback know we're shutting down */ +queue_cfg->pwr_mgmt_state =PMD_MGMT_BUSY ;Same as above - write to
pwr_mgmt_stateand read fromumwait_in_progresscould be reordered by cpu.
Need to insertrte_atomic_thread_fence(__ATOMIC_SEQ_CST)between them.Quoted 6 lines
BTW, out of curiosity - why do you need this intermediate state (
PMD_MGMT_BUSY) at all?
Why not directly:queue_cfg->pwr_mgmt_state=PMD_MGMT_DISABLED;
?Thanks for suggestions, i'll add those.
The goal for the "intermediate" step is to prevent Rx callback from sleeping in the first place. We can't "wake up" earlier than it goes to sleep, but we may get a request to disable power management while we're at the beginning of the callback and haven't yet entered the
rte_power_monitorcode.In this case, setting it to "BUSY" will prevent the callback from ever sleeping in the first place (see rte_power_pmd_mgmt:108 check), and will unset the "umwait in progress" if there was any.
So, we have three situations to handle:
Quoted 8 lines
1) wake up during umwait
2) "wake up" during callback after we've set the "umwait in progress" flag but before actual umwait happens - we don't wait to exit before we're sure there's nothing sleeping there
3) "wake up" during callback before we set the "umwait in progress" flag1) is handled by the
rte_power_monitor_wakeup()call, so that's taken care of. 2) is handled by the other thread waiting on "umwait in progress" becoming false. 3) is handled by having this BUSY check in the umwait thread.Hope that made sense!
On further thoughts, the "BUSY" thing relies on a hidden assumption that enable/disable power management per queue is supposed to be thread safe. If we let go of this assumption, we can get by with just enable/disable, so i think i'll just document the thread safety and leave out the "BUSY" part.
Signature · 2 lines
Thanks, Anatoly
PATCH v16 8/11 net/ixgbe: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ixgbe/ixgbe_ethdev.c | 1 + drivers/net/ixgbe/ixgbe_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/ixgbe/ixgbe_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/ixgbe/ixgbe_ethdev.c +1 -0
@@ -560,6 +560,7 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = { .udp_tunnel_port_del = ixgbe_dev_udp_tunnel_port_del, .tm_ops_get = ixgbe_tm_ops_get, .tx_done_cleanup = ixgbe_dev_tx_done_cleanup, + .get_monitor_addr = ixgbe_get_monitor_addr, };/*
drivers/net/ixgbe/ixgbe_rxtx.c +25 -0
@@ -1369,6 +1369,31 @@ const uint32_t RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP, }; +int +ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ixgbe_adv_rx_desc *rxdp; +struct ixgbe_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.upper.status_error; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + pmc->mask =rte_cpu_to_le_32 (IXGBE_RXDADV_STAT_DD ); + +/* the registers are 32-bit */ + pmc->data_sz =sizeof (uint32_t ); + +return 0 ; +} +/* @note: fix ixgbe_dev_supported_ptypes_get() if any change here. */ static inline uint32_t ixgbe_rxd_pkt_info_to_pkt_type (uint32_t pkt_info,uint16_t ptype_mask)
drivers/net/ixgbe/ixgbe_rxtx.h +1 -0
@@ -299,5 +299,6 @@ uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_queue_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_rx_port_offloads (struct rte_eth_dev *dev);uint64_t ixgbe_get_tx_queue_offloads (struct rte_eth_dev *dev); +int ixgbe_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#endif /* _IXGBE_RXTX_H_ */
Signature · 1 line
2.25.1
PATCH v16 9/11 net/i40e: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/i40e/i40e_ethdev.c | 1 + drivers/net/i40e/i40e_rxtx.c | 25 +++++++++++++++++++++++++ drivers/net/i40e/i40e_rxtx.h | 1 + 3 files changed, 27 insertions(+)
drivers/net/i40e/i40e_ethdev.c +1 -0
@@ -510,6 +510,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = { .mtu_set = i40e_dev_mtu_set, .tm_ops_get = i40e_tm_ops_get, .tx_done_cleanup = i40e_tx_done_cleanup, + .get_monitor_addr = i40e_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/i40e/i40e_rxtx.c +25 -0
@@ -72,6 +72,31 @@#define I40E_TX_OFFLOAD_NOTSUP_MASK \ (PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_MASK) +int +i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +struct i40e_rx_queue *rxq = rx_queue; +volatile union i40e_rx_desc *rxdp; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.qword1.status_error_len; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + pmc->mask =rte_cpu_to_le_64 (1 <<I40E_RX_DESC_STATUS_DD_SHIFT ); + +/* registers are 64-bit */ + pmc->data_sz =sizeof (uint64_t ); + +return 0 ; +} +static inline void i40e_rxd_to_vlan_tci (struct rte_mbuf *mb,volatile union i40e_rx_desc *rxdp) {
drivers/net/i40e/i40e_rxtx.h +1 -0
@@ -248,6 +248,7 @@ uint16_t i40e_recv_scattered_pkts_vec_avx2(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);uint16_t i40e_xmit_pkts_vec_avx2 (void *tx_queue,struct rte_mbuf **tx_pkts,uint16_t nb_pkts); +int i40e_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);/* For each value it means, datasheet of hardware can tell more details *
Signature · 1 line
2.25.1
PATCH v16 10/11 net/ice: implement power management API
In reply to Anatoly BurakovFrom: Liang Ma <[email protected]>
Implement support for the power management API by implementing a get_monitor_addr function that will return an address of an RX ring's status bit.
--- drivers/net/ice/ice_ethdev.c | 1 + drivers/net/ice/ice_rxtx.c | 26 ++++++++++++++++++++++++++ drivers/net/ice/ice_rxtx.h | 1 + 3 files changed, 28 insertions(+)
drivers/net/ice/ice_ethdev.c +1 -0
@@ -216,6 +216,7 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .udp_tunnel_port_add = ice_dev_udp_tunnel_port_add, .udp_tunnel_port_del = ice_dev_udp_tunnel_port_del, .tx_done_cleanup = ice_tx_done_cleanup, + .get_monitor_addr = ice_get_monitor_addr, };/* store statistics names and its offset in stats structure */
drivers/net/ice/ice_rxtx.c +26 -0
@@ -26,6 +26,32 @@ uint64_t rte_net_ice_dynflag_proto_xtr_ipv6_flow_mask;uint64_t rte_net_ice_dynflag_proto_xtr_tcp_mask;uint64_t rte_net_ice_dynflag_proto_xtr_ip_offset_mask; +int +ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc) +{ +volatile union ice_rx_flex_desc *rxdp; +struct ice_rx_queue *rxq = rx_queue; +uint16_t desc; + + desc = rxq->rx_tail; + rxdp = &rxq->rx_ring[desc]; +/* watch for changes in status bit */ + pmc->addr = &rxdp->wb.status_error0; + +/* +* we expect the DD bit to be set to 1 if this descriptor was already +* written to. +*/ + pmc->val =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + pmc->mask =rte_cpu_to_le_16 (1 <<ICE_RX_FLEX_DESC_STATUS0_DD_S ); + +/* register is 16-bit */ + pmc->data_sz =sizeof (uint16_t ); + +return 0 ; +} + +static inline uint8_t ice_proto_xtr_type_to_rxdid (uint8_t xtr_type) {
drivers/net/ice/ice_rxtx.h +1 -0
@@ -263,6 +263,7 @@ uint16_t ice_xmit_pkts_vec_avx512(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);int ice_fdir_programming (struct ice_pf *pf,struct ice_fltr_desc *fdir_desc);int ice_tx_done_cleanup (void *txq,uint32_t free_cnt); +int ice_get_monitor_addr (void *rx_queue,struct rte_power_monitor_cond *pmc);#define FDIR_PARSING_ENABLE_PER_QUEUE (ad, on) do { \ int i; \
Signature · 1 line
2.25.1