Skip to contentSkip to conversation

Power-optimized RX for Ethernet devices

RFC In review Anatoly Burakov 20 participants 421 messages May 27, 2020 – Jan 29, 2021 Page 1 of 4

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
new file mode 100644
index 0000000000..8646c4ac16
@@ -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
index bc73ec2c5c..b54a2be4f6 100644
@@ -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
index f0e998c2fe..494a8142a2 100644
@@ -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
index c1d20364d1..94d6a43763 100644
@@ -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
index 30439e7951..0325c4b93b 100644
@@ -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
#2 lore raw

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
---
 .../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
new file mode 100644
index 0000000000..8646c4ac16
@@ -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
index bc73ec2c5c..b54a2be4f6 100644
@@ -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
index f0e998c2fe..494a8142a2 100644
@@ -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
index c1d20364d1..94d6a43763 100644
@@ -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
new file mode 100644
index 0000000000..a0522400fb
@@ -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 */
+	asm volatile(".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 */
+	asm volatile(".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 */
+	asm volatile(".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
index 30439e7951..0325c4b93b 100644
@@ -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

#3 lore raw

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
#4 lore raw

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

#5 lore raw

Re: [dpdk-dev] [RFC 1/6] eal: add power management intrinsics

In reply to Burakov, Anatoly
Quoted 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

#6 lore raw

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.
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);

Quoted 6 lines

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.

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

#7 lore raw

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.
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);

Quoted 6 lines

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.

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

#8 lore raw

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.
Konstantin

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.
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.

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.html

Quoted 2 lines

Might be ARM people can comment/correct me here.
Konstantin

#9 lore raw

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.
Konstantin

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.
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.

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.html

Quoted 2 lines

Might be ARM people can comment/correct me here.
Konstantin

#10 lore raw

v11 cover Add PMD power mgmt

In reply to 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, 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
#11 lore raw

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
index 88b9086390..e95e6aa7a5 100644
@@ -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
index b12bb3854d..4f3115fe8e 100644
@@ -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
index c63b9f7eb7..e7ce1e261d 100644
@@ -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
index 8ddda2547f..f9ce4e3c8d 100644
@@ -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
#12 lore raw

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.

---

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
---

<snip>

Quoted 13 lines

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 +2 -0
index e341a08817..d208fe99ca 100644
@@ -4364,6 +4364,47 @@ __rte_experimental
  int 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
index c63b9f7eb7..e7ce1e261d 100644
@@ -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
index 8ddda2547f..f9ce4e3c8d 100644
@@ -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
#13 lore raw

PATCH v11 2/6 power: add PMD power management API and callback

In reply to Liang Ma

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.

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
index 0a3755a901..380e7aace7 100644
@@ -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
index e95e6aa7a5..0430fca9cc 100644
@@ -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
index 78c031c943..cc3c7a8646 100644
@@ -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
index 69ca9af616..3f2f6cd6f6 100644
@@ -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
#14 lore raw

PATCH v11 3/6 net/ixgbe: implement power management API

In reply to Liang Ma

Implement 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
index 00101c2eec..fcc4026372 100644
@@ -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
index 6cfbb582e2..db94b9d05d 100644
@@ -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
index 6d2f7c9da3..1ef0b05e66 100644
@@ -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
#15 lore raw

PATCH v11 4/6 net/i40e: implement power management API

In reply to Liang Ma

Implement 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
index 4778aaf299..358a38232b 100644
@@ -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
index 5df9a9df56..78862fe3a2 100644
@@ -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
index 57d7b4160b..5826cf1099 100644
@@ -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
#16 lore raw

PATCH v11 5/6 net/ice: implement power management API

In reply to Liang Ma

Implement 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
index c65125ff32..54f185ad4d 100644
@@ -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
index ee576c362a..fafd6ada62 100644
@@ -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
index 1c23c7541e..7eeb8d467e 100644
@@ -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
#17 lore raw

PATCH v11 6/6 examples/l3fwd-power: enable PMD power mgmt

In reply to Liang Ma

Add 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
index d7e1dc5813..149342d112 100644
@@ -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
#18 lore raw

v12 cover Add PMD power management

In reply to Liang Ma

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.

- 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
#19 lore raw

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
index a4a1bc1159..5e384d380e 100644
@@ -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
index dd520d90fa..67977bd511 100644
@@ -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
index 4ed03d521f..4cb5560c02 100644
@@ -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
index 354c068f31..31bf76ae81 100644
@@ -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
index e78f29002e..dfd42dee0c 100644
@@ -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
#20 lore raw

PATCH v12 2/11 eal: avoid invalid API usage in power intrinsics

In reply to Anatoly Burakov

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_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
index 67977bd511..ffa72f7578 100644
@@ -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
index 34c5fd9c3e..b48a54ec7f 100644
@@ -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 */
 	asm volatile(".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
#21 lore raw

PATCH v12 3/11 eal: change API of power intrinsics

In reply to Anatoly Burakov

Instead 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
index 0c95c4793d..d2f2026291 100644
@@ -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
index 86724863f2..c9a8a02278 100644
@@ -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
index 5e384d380e..76a5fa5234 100644
@@ -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
index 4cb5560c02..cff0996770 100644
@@ -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
index b48a54ec7f..3e224f5ac7 100644
@@ -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 */
 	asm volatile(".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 */
 	asm volatile(".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
#22 lore raw

PATCH v12 4/11 eal: remove sync version of power monitor

In reply to Anatoly Burakov

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.

---
 .../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
index 76a5fa5234..27869251a8 100644
@@ -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
index 00c670cb50..a6f1955996 100644
@@ -57,40 +57,6 @@ __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
- *
- * 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
index cff0996770..248d1f4a23 100644
@@ -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
index 31bf76ae81..20945b1efa 100644
@@ -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
#23 lore raw

PATCH v12 5/11 eal: add monitor wakeup function

In reply to Anatoly Burakov

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_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
index 27869251a8..39e49cc45b 100644
@@ -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
index a6f1955996..e311d6f8ea 100644
@@ -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 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
index 248d1f4a23..2e7db0e7eb 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
#24 lore raw

PATCH v12 6/11 ethdev: add simple power management API

In reply to 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.

---

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
index 638f98168b..feb3ff4f06 100644
@@ -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
index 17ddacc78d..58f68321ea 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
 int 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
index 0eacfd8425..ae4f152cf0 100644
@@ -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
index d3f5410806..a124e1e370 100644
@@ -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
#25 lore raw

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.

---

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
index 638f98168b..feb3ff4f06 100644

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 -0
 Removed Items
 -------------
lib/librte_ethdev/rte_ethdev.c +4 -0
index 17ddacc78d..58f68321ea 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
 int 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
index 0eacfd8425..ae4f152cf0 100644

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
index d3f5410806..a124e1e370 100644
@@ -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 {
#26 lore raw

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.

---

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 -0
  Removed Items
  -------------
lib/librte_ethdev/rte_ethdev.c +4 -0
index 17ddacc78d..58f68321ea 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
  int 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
index 0eacfd8425..ae4f152cf0 100644
@@ -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
index d3f5410806..a124e1e370 100644
@@ -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
#27 lore raw

Re: [dpdk-dev] [PATCH v12 06/11] ethdev: add simple power management API

In reply to Anatoly Burakov
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.

---

<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?

#28 lore raw

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.

---

<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_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?

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
#29 lore raw

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.

---

<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_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?

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.

#30 lore raw

PATCH v12 7/11 power: add PMD power management API and callback

In reply to Anatoly Burakov

From: 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
index 0a3755a901..02280dd689 100644
@@ -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
index feb3ff4f06..bf65000425 100644
@@ -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
index 4b4cf1b90b..51a471b669 100644
@@ -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
index 69ca9af616..61996b4d11 100644
@@ -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
#31 lore raw

PATCH v12 8/11 net/ixgbe: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a47a8b262..4b7a5ca60b 100644
@@ -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
index 6cfbb582e2..7e046a1819 100644
@@ -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
index 6d2f7c9da3..8a25e98df6 100644
@@ -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
#32 lore raw

PATCH v12 9/11 net/i40e: implement power management API

In reply to Anatoly Burakov

From: 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
index f54769c29d..af2577a140 100644
@@ -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
index 5df9a9df56..0b4220fc9c 100644
@@ -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
index 57d7b4160b..e1494525ce 100644
@@ -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
#33 lore raw

PATCH v12 10/11 net/ice: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a5d6a559f..c21682c120 100644
@@ -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
index 5fbd68eafc..fa9e9a235b 100644
@@ -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
index 6b16716063..906fbefdc4 100644
@@ -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
#34 lore raw

PATCH v12 11/11 examples/l3fwd-power: enable PMD power mgmt

In reply to Anatoly Burakov

From: 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
index 85a78a5c1e..aaa9367fae 100644
@@ -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
#35 lore raw

Re: [dpdk-dev] [PATCH v12 00/11] Add PMD power management

In reply to Anatoly Burakov
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

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
#36 lore raw

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:16082

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.

Signature · 2 lines
Thanks,
Anatoly
#37 lore raw

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 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

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
#38 lore raw

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
index d62875ebae..6ec53ea03a 100644
@@ -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
#39 lore raw

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
index 39e49cc45b..9e498e9ebf 100644
@@ -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
index d62875ebae..6ec53ea03a 100644
@@ -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
new file mode 100644
index 0000000000..998f9898ad
@@ -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
#40 lore raw

v13 cover Add PMD power management

In reply to 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. 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
#41 lore raw

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
index a4a1bc1159..5e384d380e 100644
@@ -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
index dd520d90fa..67977bd511 100644
@@ -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
index 4ed03d521f..4cb5560c02 100644
@@ -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
index 354c068f31..31bf76ae81 100644
@@ -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
index e78f29002e..dfd42dee0c 100644
@@ -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
Acked-by on v13 1/11
#42 lore raw

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.

---

Quoted 2 lines

--
2.25.1

#43 lore raw

PATCH v13 2/11 eal: avoid invalid API usage in power intrinsics

In reply to Anatoly Burakov

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_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
index 67977bd511..ffa72f7578 100644
@@ -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
index 34c5fd9c3e..b48a54ec7f 100644
@@ -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 */
 	asm volatile(".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
#44 lore raw

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_t wait_supported;

Since it is being used as a flag, bool is more common usage.

#45 lore raw

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_t wait_supported;

Since it is being used as a flag, bool is more common usage.

Will fix in v14, thanks!

Signature · 2 lines
Thanks,
Anatoly
Acked-by on v13 2/11
#46 lore raw

Re: [dpdk-dev] [PATCH v13 02/11] eal: avoid invalid API usage in power intrinsics

In reply to Anatoly Burakov
Quoted 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_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
index 67977bd511..ffa72f7578 100644
@@ -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
index 34c5fd9c3e..b48a54ec7f 100644
@@ -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 */
 	asm volatile(".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

#47 lore raw

PATCH v13 3/11 eal: change API of power intrinsics

In reply to Anatoly Burakov

Instead 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
index 0c95c4793d..d2f2026291 100644
@@ -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
index 86724863f2..c9a8a02278 100644
@@ -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
index 5e384d380e..76a5fa5234 100644
@@ -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
index 4cb5560c02..cff0996770 100644
@@ -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
index b48a54ec7f..3e224f5ac7 100644
@@ -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 */
 	asm volatile(".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 */
 	asm volatile(".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
#49 lore raw

PATCH v13 4/11 eal: remove sync version of power monitor

In reply to Anatoly Burakov

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.

---
 .../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
index 76a5fa5234..27869251a8 100644
@@ -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
index 00c670cb50..a6f1955996 100644
@@ -57,40 +57,6 @@ __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
- *
- * 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
index cff0996770..248d1f4a23 100644
@@ -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
index 31bf76ae81..20945b1efa 100644
@@ -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
Acked-by on v13 4/11
#50 lore raw

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.

---

Quoted 2 lines

--
2.25.1

#51 lore raw

PATCH v13 5/11 eal: add monitor wakeup function

In reply to Anatoly Burakov

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_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
index 27869251a8..39e49cc45b 100644
@@ -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
index a6f1955996..e311d6f8ea 100644
@@ -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 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
index 248d1f4a23..2e7db0e7eb 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
#52 lore raw

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_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
index 27869251a8..39e49cc45b 100644
@@ -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
index a6f1955996..e311d6f8ea 100644
@@ -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 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
index 248d1f4a23..2e7db0e7eb 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
index a9cd1afe9d..46a4fb6cd5 100644
@@ -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 */
 	asm volatile(".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

#53 lore raw

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_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
index 27869251a8..39e49cc45b 100644
@@ -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
index a6f1955996..e311d6f8ea 100644
@@ -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 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
index 248d1f4a23..2e7db0e7eb 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
index a9cd1afe9d..46a4fb6cd5 100644
@@ -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 before monitor()?
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 */
  asm volatile(".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
#54 lore raw

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_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
index 27869251a8..39e49cc45b 100644
@@ -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 +5 -0
index a6f1955996..e311d6f8ea 100644
@@ -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 calling rte_power_pause.
+ *
+ * @param lcore_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 notice

lib/librte_eal/ppc/include/rte_power_intrinsics.h +9 -0
index 248d1f4a23..2e7db0e7eb 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
index a9cd1afe9d..46a4fb6cd5 100644
@@ -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

lib/librte_eal/x86/rte_power_intrinsics.c +6 -0
@@ -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 +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 -0
   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 before monitor()?
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
#55 lore raw

PATCH v13 6/11 ethdev: add simple power management API

In reply to 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.

---

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
index 638f98168b..6de0cb568e 100644
@@ -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
index 17ddacc78d..e19dbd838b 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
 int 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
index 0eacfd8425..3b3b0ec1a0 100644
@@ -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
index d3f5410806..a124e1e370 100644
@@ -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
Acked-by on v13 6/11
#56 lore raw

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
#57 lore raw

PATCH v13 7/11 power: add PMD power management API and callback

In reply to Anatoly Burakov

From: 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
index 0a3755a901..02280dd689 100644
@@ -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
index 6de0cb568e..b34828cad6 100644
@@ -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
index 4b4cf1b90b..51a471b669 100644
@@ -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
index 69ca9af616..61996b4d11 100644
@@ -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
#58 lore raw

PATCH v13 8/11 net/ixgbe: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a47a8b262..4b7a5ca60b 100644
@@ -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
index 6cfbb582e2..7e046a1819 100644
@@ -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
index 6d2f7c9da3..8a25e98df6 100644
@@ -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
#59 lore raw

PATCH v13 9/11 net/i40e: implement power management API

In reply to Anatoly Burakov

From: 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
index f54769c29d..af2577a140 100644
@@ -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
index 5df9a9df56..0b4220fc9c 100644
@@ -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
index 57d7b4160b..e1494525ce 100644
@@ -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
#60 lore raw

PATCH v13 10/11 net/ice: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a5d6a559f..c21682c120 100644
@@ -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
index 5fbd68eafc..fa9e9a235b 100644
@@ -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
index 6b16716063..906fbefdc4 100644
@@ -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
#61 lore raw

PATCH v13 11/11 examples/l3fwd-power: enable PMD power mgmt

In reply to Anatoly Burakov

From: 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
index 85a78a5c1e..aaa9367fae 100644
@@ -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
#62 lore raw

v14 cover Add PMD power management

In reply to 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. 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
#63 lore raw

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
index a4a1bc1159..9e498e9ebf 100644
@@ -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
index d62875ebae..6ec53ea03a 100644
@@ -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
index dd520d90fa..67977bd511 100644
@@ -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
index 4ed03d521f..c0e9ac279f 100644
@@ -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
index f4b6d95c42..43c46542fb 100644
@@ -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
index 354c068f31..31bf76ae81 100644
@@ -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
index e78f29002e..dfd42dee0c 100644
@@ -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
#64 lore raw

PATCH v14 2/11 eal: avoid invalid API usage in power intrinsics

In reply to Anatoly Burakov

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_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
index 67977bd511..ffa72f7578 100644
@@ -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
index 34c5fd9c3e..050ae612a8 100644
@@ -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 */
 	asm volatile(".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
#65 lore raw

PATCH v14 3/11 eal: change API of power intrinsics

In reply to Anatoly Burakov

Instead 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
index 0c95c4793d..d2f2026291 100644
@@ -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
index 86724863f2..c9a8a02278 100644
@@ -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
index e5a49facb4..f2c3506b90 100644
@@ -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
index 785effabe6..3897d2024d 100644
@@ -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
index 050ae612a8..fd061e8e50 100644
@@ -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 */
 	asm volatile(".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 */
 	asm volatile(".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
#66 lore raw

PATCH v14 4/11 eal: remove sync version of power monitor

In reply to Anatoly Burakov

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.

---
 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
index f2c3506b90..6b8219b919 100644
@@ -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
index 00c670cb50..a6f1955996 100644
@@ -57,40 +57,6 @@ __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
- *
- * 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
index 3897d2024d..9a40c4d5d6 100644
@@ -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
index 31bf76ae81..20945b1efa 100644
@@ -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
#67 lore raw

PATCH v14 5/11 eal: add monitor wakeup function

In reply to Anatoly Burakov

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_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
index 6b8219b919..14081a2c5b 100644
@@ -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
index a6f1955996..e311d6f8ea 100644
@@ -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 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
index 9a40c4d5d6..a7db61a7c3 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
#68 lore raw

PATCH v14 6/11 ethdev: add simple power management API

In reply to 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.

---

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
index 638f98168b..6de0cb568e 100644
@@ -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
index 17ddacc78d..e19dbd838b 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
 int 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
index 0eacfd8425..3b3b0ec1a0 100644
@@ -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
index d3f5410806..a124e1e370 100644
@@ -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
#69 lore raw

PATCH v14 7/11 power: add PMD power management API and callback

In reply to Anatoly Burakov

From: 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
index 0a3755a901..02280dd689 100644
@@ -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
index 6de0cb568e..b34828cad6 100644
@@ -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
index 4b4cf1b90b..51a471b669 100644
@@ -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
index 69ca9af616..61996b4d11 100644
@@ -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
#70 lore raw

PATCH v14 8/11 net/ixgbe: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a47a8b262..4b7a5ca60b 100644
@@ -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
index 6cfbb582e2..7e046a1819 100644
@@ -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
index 6d2f7c9da3..8a25e98df6 100644
@@ -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
#71 lore raw

PATCH v14 9/11 net/i40e: implement power management API

In reply to Anatoly Burakov

From: 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
index f54769c29d..af2577a140 100644
@@ -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
index 5df9a9df56..0b4220fc9c 100644
@@ -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
index 57d7b4160b..e1494525ce 100644
@@ -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
#72 lore raw

PATCH v14 10/11 net/ice: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a5d6a559f..c21682c120 100644
@@ -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
index 5fbd68eafc..fa9e9a235b 100644
@@ -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
index 6b16716063..906fbefdc4 100644
@@ -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
#73 lore raw

PATCH v14 11/11 examples/l3fwd-power: enable PMD power mgmt

In reply to Anatoly Burakov

From: 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
index 85a78a5c1e..aaa9367fae 100644
@@ -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
#74 lore raw

v15 cover Add PMD power management

In reply to 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. 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
#75 lore raw

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
index a4a1bc1159..9e498e9ebf 100644
@@ -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
index d62875ebae..6ec53ea03a 100644
@@ -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
index dd520d90fa..67977bd511 100644
@@ -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
index 4ed03d521f..c0e9ac279f 100644
@@ -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
index f4b6d95c42..43c46542fb 100644
@@ -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
index 354c068f31..31bf76ae81 100644
@@ -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
index e78f29002e..dfd42dee0c 100644
@@ -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
#76 lore raw

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
new file mode 100644
index 0000000000..34c5fd9c3e
@@ -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
+	}
+}
+
#77 lore raw

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
new file mode 100644
index 0000000000..34c5fd9c3e
@@ -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
#78 lore raw

PATCH v15 2/11 eal: avoid invalid API usage in power intrinsics

In reply to Anatoly Burakov

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_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
index 67977bd511..ffa72f7578 100644
@@ -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
index 34c5fd9c3e..a164ad55fc 100644
@@ -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 */
 	asm volatile(".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
#79 lore raw

PATCH v15 3/11 eal: change API of power intrinsics

In reply to Anatoly Burakov

Instead 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
index 0c95c4793d..d2f2026291 100644
@@ -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
index 86724863f2..c9a8a02278 100644
@@ -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
index e5a49facb4..f2c3506b90 100644
@@ -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
index 785effabe6..3897d2024d 100644
@@ -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
index a164ad55fc..9b0638148d 100644
@@ -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 */
 	asm volatile(".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 */
 	asm volatile(".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
#80 lore raw

PATCH v15 4/11 eal: remove sync version of power monitor

In reply to Anatoly Burakov

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.

---
 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
index f2c3506b90..6b8219b919 100644
@@ -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
index 00c670cb50..a6f1955996 100644
@@ -57,40 +57,6 @@ __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
- *
- * 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
index 3897d2024d..9a40c4d5d6 100644
@@ -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
index 31bf76ae81..20945b1efa 100644
@@ -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
#81 lore raw

PATCH v15 5/11 eal: add monitor wakeup function

In reply to Anatoly Burakov

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_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
index 6b8219b919..14081a2c5b 100644
@@ -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
index a6f1955996..e311d6f8ea 100644
@@ -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 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
index 9a40c4d5d6..a7db61a7c3 100644
@@ -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
index 20945b1efa..ac026e289d 100644
@@ -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
#82 lore raw

PATCH v15 6/11 ethdev: add simple power management API

In reply to 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.

---

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
index 638f98168b..6de0cb568e 100644
@@ -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
index 17ddacc78d..e19dbd838b 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
 int 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
index 0eacfd8425..3b3b0ec1a0 100644
@@ -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
index d3f5410806..a124e1e370 100644
@@ -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
#83 lore raw

PATCH v15 7/11 power: add PMD power management API and callback

In reply to Anatoly Burakov

From: 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
index 0a3755a901..02280dd689 100644
@@ -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
index 6de0cb568e..b34828cad6 100644
@@ -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
index 4b4cf1b90b..51a471b669 100644
@@ -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
index 69ca9af616..61996b4d11 100644
@@ -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
#84 lore raw

PATCH v15 8/11 net/ixgbe: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a47a8b262..4b7a5ca60b 100644
@@ -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
index 6cfbb582e2..7e046a1819 100644
@@ -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
index 6d2f7c9da3..8a25e98df6 100644
@@ -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
#85 lore raw

PATCH v15 9/11 net/i40e: implement power management API

In reply to Anatoly Burakov

From: 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
index f54769c29d..af2577a140 100644
@@ -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
index 5df9a9df56..0b4220fc9c 100644
@@ -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
index 57d7b4160b..e1494525ce 100644
@@ -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
#86 lore raw

PATCH v15 10/11 net/ice: implement power management API

In reply to Anatoly Burakov

From: 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
index 9a5d6a559f..c21682c120 100644
@@ -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
index 5fbd68eafc..fa9e9a235b 100644
@@ -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
index 6b16716063..906fbefdc4 100644
@@ -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
#87 lore raw

PATCH v15 11/11 examples/l3fwd-power: enable PMD power mgmt

In reply to Anatoly Burakov

From: 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
index 85a78a5c1e..aaa9367fae 100644
@@ -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
#88 lore raw

v16 cover Add PMD power management

In reply to 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. 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
#89 lore raw

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
index a4a1bc1159..9e498e9ebf 100644
@@ -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
index d62875ebae..6ec53ea03a 100644
@@ -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
index dd520d90fa..67977bd511 100644
@@ -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
index 4ed03d521f..c0e9ac279f 100644
@@ -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
index f4b6d95c42..43c46542fb 100644
@@ -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
index b1db7ec795..32eceb8869 100644
@@ -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
index e78f29002e..dfd42dee0c 100644
@@ -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
#90 lore raw

PATCH v16 2/11 eal: avoid invalid API usage in power intrinsics

In reply to Anatoly Burakov

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_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
index ab1f44f611..7e7552fa8a 100644
@@ -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
index 67977bd511..37e4ec0414 100644
@@ -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
index 84340ca2a4..929e0611b0 100644
@@ -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
#91 lore raw

PATCH v16 3/11 eal: change API of power intrinsics

In reply to Anatoly Burakov

Instead 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
index 0c95c4793d..d2f2026291 100644
@@ -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
index 86724863f2..c9a8a02278 100644
@@ -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
index 7e7552fa8a..5f1caaf25b 100644
@@ -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
index 929e0611b0..5e5a1fff5a 100644
@@ -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
#93 lore raw

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.

---

Quoted 6 lines
Quoted 3 lines

Notes:
v16:
- Add error handling

There 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
#94 lore raw

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.

---

Quoted 6 lines
Quoted 3 lines

Notes:
v16:
- Add error handling

There 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.

#95 lore raw

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.

---

Quoted 6 lines
Quoted 3 lines

Notes:
v16:
- Add error handling

There 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_t x;
/**<
* 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
#96 lore raw

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.

---

Quoted 6 lines
Quoted 3 lines

Notes:
v16:
- Add error handling

There 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_t x;
/**<
* 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.

#97 lore raw

PATCH v16 4/11 eal: remove sync version of power monitor

In reply to Anatoly Burakov

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.

---
 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
index 5f1caaf25b..8d271dc0c1 100644
@@ -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
index 3ad53068d5..85343bc9eb 100644
@@ -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
index 5e5a1fff5a..f7862ea324 100644
@@ -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
index 32eceb8869..1fcd1d3bed 100644
@@ -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
#98 lore raw

PATCH v16 5/11 eal: add monitor wakeup function

In reply to Anatoly Burakov

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_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
index 8d271dc0c1..5a24c13913 100644
@@ -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
index 85343bc9eb..6109d28faa 100644
@@ -62,6 +62,22 @@ __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
+ *
+ * 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
index f7862ea324..7e334f7cf0 100644
@@ -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
index 1fcd1d3bed..fce90a112f 100644
@@ -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
Acked-by on v16 5/11
#99 lore raw

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_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

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

#100 lore raw

PATCH v16 6/11 ethdev: add simple power management API

In reply to 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.

---

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
index 706cbf8f0c..ec9958a141 100644
@@ -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
index 17ddacc78d..e19dbd838b 100644
@@ -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
index f5f8919186..ca0f91312e 100644
@@ -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_experimental
 int 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
index 0eacfd8425..3b3b0ec1a0 100644
@@ -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
index d3f5410806..a124e1e370 100644
@@ -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
Acked-by on v16 6/11
#101 lore raw

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.

---

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

#102 lore raw

PATCH v16 7/11 power: add PMD power management API and callback

In reply to Anatoly Burakov

From: 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
index 0a3755a901..02280dd689 100644
@@ -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
index ec9958a141..9cd8214e2d 100644
@@ -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
index 4b4cf1b90b..51a471b669 100644
@@ -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
index 69ca9af616..61996b4d11 100644
@@ -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
#103 lore raw

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 callback

From: 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

...

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
new file mode 100644
index 0000000000..0bfbc6ba69
@@ -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
index 69ca9af616..61996b4d11 100644
@@ -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

#104 lore raw

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 callback

From: 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

...

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.

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
#105 lore raw

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 callback

From: 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

...

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 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.

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:

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" 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!

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
#106 lore raw

PATCH v16 8/11 net/ixgbe: implement power management API

In reply to Anatoly Burakov

From: 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
index d7a1806ab8..97acf35d24 100644
@@ -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
index 7bb8460359..cc8f70e6dd 100644
@@ -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
index 6d2f7c9da3..8a25e98df6 100644
@@ -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
#107 lore raw

PATCH v16 9/11 net/i40e: implement power management API

In reply to Anatoly Burakov

From: 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
index 14622484a0..ba1abc584f 100644
@@ -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
index 5df9a9df56..0b4220fc9c 100644
@@ -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
index 57d7b4160b..e1494525ce 100644
@@ -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
#108 lore raw

PATCH v16 10/11 net/ice: implement power management API

In reply to Anatoly Burakov

From: 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
index 587f485ee3..38c6263946 100644
@@ -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
index d052bd0f1b..066651dc48 100644
@@ -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
index 6b16716063..906fbefdc4 100644
@@ -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