Skip to contentSkip to conversation

Enhancements for PMD power management

PATCH Superseded Anatoly Burakov 8 participants 156 messages Jun 1, 2021 – Aug 4, 2021 Page 2 of 2

Subsystems (2)
lore
Share & tools
lore
#89 lore raw

PATCH v6 3/7 eal: add power monitor for multiple events

In reply to Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

 doc/guides/rel_notes/release_21_08.rst        |  2 +
 lib/eal/arm/rte_power_intrinsics.c            | 11 +++
 lib/eal/include/generic/rte_cpuflags.h        |  2 +
 .../include/generic/rte_power_intrinsics.h    | 35 +++++++++
 lib/eal/ppc/rte_power_intrinsics.c            | 11 +++
 lib/eal/version.map                           |  3 +
 lib/eal/x86/rte_cpuflags.c                    |  2 +
 lib/eal/x86/rte_power_intrinsics.c            | 73 +++++++++++++++++++
 8 files changed, 139 insertions(+)
doc/guides/rel_notes/release_21_08.rst +2 -0
index c84ac280f5..9d1cfac395 100644
@@ -55,6 +55,8 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* eal: added ``rte_power_monitor_multi`` to support waiting for multiple events.
+
 
 Removed Items
 -------------
lib/eal/arm/rte_power_intrinsics.c +11 -0
index e83f04072a..78f55b7203 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/include/generic/rte_cpuflags.h +2 -0
index 28a5aecde8..d35551e931 100644
@@ -24,6 +24,8 @@ struct rte_cpu_intrinsics {
 	/**< indicates support for rte_power_monitor function */
 	uint32_t power_pause : 1;
 	/**< indicates support for rte_power_pause function */
+	uint32_t power_monitor_multi : 1;
+	/**< indicates support for rte_power_monitor_multi function */
 };
 
 /**
lib/eal/include/generic/rte_power_intrinsics.h +35 -0
index c9aa52a86d..04e8c2ab37 100644
@@ -128,4 +128,39 @@ int rte_power_monitor_wakeup(const unsigned int lcore_id);
 __rte_experimental
 int rte_power_pause(const uint64_t tsc_timestamp);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Monitor a set of addresses for changes. This will cause the CPU to enter an
+ * architecture-defined optimized power state until either one of the specified
+ * memory addresses is written to, a certain TSC timestamp is reached, or other
+ * reasons cause the CPU to wake up.
+ *
+ * Additionally, `expected` 64-bit values and 64-bit masks 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 do not match, the entering of
+ * optimized power state may be aborted.
+ *
+ * @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 pmc
+ *   An array of monitoring condition structures.
+ * @param num
+ *   Length of the `pmc` array.
+ * @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
+int rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp);
+
 #endif /* _RTE_POWER_INTRINSIC_H_ */
lib/eal/ppc/rte_power_intrinsics.c +11 -0
index 7fc9586da7..f00b58ade5 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/version.map +3 -0
index fe5c3dac98..4ccd5475d6 100644
@@ -423,6 +423,9 @@ EXPERIMENTAL {
 	rte_version_release; # WINDOWS_NO_EXPORT
 	rte_version_suffix; # WINDOWS_NO_EXPORT
 	rte_version_year; # WINDOWS_NO_EXPORT
+
+	# added in 21.08
+	rte_power_monitor_multi; # WINDOWS_NO_EXPORT
 };
 
 INTERNAL {
lib/eal/x86/rte_cpuflags.c +2 -0
index a96312ff7f..d339734a8c 100644
@@ -189,5 +189,7 @@ rte_cpu_get_intrinsics_support(struct rte_cpu_intrinsics *intrinsics)
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_WAITPKG)) {
 		intrinsics->power_monitor = 1;
 		intrinsics->power_pause = 1;
+		if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_RTM))
+			intrinsics->power_monitor_multi = 1;
 	}
 }
lib/eal/x86/rte_power_intrinsics.c +73 -0

100 lines not on this page · raw message

Signature · 1 line
2.25.1
Acked-by on v6 3/7
#90 lore raw

Re: [dpdk-dev] [PATCH v6 3/7] eal: add power monitor for multiple events

Quoted 19 lines · Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

Quoted 10 lines
 doc/guides/rel_notes/release_21_08.rst        |  2 +
 lib/eal/arm/rte_power_intrinsics.c            | 11 +++
 lib/eal/include/generic/rte_cpuflags.h        |  2 +
 .../include/generic/rte_power_intrinsics.h    | 35 +++++++++
 lib/eal/ppc/rte_power_intrinsics.c            | 11 +++
 lib/eal/version.map                           |  3 +
 lib/eal/x86/rte_cpuflags.c                    |  2 +
 lib/eal/x86/rte_power_intrinsics.c            | 73 +++++++++++++++++++
 8 files changed, 139 insertions(+)
#91 lore raw

PATCH v6 4/7 power: remove thread safety from PMD power API's

In reply to Anatoly Burakov

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

---

Notes:
v2:
- Add check for stopped queue
- Clarified doc message
- Added release notes

 doc/guides/rel_notes/release_21_08.rst |   5 +
 lib/power/meson.build                  |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 133 ++++++++++---------------
 lib/power/rte_power_pmd_mgmt.h         |   6 ++
 4 files changed, 67 insertions(+), 80 deletions(-)
doc/guides/rel_notes/release_21_08.rst +5 -0
index 9d1cfac395..f015c509fc 100644
@@ -88,6 +88,11 @@ API Changes
 
 * eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
 
+* rte_power: The experimental PMD power management API is no longer considered
+  to be thread safe; all Rx queues affected by the API will now need to be
+  stopped before making any changes to the power management scheme.
+
+
 ABI Changes
 -----------
 
lib/power/meson.build +3 -0
index c1097d32f1..4f6a242364 100644
@@ -21,4 +21,7 @@ headers = files(
         'rte_power_pmd_mgmt.h',
         'rte_power_guest_channel.h',
 )
+if cc.has_argument('-Wno-cast-qual')
+    cflags += '-Wno-cast-qual'
+endif
 deps += ['timer', 'ethdev']
lib/power/rte_power_pmd_mgmt.c +53 -80

209 lines not on this page · raw message

lib/power/rte_power_pmd_mgmt.h +6 -0
index 7a0ac24625..444e7b8a66 100644
@@ -43,6 +43,9 @@ enum rte_power_pmd_mgmt_type {
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue will be polled from.
  * @param port_id
@@ -69,6 +72,9 @@ rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id,
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue is polled from.
  * @param port_id
Signature · 1 line
2.25.1
Acked-by on v6 4/7
#92 lore raw

Re: [dpdk-dev] [PATCH v6 4/7] power: remove thread safety from PMD power API's

Quoted 7 lines

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

Quoted 9 lines

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

---

Quoted 1 line

2.25.1

#93 lore raw

PATCH v6 5/7 power: support callbacks for multiple Rx queues

In reply to Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

 doc/guides/nics/features.rst           |  10 +
 doc/guides/prog_guide/power_man.rst    |  65 ++--
 doc/guides/rel_notes/release_21_08.rst |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 452 +++++++++++++++++++------
 4 files changed, 394 insertions(+), 136 deletions(-)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
doc/guides/prog_guide/power_man.rst +37 -28

74 lines not on this page · raw message

doc/guides/rel_notes/release_21_08.rst +3 -0
index f015c509fc..3926d45ef8 100644
@@ -57,6 +57,9 @@ New Features
 
 * eal: added ``rte_power_monitor_multi`` to support waiting for multiple events.
 
+* rte_power: The experimental PMD power management API now supports managing
+  multiple Ethernet Rx queues per lcore.
+
 
 Removed Items
 -------------
lib/power/rte_power_pmd_mgmt.c +344 -108

567 lines not on this page · raw message

Signature · 1 line
2.25.1
#94 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 144 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

 doc/guides/nics/features.rst           |  10 +
 doc/guides/prog_guide/power_man.rst    |  65 ++--
 doc/guides/rel_notes/release_21_08.rst |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 452 +++++++++++++++++++------
 4 files changed, 394 insertions(+), 136 deletions(-)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
doc/guides/prog_guide/power_man.rst +35 -28
index c70ae128ac..ec04a72108 100644
@@ -198,34 +198,41 @@ Ethernet 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 ``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).
+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
+   monitor the Ethernet PMD RX descriptor address, waking the CPU up whenever
+   there's new traffic. Support for this scheme may not be available on all
+   platforms, and further limitations may apply (see below).
+
+* 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 supported by the underlying platform, use ``rte_pause()``.
+
+* Frequency scaling
+   This power saving scheme will use ``librte_power`` library functionality to
+   scale the core frequency up/down depending on traffic volume.
+
+The "monitor" mode is only supported in the following configurations and scenarios:
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor()`` is supported by the platform, then monitoring will be
+  limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
+  monitored from a different lcore).
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
+  ``rte_power_monitor()`` function is not supported, then monitor mode will not
+  be supported.
+
+* Not all Ethernet drivers support monitoring, even if the underlying
+  platform may support the necessary CPU instructions. Please refer to
+  :doc:`../nics/overview` for more information.
+

....

Quoted 29 lines
doc/guides/prog_guide/power_man.rst +29 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+	const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+	/* reset empty poll counter for this queue */
+	qcfg->n_empty_polls = 0;
+	/* reset the queue sleep counter as well */
+	qcfg->n_sleeps = 0;
+	/* remove the queue from list of cores ready to sleep */
+	if (is_ready_to_sleep)
+		cfg->n_queues_ready_to_sleep--;
+	/*
+	 * no need change the lcore sleep target counter because this lcore will
+	 * reach the n_sleeps anyway, and the other cores are already counted so
+	 * there's no need to do anything else.
+	 */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+	/* this function is called - that means we have an empty poll */
+	qcfg->n_empty_polls++;
+
+	/* if we haven't reached threshold for empty polls, we can't sleep */
+	if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+		return false;
+

doc/guides/prog_guide/power_man.rst

Quoted 10 lines
doc/guides/prog_guide/power_man.rst +10 -0
+	/*
+	 * we've reached a point where we are able to sleep, but we still need
+	 * to check if this queue has already been marked for sleeping.
+	 */
+	if (qcfg->n_sleeps == cfg->sleep_target)
+		return true;
+
+	/* mark this queue as ready for sleep */
+	qcfg->n_sleeps = cfg->sleep_target;
+	cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ...
OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; ... ?

My initial thought was 2) but might be the intention is 1)?

Quoted 52 lines
doc/guides/prog_guide/power_man.rst +25 -10
+
+	return true;
+}
+
+static inline bool
+lcore_can_sleep(struct pmd_core_cfg *cfg)
+{
+	/* are all queues ready to sleep? */
+	if (cfg->n_queues_ready_to_sleep != cfg->n_queues)
+		return false;
+
+	/* we've reached an iteration where we can sleep, reset sleep counter */
+	cfg->n_queues_ready_to_sleep = 0;
+	cfg->sleep_target++;
+
+	return true;
+}
+
 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)
+		uint16_t nb_rx, uint16_t max_pkts __rte_unused, void *arg)
 {
+	struct queue_list_entry *queue_conf = arg;
 
-	struct pmd_queue_cfg *q_conf;
-
-	q_conf = &port_cfg[port_id][qidx];
-
+	/* this callback can't do more than one queue, omit multiqueue logic */
 	if (unlikely(nb_rx == 0)) {
-		q_conf->empty_poll_stats++;
-		if (unlikely(q_conf->empty_poll_stats > EMPTYPOLL_MAX)) {
+		queue_conf->n_empty_polls++;
+		if (unlikely(queue_conf->n_empty_polls > EMPTYPOLL_MAX)) {
 			struct rte_power_monitor_cond pmc;
-			uint16_t ret;
+			int ret;
 
 			/* use monitoring condition to sleep */
 			ret = rte_eth_get_monitor_addr(port_id, qidx,
@@ -97,60 +231,77 @@ clb_umwait(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts __rte_unused,
 				rte_power_monitor(&pmc, UINT64_MAX);
 		}
 	} else
-		q_conf->empty_poll_stats = 0;
+		queue_conf->n_empty_polls = 0;
 
 	return nb_rx;
 }
 
#95 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 46 lines · Konstantin
Quoted 46 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

<snip>

Quoted 47 lines

....

Quoted 29 lines
lib/power/rte_power_pmd_mgmt.c +29 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+     /* reset empty poll counter for this queue */
+     qcfg->n_empty_polls = 0;
+     /* reset the queue sleep counter as well */
+     qcfg->n_sleeps = 0;
+     /* remove the queue from list of cores ready to sleep */
+     if (is_ready_to_sleep)
+             cfg->n_queues_ready_to_sleep--;
+     /*
+      * no need change the lcore sleep target counter because this lcore will
+      * reach the n_sleeps anyway, and the other cores are already counted so
+      * there's no need to do anything else.
+      */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     /* this function is called - that means we have an empty poll */
+     qcfg->n_empty_polls++;
+
+     /* if we haven't reached threshold for empty polls, we can't sleep */
+     if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+             return false;
+

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 195

Quoted 10 lines
lib/power/rte_power_pmd_mgmt.c +10 -0
+     /*
+      * we've reached a point where we are able to sleep, but we still need
+      * to check if this queue has already been marked for sleeping.
+      */
+     if (qcfg->n_sleeps == cfg->sleep_target)
+             return true;
+
+     /* mark this queue as ready for sleep */
+     qcfg->n_sleeps = cfg->sleep_target;
+     cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ...
OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; ... ?

Quoted 1 line

My initial thought was 2) but might be the intention is 1)?

The intent is 1), not 2). There's no need to wait for more empty polls once we pass the threshold - we keep sleeping until there's traffic.

Signature · 2 lines
Thanks,
Anatoly
#96 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 94 lines
Quoted 46 lines
Quoted 46 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

<snip>

Quoted 37 lines

....

Quoted 36 lines
lib/power/rte_power_pmd_mgmt.c +36 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+     /* reset empty poll counter for this queue */
+     qcfg->n_empty_polls = 0;
+     /* reset the queue sleep counter as well */
+     qcfg->n_sleeps = 0;
+     /* remove the queue from list of cores ready to sleep */
+     if (is_ready_to_sleep)
+             cfg->n_queues_ready_to_sleep--;
+     /*
+      * no need change the lcore sleep target counter because this lcore will
+      * reach the n_sleeps anyway, and the other cores are already counted so
+      * there's no need to do anything else.
+      */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     /* this function is called - that means we have an empty poll */
+     qcfg->n_empty_polls++;
+
+     /* if we haven't reached threshold for empty polls, we can't sleep */
+     if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+             return false;
+
+     /*
+      * we've reached a point where we are able to sleep, but we still need
+      * to check if this queue has already been marked for sleeping.
+      */
+     if (qcfg->n_sleeps == cfg->sleep_target)
+             return true;
+
Quoted 8 lines

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 195

Quoted 3 lines
lib/power/rte_power_pmd_mgmt.c +3 -0
+     /* mark this queue as ready for sleep */
+     qcfg->n_sleeps = cfg->sleep_target;
+     cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ... OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times=

Quoted 9 lines

EMPTYPOLL_MAX); sleep; ...

Quoted 3 lines

?

My initial thought was 2) but might be the intention is 1)?

The intent is 1), not 2). There's no need to wait for more empty polls once we pass the threshold - we keep sleeping until there's traffic.

Ok, then:

Probably worth to put extra explanation here on in the doc, to help people avoid wrong assumptions😉

#97 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 104 lines · Konstantin
Quoted 94 lines
Quoted 46 lines
Quoted 46 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

<snip>

Quoted 37 lines

....

Quoted 36 lines
lib/power/rte_power_pmd_mgmt.c +36 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+     /* reset empty poll counter for this queue */
+     qcfg->n_empty_polls = 0;
+     /* reset the queue sleep counter as well */
+     qcfg->n_sleeps = 0;
+     /* remove the queue from list of cores ready to sleep */
+     if (is_ready_to_sleep)
+             cfg->n_queues_ready_to_sleep--;
+     /*
+      * no need change the lcore sleep target counter because this lcore will
+      * reach the n_sleeps anyway, and the other cores are already counted so
+      * there's no need to do anything else.
+      */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     /* this function is called - that means we have an empty poll */
+     qcfg->n_empty_polls++;
+
+     /* if we haven't reached threshold for empty polls, we can't sleep */
+     if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+             return false;
+
+     /*
+      * we've reached a point where we are able to sleep, but we still need
+      * to check if this queue has already been marked for sleeping.
+      */
+     if (qcfg->n_sleeps == cfg->sleep_target)
+             return true;
+
Quoted 8 lines

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 195

Quoted 3 lines
lib/power/rte_power_pmd_mgmt.c +3 -0
+     /* mark this queue as ready for sleep */
+     qcfg->n_sleeps = cfg->sleep_target;
+     cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ... OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times=

Quoted 9 lines

EMPTYPOLL_MAX); sleep; ...

Quoted 3 lines

?

My initial thought was 2) but might be the intention is 1)?

The intent is 1), not 2). There's no need to wait for more empty polls once we pass the threshold - we keep sleeping until there's traffic.

Quoted 6 lines

Ok, then:

Probably worth to put extra explanation here on in the doc, to help people avoid wrong assumptions😉

I don't see value in going into such details. What would be the point? Like, what difference would this information make to anyone?

Signature · 2 lines
Thanks,
Anatoly
#98 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 117 lines

-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Wednesday, July 7, 2021 12:54 PM
To: Ananyev, Konstantin <[email protected]>; [email protected]; Hunt, David <[email protected]>
Cc: Loftus, Ciara <[email protected]>
Subject: Re: [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 107 lines · Konstantin
Quoted 94 lines
Quoted 46 lines
Quoted 46 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

<snip>

Quoted 37 lines

....

Quoted 36 lines
lib/power/rte_power_pmd_mgmt.c +36 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+     /* reset empty poll counter for this queue */
+     qcfg->n_empty_polls = 0;
+     /* reset the queue sleep counter as well */
+     qcfg->n_sleeps = 0;
+     /* remove the queue from list of cores ready to sleep */
+     if (is_ready_to_sleep)
+             cfg->n_queues_ready_to_sleep--;
+     /*
+      * no need change the lcore sleep target counter because this lcore will
+      * reach the n_sleeps anyway, and the other cores are already counted so
+      * there's no need to do anything else.
+      */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     /* this function is called - that means we have an empty poll */
+     qcfg->n_empty_polls++;
+
+     /* if we haven't reached threshold for empty polls, we can't sleep */
+     if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+             return false;
+
+     /*
+      * we've reached a point where we are able to sleep, but we still need
+      * to check if this queue has already been marked for sleeping.
+      */
+     if (qcfg->n_sleeps == cfg->sleep_target)
+             return true;
+
Quoted 8 lines

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 195

Quoted 3 lines
lib/power/rte_power_pmd_mgmt.c +3 -0
+     /* mark this queue as ready for sleep */
+     qcfg->n_sleeps = cfg->sleep_target;
+     cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ... OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times=

Quoted 9 lines

EMPTYPOLL_MAX); sleep; ...

Quoted 3 lines

?

My initial thought was 2) but might be the intention is 1)?

The intent is 1), not 2). There's no need to wait for more empty polls once we pass the threshold - we keep sleeping until there's traffic.

Ok, then:

Quoted 6 lines
Quoted 3 lines

Probably worth to put extra explanation here on in the doc, to help people avoid wrong assumptions😉

I don't see value in going into such details. What would be the point? Like, what difference would this information make to anyone?

I thought it is obvious: if you put extra explanation into the code, then it would be easier for anyone who reads it (reviewers/maintainers/users) to understand what it supposed to do.

#99 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 121 lines · Konstantin
Quoted 121 lines

-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Wednesday, July 7, 2021 12:54 PM
To: Ananyev, Konstantin <[email protected]>; [email protected]; Hunt, David <[email protected]>
Cc: Loftus, Ciara <[email protected]>
Subject: Re: [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 110 lines · Konstantin
Quoted 94 lines
Quoted 46 lines
Quoted 46 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

<snip>

Quoted 37 lines

....

Quoted 36 lines
lib/power/rte_power_pmd_mgmt.c +36 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+     /* reset empty poll counter for this queue */
+     qcfg->n_empty_polls = 0;
+     /* reset the queue sleep counter as well */
+     qcfg->n_sleeps = 0;
+     /* remove the queue from list of cores ready to sleep */
+     if (is_ready_to_sleep)
+             cfg->n_queues_ready_to_sleep--;
+     /*
+      * no need change the lcore sleep target counter because this lcore will
+      * reach the n_sleeps anyway, and the other cores are already counted so
+      * there's no need to do anything else.
+      */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     /* this function is called - that means we have an empty poll */
+     qcfg->n_empty_polls++;
+
+     /* if we haven't reached threshold for empty polls, we can't sleep */
+     if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+             return false;
+
+     /*
+      * we've reached a point where we are able to sleep, but we still need
+      * to check if this queue has already been marked for sleeping.
+      */
+     if (qcfg->n_sleeps == cfg->sleep_target)
+             return true;
+
Quoted 8 lines

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 195

Quoted 3 lines
lib/power/rte_power_pmd_mgmt.c +3 -0
+     /* mark this queue as ready for sleep */
+     qcfg->n_sleeps = cfg->sleep_target;
+     cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ... OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times=

Quoted 9 lines

EMPTYPOLL_MAX); sleep; ...

Quoted 3 lines

?

My initial thought was 2) but might be the intention is 1)?

The intent is 1), not 2). There's no need to wait for more empty polls once we pass the threshold - we keep sleeping until there's traffic.

Ok, then:

Probably worth to put extra explanation here on in the doc, to help people avoid wrong assumptions😉

Quoted 7 lines
Quoted 2 lines

I don't see value in going into such details. What would be the point? Like, what difference would this information make to anyone?

I thought it is obvious: if you put extra explanation into the code, then it would be easier for anyone who reads it (reviewers/maintainers/users) to understand what it supposed to do.

You're suggesting to put this *in the doc*, which implies that *the user* will find this information useful. I'm OK with adding this info as a comment somewhere perhaps, but why put it in the doc?

Signature · 2 lines
Thanks,
Anatoly
#100 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 115 lines
Quoted 115 lines
Quoted 114 lines
Quoted 105 lines
Quoted 95 lines
Quoted 47 lines
Quoted 47 lines

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

<snip>

Quoted 37 lines

....

Quoted 36 lines
(quoted diff) +36 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+     /* reset empty poll counter for this queue */
+     qcfg->n_empty_polls = 0;
+     /* reset the queue sleep counter as well */
+     qcfg->n_sleeps = 0;
+     /* remove the queue from list of cores ready to sleep */
+     if (is_ready_to_sleep)
+             cfg->n_queues_ready_to_sleep--;
+     /*
+      * no need change the lcore sleep target counter because this lcore will
+      * reach the n_sleeps anyway, and the other cores are already counted so
+      * there's no need to do anything else.
+      */
+}
+
+static inline bool
+queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+     /* this function is called - that means we have an empty poll */
+     qcfg->n_empty_polls++;
+
+     /* if we haven't reached threshold for empty polls, we can't sleep */
+     if (qcfg->n_empty_polls <= EMPTYPOLL_MAX)
+             return false;
+
+     /*
+      * we've reached a point where we are able to sleep, but we still need
+      * to check if this queue has already been marked for sleeping.
+      */
+     if (qcfg->n_sleeps == cfg->sleep_target)
+             return true;
+
Quoted 8 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+     /* mark this queue as ready for sleep */
+     qcfg->n_sleeps = cfg->sleep_target;
+     cfg->n_queues_ready_to_sleep++;

So, assuming there is no incoming traffic, should it be:
1) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times=1); sleep; poll_all_queues(times=1); sleep; ... OR
2) poll_all_queues(times=EMPTYPOLL_MAX); sleep; poll_all_queues(times= EMPTYPOLL_MAX); sleep; poll_all_queues(times=

Quoted 9 lines

EMPTYPOLL_MAX); sleep; ...

Quoted 3 lines

?

My initial thought was 2) but might be the intention is 1)?

The intent is 1), not 2). There's no need to wait for more empty polls once we pass the threshold - we keep sleeping until there's traffic.

Quoted 6 lines

Ok, then:

Probably worth to put extra explanation here on in the doc, to help people avoid wrong assumptions😉

I don't see value in going into such details. What would be the point? Like, what difference would this information make to anyone?

Quoted 8 lines
Quoted 4 lines

I thought it is obvious: if you put extra explanation into the code, then it would be easier for anyone who reads it (reviewers/maintainers/users) to understand what it supposed to do.

You're suggesting to put this *in the doc*, which implies that *the user* will find this information useful. I'm OK with adding this info as a comment somewhere perhaps, but why put it in the doc?

I don't really mind where you'll put it, either extra comments in that file, or few lines in the doc - both are ok to me.

#101 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

In reply to Anatoly Burakov
Quoted 45 lines · Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

Quoted 6 lines
  doc/guides/nics/features.rst           |  10 +
  doc/guides/prog_guide/power_man.rst    |  65 ++--
  doc/guides/rel_notes/release_21_08.rst |   3 +
  lib/power/rte_power_pmd_mgmt.c         | 452 +++++++++++++++++++------
  4 files changed, 394 insertions(+), 136 deletions(-)

--snip--

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 168

Quoted 13 lines
lib/power/rte_power_pmd_mgmt.c +12 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+	const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+	/* reset empty poll counter for this queue */
+	qcfg->n_empty_polls = 0;
+	/* reset the queue sleep counter as well */
+	qcfg->n_sleeps = 0;
+	/* remove the queue from list of cores ready to sleep */
+	if (is_ready_to_sleep)
+		cfg->n_queues_ready_to_sleep--;

Hi Anatoly,

   I don't think the logic around this is bulletproof yet, in my testing I'm seeing n_queues_ready_to_sleep wrap around (i.e. decremented while already zero).

Rgds,
Dave.

--snip--

#102 lore raw

Re: [dpdk-dev] [PATCH v6 5/7] power: support callbacks for multiple Rx queues

Quoted 70 lines · David Hunt
Quoted 42 lines · Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
     v6:
     - Track each individual queue sleep status (Konstantin)
     - Fix segfault (Dave)
     v5:
     - Remove the "power save queue" API and replace it with mechanism suggested by Konstantin
     v3:
     - Move the list of supported NICs to NIC feature table
     v2:
     - Use a TAILQ for queues instead of a static array
     - Address feedback from Konstantin
     - Add additional checks for stopped queues

Quoted 6 lines

  doc/guides/nics/features.rst           |  10 + doc/guides/prog_guide/power_man.rst    |  65 ++-- doc/guides/rel_notes/release_21_08.rst |   3 + lib/power/rte_power_pmd_mgmt.c         | 452 +++++++++++++++++++------

  4 files changed, 394 insertions(+), 136 deletions(-)

--snip--

lib/power/rte_power_pmd_mgmt.c calc_tsc(void) · line 168

Quoted 12 lines
lib/power/rte_power_pmd_mgmt.c +12 -0
+static inline void
+queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
+{
+    const bool is_ready_to_sleep = qcfg->n_empty_polls > EMPTYPOLL_MAX;
+
+    /* reset empty poll counter for this queue */
+    qcfg->n_empty_polls = 0;
+    /* reset the queue sleep counter as well */
+    qcfg->n_sleeps = 0;
+    /* remove the queue from list of cores ready to sleep */
+    if (is_ready_to_sleep)
+        cfg->n_queues_ready_to_sleep--;

Hi Anatoly,

Quoted 8 lines

   I don't think the logic around this is bulletproof yet, in my
testing I'm seeing n_queues_ready_to_sleep wrap around (i.e. decremented while already zero).

Rgds,
Dave.

--snip--

Thanks for your testing!

It seems that number of empty polls is not a reliable indicator of whether the queue is ready to sleep, because if we get a non-empty poll right after sleep, we'll have empty poll counter still at high value, which will cause the n_queues_ready_to_sleep to decrement, even though it's at zero because we just had a sleep.

Using n_sleeps and sleep_target is better in this case. I'll submit a v7 with this fix. Thanks!

Signature · 2 lines
Thanks,
Anatoly
#103 lore raw

PATCH v6 6/7 power: support monitoring multiple Rx queues

In reply to Anatoly Burakov

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Notes:
v6:
- Fix the missed feedback from v5

v4:
- Fix possible out of bounds access
- Added missing index increment

 doc/guides/prog_guide/power_man.rst |  9 ++--
 lib/power/rte_power_pmd_mgmt.c      | 82 ++++++++++++++++++++++++++++-
 2 files changed, 86 insertions(+), 5 deletions(-)
doc/guides/prog_guide/power_man.rst +6 -3
index ec04a72108..94353ca012 100644
@@ -221,13 +221,16 @@ power saving whenever empty poll count reaches a certain number.
 The "monitor" mode is only supported in the following configurations and scenarios:
 
 * If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor_multi()`` function is supported by the platform, then
+  monitoring multiple Ethernet Rx queues for traffic will be supported.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that only
   ``rte_power_monitor()`` is supported by the platform, then monitoring will be
   limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
   monitored from a different lcore).
 
-* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
-  ``rte_power_monitor()`` function is not supported, then monitor mode will not
-  be supported.
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that neither of the
+  two monitoring functions are supported, then monitor mode will not be supported.
 
 * Not all Ethernet drivers support monitoring, even if the underlying
   platform may support the necessary CPU instructions. Please refer to
lib/power/rte_power_pmd_mgmt.c +80 -2

119 lines not on this page · raw message

Signature · 1 line
2.25.1
Acked-by on v6 6/7
#104 lore raw

Re: [dpdk-dev] [PATCH v6 6/7] power: support monitoring multiple Rx queues

Quoted 9 lines

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Quoted 2 lines

--
2.25.1

#105 lore raw

PATCH v6 7/7 l3fwd-power: support multiqueue in PMD pmgmt modes

In reply to Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

---
 examples/l3fwd-power/main.c | 6 ------
 1 file changed, 6 deletions(-)
examples/l3fwd-power/main.c +0 -6
index f8dfed1634..52f56dc405 100644
@@ -2723,12 +2723,6 @@ main(int argc, char **argv)
 		printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
 		fflush(stdout);
 
-		/* PMD power management mode can only do 1 queue per core */
-		if (app_mode == APP_MODE_PMD_MGMT && qconf->n_rx_queue > 1) {
-			rte_exit(EXIT_FAILURE,
-				"In PMD power management mode, only one queue per lcore is allowed\n");
-		}
-
 		/* init RX queues */
 		for(queue = 0; queue < qconf->n_rx_queue; ++queue) {
 			struct rte_eth_rxconf rxq_conf;
Signature · 1 line
2.25.1
#106 lore raw

v7 cover Enhancements for PMD power management

In reply to Anatoly Burakov

This patchset introduces several changes related to PMD power management:

- Changed monitoring intrinsics to use callbacks as a comparison function, based on previous patchset [1] but incorporating feedback [2] - this hopefully will make it possible to add support for .get_monitor_addr in virtio
- Add a new intrinsic to monitor multiple addresses, based on RTM instruction set and the TPAUSE instruction
- Add support for PMD power management on multiple queues, as well as all accompanying infrastructure and example apps changes

v7:
- Fixed various bugs

v6:
- Improved the algorithm for multi-queue sleep
- Fixed segfault and addressed other feedback

v5:
- Removed "power save queue" API and replaced with mechanism suggested by Konstantin
- Addressed other feedback

v4:
- Replaced raw number with a macro
- Fixed all the bugs found by Konstantin
- Some other minor corrections

v3:
- Moved some doc updates to NIC features list

v2:
- Changed check inversion to callbacks
- Addressed feedback from Konstantin
- Added doc updates where necessary

[1] http://patches.dpdk.org/project/dpdk/list/?series=16930&state=*
[2] http://patches.dpdk.org/project/dpdk/patch/819ef1ace187365a615d3383e54579e3d9fb216e.1620747068.git.anatoly.burakov@intel.com/#133274

Anatoly Burakov (7):
power_intrinsics: use callbacks for comparison net/af_xdp: add power monitor support
eal: add power monitor for multiple events
power: remove thread safety from PMD power API's
power: support callbacks for multiple Rx queues
power: support monitoring multiple Rx queues
l3fwd-power: support multiqueue in PMD pmgmt modes

 doc/guides/nics/features.rst                  |  10 +
 doc/guides/prog_guide/power_man.rst           |  74 +-
 doc/guides/rel_notes/release_21_08.rst        |   9 +
 drivers/event/dlb2/dlb2.c                     |  17 +-
 drivers/net/af_xdp/rte_eth_af_xdp.c           |  34 +
 drivers/net/i40e/i40e_rxtx.c                  |  20 +-
 drivers/net/iavf/iavf_rxtx.c                  |  20 +-
 drivers/net/ice/ice_rxtx.c                    |  20 +-
 drivers/net/ixgbe/ixgbe_rxtx.c                |  20 +-
 drivers/net/mlx5/mlx5_rx.c                    |  17 +-
 examples/l3fwd-power/main.c                   |   6 -
 lib/eal/arm/rte_power_intrinsics.c            |  11 +
 lib/eal/include/generic/rte_cpuflags.h        |   2 +
 .../include/generic/rte_power_intrinsics.h    |  68 +-
 lib/eal/ppc/rte_power_intrinsics.c            |  11 +
 lib/eal/version.map                           |   3 +
 lib/eal/x86/rte_cpuflags.c                    |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |  90 ++-
 lib/power/meson.build                         |   3 +
 lib/power/rte_power_pmd_mgmt.c                | 659 +++++++++++++-----
 lib/power/rte_power_pmd_mgmt.h                |   6 +
 21 files changed, 840 insertions(+), 262 deletions(-)
Signature · 1 line
2.25.1
#107 lore raw

PATCH v7 1/7 power_intrinsics: use callbacks for comparison

Previously, the semantics of power monitor were such that we were checking current value against the expected value, and if they matched, then the sleep was aborted. This is somewhat inflexible, because it only allowed us to check for a specific value in a specific way.

This commit replaces the comparison with a user callback mechanism, so that any PMD (or other code) using rte_power_monitor() can define their own comparison semantics and decision making on how to detect the need to abort the entering of power optimized state.

Existing implementations are adjusted to follow the new semantics.

---

Notes:
v4:
- Return error if callback is set to NULL
- Replace raw number with a macro in monitor condition opaque data

v2:
- Use callback mechanism for more flexibility
- Address feedback from Konstantin

 doc/guides/rel_notes/release_21_08.rst        |  2 ++
 drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
 drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
 drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
 drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
 drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
 drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
 .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
 lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
 9 files changed, 122 insertions(+), 44 deletions(-)
doc/guides/rel_notes/release_21_08.rst +2 -0
index cd02820e68..c1d063bb11 100644
@@ -117,6 +117,8 @@ API Changes
 * eal: ``rte_strscpy`` sets ``rte_errno`` to ``E2BIG`` in case of string
   truncation.
 
+* eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
+
 
 ABI Changes
 -----------
drivers/event/dlb2/dlb2.c +15 -2
index eca183753f..252bbd8d5e 100644
@@ -3154,6 +3154,16 @@ dlb2_port_credits_inc(struct dlb2_port *qm_port, int num)
 	}
 }
 
+#define CLB_MASK_IDX 0
+#define CLB_VAL_IDX 1
+static int
+dlb2_monitor_callback(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	/* abort if the value matches */
+	return (val & opaque[CLB_MASK_IDX]) == opaque[CLB_VAL_IDX] ? -1 : 0;
+}
+
 static inline int
 dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 		  struct dlb2_eventdev_port *ev_port,
@@ -3194,8 +3204,11 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 			expected_value = 0;
 
 		pmc.addr = monitor_addr;
-		pmc.val = expected_value;
-		pmc.mask = qe_mask.raw_qe[1];
+		/* store expected value and comparison mask in opaque data */
+		pmc.opaque[CLB_VAL_IDX] = expected_value;
+		pmc.opaque[CLB_MASK_IDX] = qe_mask.raw_qe[1];
+		/* set up callback */
+		pmc.fn = dlb2_monitor_callback;
 		pmc.size = sizeof(uint64_t);
 
 		rte_power_monitor(&pmc, timeout + start_ticks);
drivers/net/i40e/i40e_rxtx.c +14 -6
index 8d65f287f4..65f325ede1 100644
@@ -81,6 +81,18 @@
 #define I40E_TX_OFFLOAD_SIMPLE_NOTSUP_MASK \
 		(PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_SIMPLE_SUP_MASK)
 
+static int
+i40e_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << I40E_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -93,12 +105,8 @@ i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = i40e_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/iavf/iavf_rxtx.c +14 -6
index f817fbc49b..d61b32fcee 100644
@@ -57,6 +57,18 @@ iavf_proto_xtr_type_to_rxdid(uint8_t flex_type)
 				rxdid_map[flex_type] : IAVF_RXDID_COMMS_OVS_1;
 }
 
+static int
+iavf_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -69,12 +81,8 @@ iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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 << IAVF_RX_DESC_STATUS_DD_SHIFT);
-	pmc->mask = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/* comparison callback */
+	pmc->fn = iavf_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/ice/ice_rxtx.c +14 -6
index 3f6e735984..5d7ab4f047 100644
@@ -27,6 +27,18 @@ 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;
 
+static int
+ice_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_16(1 << ICE_RX_FLEX_DESC_STATUS0_DD_S);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -39,12 +51,8 @@ ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ice_monitor_callback;
 
 	/* register is 16-bit */
 	pmc->size = sizeof(uint16_t);
drivers/net/ixgbe/ixgbe_rxtx.c +14 -6
index d69f36e977..c814a28cb4 100644
@@ -1369,6 +1369,18 @@ const uint32_t
 		RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP,
 };
 
+static int
+ixgbe_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -1381,12 +1393,8 @@ ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ixgbe_monitor_callback;
 
 	/* the registers are 32-bit */
 	pmc->size = sizeof(uint32_t);
drivers/net/mlx5/mlx5_rx.c +15 -2
index 777a1d6e45..17370b77dc 100644
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
 	return rx_queue_count(rxq);
 }
 
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t m = opaque[CLB_MSK_IDX];
+	const uint64_t v = opaque[CLB_VAL_IDX];
+
+	return (value & m) == v ? -1 : 0;
+}
+
 int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
 	struct mlx5_rxq_data *rxq = rx_queue;
@@ -282,8 +294,9 @@ int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 		return -rte_errno;
 	}
 	pmc->addr = &cqe->op_own;
-	pmc->val =  !!idx;
-	pmc->mask = MLX5_CQE_OWNER_MASK;
+	pmc->opaque[CLB_VAL_IDX] = !!idx;
+	pmc->opaque[CLB_MSK_IDX] = MLX5_CQE_OWNER_MASK;
+	pmc->fn = mlx_monitor_callback;
 	pmc->size = sizeof(uint8_t);
 	return 0;
 }
lib/eal/include/generic/rte_power_intrinsics.h +26 -7
index dddca3d41c..c9aa52a86d 100644
@@ -18,19 +18,38 @@
  * which are architecture-dependent.
  */
 
+/** Size of the opaque data in monitor condition */
+#define RTE_POWER_MONITOR_OPAQUE_SZ 4
+
+/**
+ * Callback definition for monitoring conditions. Callbacks with this signature
+ * will be used by `rte_power_monitor()` to check if the entering of power
+ * optimized state should be aborted.
+ *
+ * @param val
+ *   The value read from memory.
+ * @param opaque
+ *   Callback-specific data.
+ *
+ * @return
+ *   0 if entering of power optimized state should proceed
+ *   -1 if entering of power optimized state should be aborted
+ */
+typedef int (*rte_power_monitor_clb_t)(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ]);
 struct rte_power_monitor_cond {
 	volatile void *addr;  /**< Address to monitor for changes */
-	uint64_t val;         /**< If the `mask` is non-zero, location pointed
-	                       *   to by `addr` will be read and compared
-	                       *   against this value.
-	                       */
-	uint64_t mask;   /**< 64-bit mask to extract value read from `addr` */
-	uint8_t size;    /**< Data size (in bytes) that will be used to compare
-	                  *   expected value (`val`) with data read from the
+	uint8_t size;    /**< Data size (in bytes) that will be read from the
 	                  *   monitored memory location (`addr`). Can be 1, 2,
 	                  *   4, or 8. Supplying any other value will result in
 	                  *   an error.
 	                  */
+	rte_power_monitor_clb_t fn; /**< Callback to be used to check if
+	                             *   entering power optimized state should
+	                             *   be aborted.
+	                             */
+	uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ];
+	/**< Callback-specific data */
 };
 
 /**
lib/eal/x86/rte_power_intrinsics.c +8 -9
index 39ea9fdecd..66fea28897 100644
@@ -76,6 +76,7 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
 	const unsigned int lcore_id = rte_lcore_id();
 	struct power_wait_status *s;
+	uint64_t cur_value;
 
 	/* prevent user from running this instruction if it's not supported */
 	if (!wait_supported)
@@ -91,6 +92,9 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	if (__check_val_size(pmc->size) < 0)
 		return -EINVAL;
 
+	if (pmc->fn == NULL)
+		return -EINVAL;
+
 	s = &wait_status[lcore_id];
 
 	/* update sleep address */
@@ -110,16 +114,11 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	/* now that we've put this address into monitor, we can unlock */
 	rte_spinlock_unlock(&s->lock);
 
-	/* if we have a comparison mask, we might not need to sleep at all */
-	if (pmc->mask) {
-		const uint64_t cur_value = __get_umwait_val(
-				pmc->addr, pmc->size);
-		const uint64_t masked = cur_value & pmc->mask;
+	cur_value = __get_umwait_val(pmc->addr, pmc->size);
 
-		/* if the masked value is already matching, abort */
-		if (masked == pmc->val)
-			goto end;
-	}
+	/* check if callback indicates we should abort */
+	if (pmc->fn(cur_value, pmc->opaque) != 0)
+		goto end;
 
 	/* execute UMWAIT */
 	asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
Signature · 1 line
2.25.1
Tested-by on v7 1/7
#108 lore raw

Re: [dpdk-dev] [PATCH v7 1/7] power_intrinsics: use callbacks for comparison

Quoted 17 lines · Anatoly Burakov

Previously, the semantics of power monitor were such that we were checking current value against the expected value, and if they matched, then the sleep was aborted. This is somewhat inflexible, because it only allowed us to check for a specific value in a specific way.

This commit replaces the comparison with a user callback mechanism, so that any PMD (or other code) using rte_power_monitor() can define their own comparison semantics and decision making on how to detect the need to abort the entering of power optimized state.

Existing implementations are adjusted to follow the new semantics.

---

Quoted 10 lines

Notes:
v4:
- Return error if callback is set to NULL
- Replace raw number with a macro in monitor condition opaque data

v2:
- Use callback mechanism for more flexibility
- Address feedback from Konstantin

#109 lore raw

PATCH v7 2/7 net/af_xdp: add power monitor support

In reply to Anatoly Burakov

Implement support for .get_monitor_addr in AF_XDP driver.

---

Notes:
v2:
- Rewrite using the callback mechanism

 drivers/net/af_xdp/rte_eth_af_xdp.c | 34 +++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
drivers/net/af_xdp/rte_eth_af_xdp.c +34 -0
index eb5660a3dc..7830d0c23a 100644
@@ -37,6 +37,7 @@
 #include <rte_malloc.h>
 #include <rte_ring.h>
 #include <rte_spinlock.h>
+#include <rte_power_intrinsics.h>
 
 #include "compat.h"
 
@@ -788,6 +789,38 @@ eth_dev_configure(struct rte_eth_dev *dev)
 	return 0;
 }
 
+#define CLB_VAL_IDX 0
+static int
+eth_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t v = opaque[CLB_VAL_IDX];
+	const uint64_t m = (uint32_t)~0;
+
+	/* if the value has changed, abort entering power optimized state */
+	return (value & m) == v ? 0 : -1;
+}
+
+static int
+eth_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
+{
+	struct pkt_rx_queue *rxq = rx_queue;
+	unsigned int *prod = rxq->rx.producer;
+	const uint32_t cur_val = rxq->rx.cached_prod; /* use cached value */
+
+	/* watch for changes in producer ring */
+	pmc->addr = (void*)prod;
+
+	/* store current value */
+	pmc->opaque[CLB_VAL_IDX] = cur_val;
+	pmc->fn = eth_monitor_callback;
+
+	/* AF_XDP producer ring index is 32-bit */
+	pmc->size = sizeof(uint32_t);
+
+	return 0;
+}
+
 static int
 eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 {
@@ -1448,6 +1481,7 @@ static const struct eth_dev_ops ops = {
 	.link_update = eth_link_update,
 	.stats_get = eth_stats_get,
 	.stats_reset = eth_stats_reset,
+	.get_monitor_addr = eth_get_monitor_addr
 };
 
 /** parse busy_budget argument */
Signature · 1 line
2.25.1
#110 lore raw

PATCH v7 3/7 eal: add power monitor for multiple events

In reply to Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

 lib/eal/arm/rte_power_intrinsics.c            | 11 +++
 lib/eal/include/generic/rte_cpuflags.h        |  2 +
 .../include/generic/rte_power_intrinsics.h    | 35 +++++++++
 lib/eal/ppc/rte_power_intrinsics.c            | 11 +++
 lib/eal/version.map                           |  3 +
 lib/eal/x86/rte_cpuflags.c                    |  2 +
 lib/eal/x86/rte_power_intrinsics.c            | 73 +++++++++++++++++++
 7 files changed, 137 insertions(+)
lib/eal/arm/rte_power_intrinsics.c +11 -0
index e83f04072a..78f55b7203 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/include/generic/rte_cpuflags.h +2 -0
index 28a5aecde8..d35551e931 100644
@@ -24,6 +24,8 @@ struct rte_cpu_intrinsics {
 	/**< indicates support for rte_power_monitor function */
 	uint32_t power_pause : 1;
 	/**< indicates support for rte_power_pause function */
+	uint32_t power_monitor_multi : 1;
+	/**< indicates support for rte_power_monitor_multi function */
 };
 
 /**
lib/eal/include/generic/rte_power_intrinsics.h +35 -0
index c9aa52a86d..04e8c2ab37 100644
@@ -128,4 +128,39 @@ int rte_power_monitor_wakeup(const unsigned int lcore_id);
 __rte_experimental
 int rte_power_pause(const uint64_t tsc_timestamp);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Monitor a set of addresses for changes. This will cause the CPU to enter an
+ * architecture-defined optimized power state until either one of the specified
+ * memory addresses is written to, a certain TSC timestamp is reached, or other
+ * reasons cause the CPU to wake up.
+ *
+ * Additionally, `expected` 64-bit values and 64-bit masks 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 do not match, the entering of
+ * optimized power state may be aborted.
+ *
+ * @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 pmc
+ *   An array of monitoring condition structures.
+ * @param num
+ *   Length of the `pmc` array.
+ * @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
+int rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp);
+
 #endif /* _RTE_POWER_INTRINSIC_H_ */
lib/eal/ppc/rte_power_intrinsics.c +11 -0
index 7fc9586da7..f00b58ade5 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/version.map +3 -0
index fe5c3dac98..4ccd5475d6 100644
@@ -423,6 +423,9 @@ EXPERIMENTAL {
 	rte_version_release; # WINDOWS_NO_EXPORT
 	rte_version_suffix; # WINDOWS_NO_EXPORT
 	rte_version_year; # WINDOWS_NO_EXPORT
+
+	# added in 21.08
+	rte_power_monitor_multi; # WINDOWS_NO_EXPORT
 };
 
 INTERNAL {
lib/eal/x86/rte_cpuflags.c +2 -0
index a96312ff7f..d339734a8c 100644
@@ -189,5 +189,7 @@ rte_cpu_get_intrinsics_support(struct rte_cpu_intrinsics *intrinsics)
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_WAITPKG)) {
 		intrinsics->power_monitor = 1;
 		intrinsics->power_pause = 1;
+		if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_RTM))
+			intrinsics->power_monitor_multi = 1;
 	}
 }
lib/eal/x86/rte_power_intrinsics.c +73 -0

100 lines not on this page · raw message

Signature · 1 line
2.25.1
Tested-by on v7 3/7
#111 lore raw

Re: [dpdk-dev] [PATCH v7 3/7] eal: add power monitor for multiple events

Quoted 11 lines · Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Quoted 8 lines

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

Initially I had issues running this as I couldn't see the "rtm" flag in /proc/cpuinfo, but adding "tsx=on" to the kernel parameters resolved this, and rtm was then available to use. Once this was done, I was then able to successfully see power saving when a core was configuired with multiple queues.

#112 lore raw

PATCH v7 4/7 power: remove thread safety from PMD power API's

In reply to Anatoly Burakov

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

---

Notes:
v2:
- Add check for stopped queue
- Clarified doc message
- Added release notes

 doc/guides/rel_notes/release_21_08.rst |   4 +
 lib/power/meson.build                  |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 133 ++++++++++---------------
 lib/power/rte_power_pmd_mgmt.h         |   6 ++
 4 files changed, 66 insertions(+), 80 deletions(-)
doc/guides/rel_notes/release_21_08.rst +4 -0
index c1d063bb11..4b84c89c0b 100644
@@ -119,6 +119,10 @@ API Changes
 
 * eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
 
+* rte_power: The experimental PMD power management API is no longer considered
+  to be thread safe; all Rx queues affected by the API will now need to be
+  stopped before making any changes to the power management scheme.
+
 
 ABI Changes
 -----------
lib/power/meson.build +3 -0
index c1097d32f1..4f6a242364 100644
@@ -21,4 +21,7 @@ headers = files(
         'rte_power_pmd_mgmt.h',
         'rte_power_guest_channel.h',
 )
+if cc.has_argument('-Wno-cast-qual')
+    cflags += '-Wno-cast-qual'
+endif
 deps += ['timer', 'ethdev']
lib/power/rte_power_pmd_mgmt.c +53 -80

209 lines not on this page · raw message

lib/power/rte_power_pmd_mgmt.h +6 -0
index 7a0ac24625..444e7b8a66 100644
@@ -43,6 +43,9 @@ enum rte_power_pmd_mgmt_type {
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue will be polled from.
  * @param port_id
@@ -69,6 +72,9 @@ rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id,
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue is polled from.
  * @param port_id
Signature · 1 line
2.25.1
Tested-by on v7 4/7
#113 lore raw

Re: [dpdk-dev] [PATCH v7 4/7] power: remove thread safety from PMD power API's

Quoted 12 lines · Anatoly Burakov

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

Quoted 9 lines

---

Notes:
v2:
- Add check for stopped queue
- Clarified doc message
- Added release notes

#114 lore raw

PATCH v7 5/7 power: support callbacks for multiple Rx queues

In reply to Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v7:
- Fix bug where initial sleep target was always set to zero
- Fix logic in handling of n_queues_ready_to_sleep counter
- Update documentation on hardware requirements

v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

 doc/guides/nics/features.rst           |  10 +
 doc/guides/prog_guide/power_man.rst    |  69 ++--
 doc/guides/rel_notes/release_21_08.rst |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 456 +++++++++++++++++++------
 4 files changed, 402 insertions(+), 136 deletions(-)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
doc/guides/prog_guide/power_man.rst +41 -28

78 lines not on this page · raw message

doc/guides/rel_notes/release_21_08.rst +3 -0
index 4b84c89c0b..fce50f0cd6 100644
@@ -85,6 +85,9 @@ New Features
   usecases. Configuration happens via standard rawdev enq/deq operations. See
   the :doc:`../rawdevs/cnxk_bphy` rawdev guide for more details on this driver.
 
+* rte_power: The experimental PMD power management API now supports managing
+  multiple Ethernet Rx queues per lcore.
+
 
 Removed Items
 -------------
lib/power/rte_power_pmd_mgmt.c +348 -108

571 lines not on this page · raw message

Signature · 1 line
2.25.1
Tested-by on v7 5/7
#115 lore raw

Re: [dpdk-dev] [PATCH v7 5/7] power: support callbacks for multiple Rx queues

Quoted 50 lines · Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v7:
- Fix bug where initial sleep target was always set to zero
- Fix logic in handling of n_queues_ready_to_sleep counter
- Update documentation on hardware requirements

v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

Quoted 6 lines
  doc/guides/nics/features.rst           |  10 +
  doc/guides/prog_guide/power_man.rst    |  69 ++--
  doc/guides/rel_notes/release_21_08.rst |   3 +
  lib/power/rte_power_pmd_mgmt.c         | 456 +++++++++++++++++++------
  4 files changed, 402 insertions(+), 136 deletions(-)

--snip--

Thanks Anatoly. Not seeing the rollover now, and power savings are back as expected when low traffic in both monitor and pause modes. All previous issues seem now to be resolved.

Patch set LGTM.

#116 lore raw

PATCH v7 6/7 power: support monitoring multiple Rx queues

In reply to Anatoly Burakov

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Notes:
v6:
- Fix the missed feedback from v5

v4:
- Fix possible out of bounds access
- Added missing index increment

 doc/guides/prog_guide/power_man.rst | 15 ++++--
 lib/power/rte_power_pmd_mgmt.c      | 82 ++++++++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 7 deletions(-)
doc/guides/prog_guide/power_man.rst +10 -5
index 0e66878892..e387d7811e 100644
@@ -221,17 +221,22 @@ power saving whenever empty poll count reaches a certain number.
 The "monitor" mode is only supported in the following configurations and scenarios:
 
 * On Linux* x86_64, `rte_power_monitor()` requires WAITPKG instruction set being
-  supported by the CPU. Please refer to your platform documentation for further
-  information.
+  supported by the CPU, while `rte_power_monitor_multi()` requires WAITPKG and
+  RTM instruction sets being supported by the CPU. RTM instruction set may also
+  require booting the Linux with `tsx=on` command line parameter. Please refer
+  to your platform documentation for further information.
 
 * If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor_multi()`` function is supported by the platform, then
+  monitoring multiple Ethernet Rx queues for traffic will be supported.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that only
   ``rte_power_monitor()`` is supported by the platform, then monitoring will be
   limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
   monitored from a different lcore).
 
-* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
-  ``rte_power_monitor()`` function is not supported, then monitor mode will not
-  be supported.
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that neither of the
+  two monitoring functions are supported, then monitor mode will not be supported.
 
 * Not all Ethernet drivers support monitoring, even if the underlying
   platform may support the necessary CPU instructions. Please refer to
lib/power/rte_power_pmd_mgmt.c +80 -2

119 lines not on this page · raw message

Signature · 1 line
2.25.1
Tested-by on v7 6/7
#117 lore raw

Re: [dpdk-dev] [PATCH v7 6/7] power: support monitoring multiple Rx queues

Quoted 8 lines · Anatoly Burakov

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Quoted 9 lines

Notes:
v6:
- Fix the missed feedback from v5

v4:
- Fix possible out of bounds access
- Added missing index increment

--snip--

#118 lore raw

PATCH v7 7/7 l3fwd-power: support multiqueue in PMD pmgmt modes

In reply to Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

---
 examples/l3fwd-power/main.c | 6 ------
 1 file changed, 6 deletions(-)
examples/l3fwd-power/main.c +0 -6
index f8dfed1634..52f56dc405 100644
@@ -2723,12 +2723,6 @@ main(int argc, char **argv)
 		printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
 		fflush(stdout);
 
-		/* PMD power management mode can only do 1 queue per core */
-		if (app_mode == APP_MODE_PMD_MGMT && qconf->n_rx_queue > 1) {
-			rte_exit(EXIT_FAILURE,
-				"In PMD power management mode, only one queue per lcore is allowed\n");
-		}
-
 		/* init RX queues */
 		for(queue = 0; queue < qconf->n_rx_queue; ++queue) {
 			struct rte_eth_rxconf rxq_conf;
Signature · 1 line
2.25.1
Tested-by on v7 7/7
#119 lore raw

Re: [dpdk-dev] [PATCH v7 7/7] l3fwd-power: support multiqueue in PMD pmgmt modes

Quoted 15 lines · Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

---
  examples/l3fwd-power/main.c | 6 ------
  1 file changed, 6 deletions(-)
examples/l3fwd-power/main.c +0 -0
index f8dfed1634..52f56dc405 100644
@@ -2723,12 +2723,6 @@ main(int argc, char **argv)
  		printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
  		fflush(stdout);

examples/l3fwd-power/main.c main(int argc, char **argv) · line 2728

Quoted 10 lines
examples/l3fwd-power/main.c +0 -6
 
-		/* PMD power management mode can only do 1 queue per core */
-		if (app_mode == APP_MODE_PMD_MGMT && qconf->n_rx_queue > 1) {
-			rte_exit(EXIT_FAILURE,
-				"In PMD power management mode, only one queue per lcore is allowed\n");
-		}
-
  		/* init RX queues */
  		for(queue = 0; queue < qconf->n_rx_queue; ++queue) {
  			struct rte_eth_rxconf rxq_conf;
#120 lore raw

v8 cover Enhancements for PMD power management

In reply to Anatoly Burakov

This patchset introduces several changes related to PMD power management:

- Changed monitoring intrinsics to use callbacks as a comparison function, based on previous patchset [1] but incorporating feedback [2] - this hopefully will make it possible to add support for .get_monitor_addr in virtio
- Add a new intrinsic to monitor multiple addresses, based on RTM instruction set and the TPAUSE instruction
- Add support for PMD power management on multiple queues, as well as all accompanying infrastructure and example apps changes

v8:
- Fixed checkpatch issue
- Added comment explaining empty poll handling (Konstantin)

v7:
- Fixed various bugs

v6:
- Improved the algorithm for multi-queue sleep
- Fixed segfault and addressed other feedback

v5:
- Removed "power save queue" API and replaced with mechanism suggested by Konstantin
- Addressed other feedback

v4:
- Replaced raw number with a macro
- Fixed all the bugs found by Konstantin
- Some other minor corrections

v3:
- Moved some doc updates to NIC features list

v2:
- Changed check inversion to callbacks
- Addressed feedback from Konstantin
- Added doc updates where necessary

[1] http://patches.dpdk.org/project/dpdk/list/?series=16930&state=*
[2] http://patches.dpdk.org/project/dpdk/patch/819ef1ace187365a615d3383e54579e3d9fb216e.1620747068.git.anatoly.burakov@intel.com/#133274

Anatoly Burakov (7):
power_intrinsics: use callbacks for comparison net/af_xdp: add power monitor support
eal: add power monitor for multiple events
power: remove thread safety from PMD power API's
power: support callbacks for multiple Rx queues
power: support monitoring multiple Rx queues
l3fwd-power: support multiqueue in PMD pmgmt modes

 doc/guides/nics/features.rst                  |  10 +
 doc/guides/prog_guide/power_man.rst           |  74 +-
 doc/guides/rel_notes/release_21_08.rst        |   9 +
 drivers/event/dlb2/dlb2.c                     |  17 +-
 drivers/net/af_xdp/rte_eth_af_xdp.c           |  34 +
 drivers/net/i40e/i40e_rxtx.c                  |  20 +-
 drivers/net/iavf/iavf_rxtx.c                  |  20 +-
 drivers/net/ice/ice_rxtx.c                    |  20 +-
 drivers/net/ixgbe/ixgbe_rxtx.c                |  20 +-
 drivers/net/mlx5/mlx5_rx.c                    |  17 +-
 examples/l3fwd-power/main.c                   |   6 -
 lib/eal/arm/rte_power_intrinsics.c            |  11 +
 lib/eal/include/generic/rte_cpuflags.h        |   2 +
 .../include/generic/rte_power_intrinsics.h    |  68 +-
 lib/eal/ppc/rte_power_intrinsics.c            |  11 +
 lib/eal/version.map                           |   3 +
 lib/eal/x86/rte_cpuflags.c                    |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |  90 ++-
 lib/power/meson.build                         |   3 +
 lib/power/rte_power_pmd_mgmt.c                | 663 +++++++++++++-----
 lib/power/rte_power_pmd_mgmt.h                |   6 +
 21 files changed, 844 insertions(+), 262 deletions(-)
Signature · 1 line
2.25.1
#121 lore raw

PATCH v8 1/7 power_intrinsics: use callbacks for comparison

Previously, the semantics of power monitor were such that we were checking current value against the expected value, and if they matched, then the sleep was aborted. This is somewhat inflexible, because it only allowed us to check for a specific value in a specific way.

This commit replaces the comparison with a user callback mechanism, so that any PMD (or other code) using rte_power_monitor() can define their own comparison semantics and decision making on how to detect the need to abort the entering of power optimized state.

Existing implementations are adjusted to follow the new semantics.

---

Notes:
v4:
- Return error if callback is set to NULL
- Replace raw number with a macro in monitor condition opaque data

v2:
- Use callback mechanism for more flexibility
- Address feedback from Konstantin

 doc/guides/rel_notes/release_21_08.rst        |  2 ++
 drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
 drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
 drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
 drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
 drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
 drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
 .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
 lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
 9 files changed, 122 insertions(+), 44 deletions(-)
doc/guides/rel_notes/release_21_08.rst +2 -0
index c92e016783..65910de348 100644
@@ -135,6 +135,8 @@ API Changes
 * eal: ``rte_strscpy`` sets ``rte_errno`` to ``E2BIG`` in case of string
   truncation.
 
+* eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
+
 
 ABI Changes
 -----------
drivers/event/dlb2/dlb2.c +15 -2
index eca183753f..252bbd8d5e 100644
@@ -3154,6 +3154,16 @@ dlb2_port_credits_inc(struct dlb2_port *qm_port, int num)
 	}
 }
 
+#define CLB_MASK_IDX 0
+#define CLB_VAL_IDX 1
+static int
+dlb2_monitor_callback(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	/* abort if the value matches */
+	return (val & opaque[CLB_MASK_IDX]) == opaque[CLB_VAL_IDX] ? -1 : 0;
+}
+
 static inline int
 dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 		  struct dlb2_eventdev_port *ev_port,
@@ -3194,8 +3204,11 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 			expected_value = 0;
 
 		pmc.addr = monitor_addr;
-		pmc.val = expected_value;
-		pmc.mask = qe_mask.raw_qe[1];
+		/* store expected value and comparison mask in opaque data */
+		pmc.opaque[CLB_VAL_IDX] = expected_value;
+		pmc.opaque[CLB_MASK_IDX] = qe_mask.raw_qe[1];
+		/* set up callback */
+		pmc.fn = dlb2_monitor_callback;
 		pmc.size = sizeof(uint64_t);
 
 		rte_power_monitor(&pmc, timeout + start_ticks);
drivers/net/i40e/i40e_rxtx.c +14 -6
index 8d65f287f4..65f325ede1 100644
@@ -81,6 +81,18 @@
 #define I40E_TX_OFFLOAD_SIMPLE_NOTSUP_MASK \
 		(PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_SIMPLE_SUP_MASK)
 
+static int
+i40e_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << I40E_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -93,12 +105,8 @@ i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = i40e_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/iavf/iavf_rxtx.c +14 -6
index f817fbc49b..d61b32fcee 100644
@@ -57,6 +57,18 @@ iavf_proto_xtr_type_to_rxdid(uint8_t flex_type)
 				rxdid_map[flex_type] : IAVF_RXDID_COMMS_OVS_1;
 }
 
+static int
+iavf_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -69,12 +81,8 @@ iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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 << IAVF_RX_DESC_STATUS_DD_SHIFT);
-	pmc->mask = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/* comparison callback */
+	pmc->fn = iavf_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/ice/ice_rxtx.c +14 -6
index 3f6e735984..5d7ab4f047 100644
@@ -27,6 +27,18 @@ 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;
 
+static int
+ice_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_16(1 << ICE_RX_FLEX_DESC_STATUS0_DD_S);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -39,12 +51,8 @@ ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ice_monitor_callback;
 
 	/* register is 16-bit */
 	pmc->size = sizeof(uint16_t);
drivers/net/ixgbe/ixgbe_rxtx.c +14 -6
index d69f36e977..c814a28cb4 100644
@@ -1369,6 +1369,18 @@ const uint32_t
 		RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP,
 };
 
+static int
+ixgbe_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -1381,12 +1393,8 @@ ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ixgbe_monitor_callback;
 
 	/* the registers are 32-bit */
 	pmc->size = sizeof(uint32_t);
drivers/net/mlx5/mlx5_rx.c +15 -2
index 777a1d6e45..17370b77dc 100644
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
 	return rx_queue_count(rxq);
 }
 
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t m = opaque[CLB_MSK_IDX];
+	const uint64_t v = opaque[CLB_VAL_IDX];
+
+	return (value & m) == v ? -1 : 0;
+}
+
 int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
 	struct mlx5_rxq_data *rxq = rx_queue;
@@ -282,8 +294,9 @@ int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 		return -rte_errno;
 	}
 	pmc->addr = &cqe->op_own;
-	pmc->val =  !!idx;
-	pmc->mask = MLX5_CQE_OWNER_MASK;
+	pmc->opaque[CLB_VAL_IDX] = !!idx;
+	pmc->opaque[CLB_MSK_IDX] = MLX5_CQE_OWNER_MASK;
+	pmc->fn = mlx_monitor_callback;
 	pmc->size = sizeof(uint8_t);
 	return 0;
 }
lib/eal/include/generic/rte_power_intrinsics.h +26 -7
index dddca3d41c..c9aa52a86d 100644
@@ -18,19 +18,38 @@
  * which are architecture-dependent.
  */
 
+/** Size of the opaque data in monitor condition */
+#define RTE_POWER_MONITOR_OPAQUE_SZ 4
+
+/**
+ * Callback definition for monitoring conditions. Callbacks with this signature
+ * will be used by `rte_power_monitor()` to check if the entering of power
+ * optimized state should be aborted.
+ *
+ * @param val
+ *   The value read from memory.
+ * @param opaque
+ *   Callback-specific data.
+ *
+ * @return
+ *   0 if entering of power optimized state should proceed
+ *   -1 if entering of power optimized state should be aborted
+ */
+typedef int (*rte_power_monitor_clb_t)(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ]);
 struct rte_power_monitor_cond {
 	volatile void *addr;  /**< Address to monitor for changes */
-	uint64_t val;         /**< If the `mask` is non-zero, location pointed
-	                       *   to by `addr` will be read and compared
-	                       *   against this value.
-	                       */
-	uint64_t mask;   /**< 64-bit mask to extract value read from `addr` */
-	uint8_t size;    /**< Data size (in bytes) that will be used to compare
-	                  *   expected value (`val`) with data read from the
+	uint8_t size;    /**< Data size (in bytes) that will be read from the
 	                  *   monitored memory location (`addr`). Can be 1, 2,
 	                  *   4, or 8. Supplying any other value will result in
 	                  *   an error.
 	                  */
+	rte_power_monitor_clb_t fn; /**< Callback to be used to check if
+	                             *   entering power optimized state should
+	                             *   be aborted.
+	                             */
+	uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ];
+	/**< Callback-specific data */
 };
 
 /**
lib/eal/x86/rte_power_intrinsics.c +8 -9
index 39ea9fdecd..66fea28897 100644
@@ -76,6 +76,7 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
 	const unsigned int lcore_id = rte_lcore_id();
 	struct power_wait_status *s;
+	uint64_t cur_value;
 
 	/* prevent user from running this instruction if it's not supported */
 	if (!wait_supported)
@@ -91,6 +92,9 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	if (__check_val_size(pmc->size) < 0)
 		return -EINVAL;
 
+	if (pmc->fn == NULL)
+		return -EINVAL;
+
 	s = &wait_status[lcore_id];
 
 	/* update sleep address */
@@ -110,16 +114,11 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	/* now that we've put this address into monitor, we can unlock */
 	rte_spinlock_unlock(&s->lock);
 
-	/* if we have a comparison mask, we might not need to sleep at all */
-	if (pmc->mask) {
-		const uint64_t cur_value = __get_umwait_val(
-				pmc->addr, pmc->size);
-		const uint64_t masked = cur_value & pmc->mask;
+	cur_value = __get_umwait_val(pmc->addr, pmc->size);
 
-		/* if the masked value is already matching, abort */
-		if (masked == pmc->val)
-			goto end;
-	}
+	/* check if callback indicates we should abort */
+	if (pmc->fn(cur_value, pmc->opaque) != 0)
+		goto end;
 
 	/* execute UMWAIT */
 	asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
Signature · 1 line
2.25.1
Acked-by on v8 1/7
#122 lore raw

Re: [dpdk-dev] [PATCH v8 1/7] power_intrinsics: use callbacks for comparison

Quoted 410 lines

-----Original Message-----
From: Burakov, Anatoly <[email protected]>
Sent: Thursday, July 8, 2021 9:14 AM
To: [email protected]; McDaniel, Timothy <[email protected]>; Xing, Beilei <[email protected]>; Wu, Jingjing <[email protected]>; Yang, Qiming <[email protected]>; Zhang, Qi Z <[email protected]>; Wang, Haiyue <[email protected]>; Matan Azrad <[email protected]>; Shahaf Shuler <[email protected]>; Viacheslav Ovsiienko <[email protected]>; Richardson, Bruce <[email protected]>; Ananyev, Konstantin <[email protected]>
Cc: Loftus, Ciara <[email protected]>; Hunt, David <[email protected]>
Subject: [PATCH v8 1/7] power_intrinsics: use callbacks for comparison

Previously, the semantics of power monitor were such that we were checking current value against the expected value, and if they matched, then the sleep was aborted. This is somewhat inflexible, because it only allowed us to check for a specific value in a specific way.

This commit replaces the comparison with a user callback mechanism, so that any PMD (or other code) using rte_power_monitor() can define their own comparison semantics and decision making on how to detect the need to abort the entering of power optimized state.

Existing implementations are adjusted to follow the new semantics.

---

Notes:
v4:
- Return error if callback is set to NULL
- Replace raw number with a macro in monitor condition opaque data

v2:
- Use callback mechanism for more flexibility
- Address feedback from Konstantin

 doc/guides/rel_notes/release_21_08.rst        |  2 ++
 drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
 drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
 drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
 drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
 drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
 drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
 .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
 lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
 9 files changed, 122 insertions(+), 44 deletions(-)
doc/guides/rel_notes/release_21_08.rst +2 -0
index c92e016783..65910de348 100644
@@ -135,6 +135,8 @@ API Changes
 * eal: ``rte_strscpy`` sets ``rte_errno`` to ``E2BIG`` in case of string
   truncation.
 
+* eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
+
 
 ABI Changes
 -----------
drivers/event/dlb2/dlb2.c +10 -0
index eca183753f..252bbd8d5e 100644
@@ -3154,6 +3154,16 @@ dlb2_port_credits_inc(struct dlb2_port *qm_port,
 int num)
 	}
 }
 
+#define CLB_MASK_IDX 0
+#define CLB_VAL_IDX 1
+static int
+dlb2_monitor_callback(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	/* abort if the value matches */
+	return (val & opaque[CLB_MASK_IDX]) == opaque[CLB_VAL_IDX] ? -1 :
 0;
+}
+
 static inline int

dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,

drivers/event/dlb2/dlb2.c +5 -2
 		  struct dlb2_eventdev_port *ev_port,
@@ -3194,8 +3204,11 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 			expected_value = 0;
 
 		pmc.addr = monitor_addr;
-		pmc.val = expected_value;
-		pmc.mask = qe_mask.raw_qe[1];
+		/* store expected value and comparison mask in opaque data */
+		pmc.opaque[CLB_VAL_IDX] = expected_value;
+		pmc.opaque[CLB_MASK_IDX] = qe_mask.raw_qe[1];
+		/* set up callback */
+		pmc.fn = dlb2_monitor_callback;
 		pmc.size = sizeof(uint64_t);
 
 		rte_power_monitor(&pmc, timeout + start_ticks);
drivers/net/i40e/i40e_rxtx.c +12 -0
index 8d65f287f4..65f325ede1 100644
@@ -81,6 +81,18 @@
 #define I40E_TX_OFFLOAD_SIMPLE_NOTSUP_MASK \
 		(PKT_TX_OFFLOAD_MASK ^
 I40E_TX_OFFLOAD_SIMPLE_SUP_MASK)
 
+static int
+i40e_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ]
 __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 <<
 I40E_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+

int
i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond
*pmc)
{

drivers/net/i40e/i40e_rxtx.c +2 -6
@@ -93,12 +105,8 @@ i40e_get_monitor_addr(void *rx_queue, struct
 rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = i40e_monitor_callback;
 
drivers/net/i40e/i40e_rxtx.c +0 -0
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/iavf/iavf_rxtx.c +12 -0
index f817fbc49b..d61b32fcee 100644
@@ -57,6 +57,18 @@ iavf_proto_xtr_type_to_rxdid(uint8_t flex_type)
 				rxdid_map[flex_type] :
 IAVF_RXDID_COMMS_OVS_1;
 }
 
+static int
+iavf_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ]
 __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 <<
 IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+

int
iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc) {

drivers/net/iavf/iavf_rxtx.c +2 -6
@@ -69,12 +81,8 @@ iavf_get_monitor_addr(void *rx_queue, struct
 rte_power_monitor_cond *pmc)
 	/* 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 << IAVF_RX_DESC_STATUS_DD_SHIFT);
-	pmc->mask = rte_cpu_to_le_64(1 <<
 IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/* comparison callback */
+	pmc->fn = iavf_monitor_callback;
 
drivers/net/iavf/iavf_rxtx.c +0 -0
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/ice/ice_rxtx.c +12 -0
index 3f6e735984..5d7ab4f047 100644
@@ -27,6 +27,18 @@ 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;
 
+static int
+ice_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ]
 __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_16(1 <<
 ICE_RX_FLEX_DESC_STATUS0_DD_S);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+

int
ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc) {

drivers/net/ice/ice_rxtx.c +2 -6
@@ -39,12 +51,8 @@ ice_get_monitor_addr(void *rx_queue, struct
 rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ice_monitor_callback;
 
drivers/net/ice/ice_rxtx.c +0 -0
 	/* register is 16-bit */
 	pmc->size = sizeof(uint16_t);
drivers/net/ixgbe/ixgbe_rxtx.c +12 -0
index d69f36e977..c814a28cb4 100644
@@ -1369,6 +1369,18 @@ const uint32_t
 		RTE_PTYPE_INNER_L3_IPV4_EXT |
 RTE_PTYPE_INNER_L4_UDP,
 };
 
+static int
+ixgbe_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ]
 __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int

ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond
*pmc)
{

drivers/net/ixgbe/ixgbe_rxtx.c +2 -6
@@ -1381,12 +1393,8 @@ ixgbe_get_monitor_addr(void *rx_queue, struct
 rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ixgbe_monitor_callback;
 
 	/* the registers are 32-bit */
drivers/net/ixgbe/ixgbe_rxtx.c +0 -0
 	pmc->size = sizeof(uint32_t);
drivers/net/mlx5/mlx5_rx.c +12 -0
index 777a1d6e45..17370b77dc 100644
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev,
 uint16_t rx_queue_id)
 	return rx_queue_count(rxq);
 }
 
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t m = opaque[CLB_MSK_IDX];
+	const uint64_t v = opaque[CLB_VAL_IDX];
+
+	return (value & m) == v ? -1 : 0;
+}
+
 int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond
 *pmc)

{

drivers/net/mlx5/mlx5_rx.c +3 -2
 	struct mlx5_rxq_data *rxq = rx_queue;
@@ -282,8 +294,9 @@ int mlx5_get_monitor_addr(void *rx_queue, struct
 rte_power_monitor_cond *pmc)
 		return -rte_errno;
 	}
 	pmc->addr = &cqe->op_own;
-	pmc->val =  !!idx;
-	pmc->mask = MLX5_CQE_OWNER_MASK;
+	pmc->opaque[CLB_VAL_IDX] = !!idx;
+	pmc->opaque[CLB_MSK_IDX] = MLX5_CQE_OWNER_MASK;
+	pmc->fn = mlx_monitor_callback;
 	pmc->size = sizeof(uint8_t);
 	return 0;

}

lib/eal/include/generic/rte_power_intrinsics.h +26 -7
index dddca3d41c..c9aa52a86d 100644
@@ -18,19 +18,38 @@
  * which are architecture-dependent.
  */
 
+/** Size of the opaque data in monitor condition */
+#define RTE_POWER_MONITOR_OPAQUE_SZ 4
+
+/**
+ * Callback definition for monitoring conditions. Callbacks with this signature
+ * will be used by `rte_power_monitor()` to check if the entering of power
+ * optimized state should be aborted.
+ *
+ * @param val
+ *   The value read from memory.
+ * @param opaque
+ *   Callback-specific data.
+ *
+ * @return
+ *   0 if entering of power optimized state should proceed
+ *   -1 if entering of power optimized state should be aborted
+ */
+typedef int (*rte_power_monitor_clb_t)(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ]);
 struct rte_power_monitor_cond {
 	volatile void *addr;  /**< Address to monitor for changes */
-	uint64_t val;         /**< If the `mask` is non-zero, location pointed
-	                       *   to by `addr` will be read and compared
-	                       *   against this value.
-	                       */
-	uint64_t mask;   /**< 64-bit mask to extract value read from `addr` */
-	uint8_t size;    /**< Data size (in bytes) that will be used to compare
-	                  *   expected value (`val`) with data read from the
+	uint8_t size;    /**< Data size (in bytes) that will be read from the
 	                  *   monitored memory location (`addr`). Can be 1, 2,
 	                  *   4, or 8. Supplying any other value will result in
 	                  *   an error.
 	                  */
+	rte_power_monitor_clb_t fn; /**< Callback to be used to check if
+	                             *   entering power optimized state should
+	                             *   be aborted.
+	                             */
+	uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ];
+	/**< Callback-specific data */
 };
 
 /**
lib/eal/x86/rte_power_intrinsics.c +1 -0
index 39ea9fdecd..66fea28897 100644
@@ -76,6 +76,7 @@ rte_power_monitor(const struct
 rte_power_monitor_cond *pmc,
 	const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
 	const unsigned int lcore_id = rte_lcore_id();
 	struct power_wait_status *s;
+	uint64_t cur_value;
 
 	/* prevent user from running this instruction if it's not supported */
lib/eal/x86/rte_power_intrinsics.c +3 -0
 	if (!wait_supported)
@@ -91,6 +92,9 @@ rte_power_monitor(const struct
 rte_power_monitor_cond *pmc,
 	if (__check_val_size(pmc->size) < 0)
 		return -EINVAL;
 
+	if (pmc->fn == NULL)
+		return -EINVAL;
+
 	s = &wait_status[lcore_id];
 
lib/eal/x86/rte_power_intrinsics.c +4 -9
 	/* update sleep address */
@@ -110,16 +114,11 @@ rte_power_monitor(const struct
 rte_power_monitor_cond *pmc,
 	/* now that we've put this address into monitor, we can unlock */
 	rte_spinlock_unlock(&s->lock);
 
-	/* if we have a comparison mask, we might not need to sleep at all */
-	if (pmc->mask) {
-		const uint64_t cur_value = __get_umwait_val(
-				pmc->addr, pmc->size);
-		const uint64_t masked = cur_value & pmc->mask;
+	cur_value = __get_umwait_val(pmc->addr, pmc->size);
 
-		/* if the masked value is already matching, abort */
-		if (masked == pmc->val)
-			goto end;
-	}
+	/* check if callback indicates we should abort */
+	if (pmc->fn(cur_value, pmc->opaque) != 0)
+		goto end;
 
 	/* execute UMWAIT */
Quoted 3 lines

asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
--
2.25.1

DLB changes look good to me

#123 lore raw

Re: [dpdk-dev] [PATCH v8 1/7] power_intrinsics: use callbacks for comparison

In reply to Anatoly Burakov

08/07/2021 16:13, Anatoly Burakov:

Quoted 10 lines
 doc/guides/rel_notes/release_21_08.rst        |  2 ++
 drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
 drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
 drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
 drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
 drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
 drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
 .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
 lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
 9 files changed, 122 insertions(+), 44 deletions(-)

About the title, it is introducing a new prefix "power_intrinsics:" with is not so much descriptive.
Probably better to formulate with "eal:" prefix.

drivers/net/mlx5/mlx5_rx.c mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id) · line 276

Quoted 11 lines
drivers/net/mlx5/mlx5_rx.c +5 -0
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
 	return rx_queue_count(rxq);
 }
 
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])

Everything is prefixed with mlx5, let's be consistent. Please replace mlx_ with mlx5_

#124 lore raw

Re: [dpdk-dev] [PATCH v8 1/7] power_intrinsics: use callbacks for comparison

Quoted 25 lines · Thomas Monjalon

08/07/2021 16:13, Anatoly Burakov:

Quoted 10 lines
  doc/guides/rel_notes/release_21_08.rst        |  2 ++
  drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
  drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
  drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
  drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
  drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
  drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
  .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
  lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
  9 files changed, 122 insertions(+), 44 deletions(-)

About the title, it is introducing a new prefix "power_intrinsics:" with is not so much descriptive.
Probably better to formulate with "eal:" prefix.

Quoted 6 lines
drivers/net/mlx5/mlx5_rx.c +0 -0
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
  	return rx_queue_count(rxq);
  }
 
Quoted 9 lines

drivers/net/mlx5/mlx5_rx.c mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id) · line 276

Quoted 5 lines
drivers/net/mlx5/mlx5_rx.c +5 -0
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])

Everything is prefixed with mlx5, let's be consistent. Please replace mlx_ with mlx5_

Sure, will fix.

Signature · 2 lines
Thanks,
Anatoly
#125 lore raw

PATCH v8 2/7 net/af_xdp: add power monitor support

In reply to Anatoly Burakov

Implement support for .get_monitor_addr in AF_XDP driver.

---

Notes:
v8:
- Fix checkpatch issue

v2:
- Rewrite using the callback mechanism

 drivers/net/af_xdp/rte_eth_af_xdp.c | 34 +++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
drivers/net/af_xdp/rte_eth_af_xdp.c +34 -0
index eb5660a3dc..989051dd6d 100644
@@ -37,6 +37,7 @@
 #include <rte_malloc.h>
 #include <rte_ring.h>
 #include <rte_spinlock.h>
+#include <rte_power_intrinsics.h>
 
 #include "compat.h"
 
@@ -788,6 +789,38 @@ eth_dev_configure(struct rte_eth_dev *dev)
 	return 0;
 }
 
+#define CLB_VAL_IDX 0
+static int
+eth_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t v = opaque[CLB_VAL_IDX];
+	const uint64_t m = (uint32_t)~0;
+
+	/* if the value has changed, abort entering power optimized state */
+	return (value & m) == v ? 0 : -1;
+}
+
+static int
+eth_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
+{
+	struct pkt_rx_queue *rxq = rx_queue;
+	unsigned int *prod = rxq->rx.producer;
+	const uint32_t cur_val = rxq->rx.cached_prod; /* use cached value */
+
+	/* watch for changes in producer ring */
+	pmc->addr = (void *)prod;
+
+	/* store current value */
+	pmc->opaque[CLB_VAL_IDX] = cur_val;
+	pmc->fn = eth_monitor_callback;
+
+	/* AF_XDP producer ring index is 32-bit */
+	pmc->size = sizeof(uint32_t);
+
+	return 0;
+}
+
 static int
 eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 {
@@ -1448,6 +1481,7 @@ static const struct eth_dev_ops ops = {
 	.link_update = eth_link_update,
 	.stats_get = eth_stats_get,
 	.stats_reset = eth_stats_reset,
+	.get_monitor_addr = eth_get_monitor_addr
 };
 
 /** parse busy_budget argument */
Signature · 1 line
2.25.1
#126 lore raw

PATCH v8 3/7 eal: add power monitor for multiple events

In reply to Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

 lib/eal/arm/rte_power_intrinsics.c            | 11 +++
 lib/eal/include/generic/rte_cpuflags.h        |  2 +
 .../include/generic/rte_power_intrinsics.h    | 35 +++++++++
 lib/eal/ppc/rte_power_intrinsics.c            | 11 +++
 lib/eal/version.map                           |  3 +
 lib/eal/x86/rte_cpuflags.c                    |  2 +
 lib/eal/x86/rte_power_intrinsics.c            | 73 +++++++++++++++++++
 7 files changed, 137 insertions(+)
lib/eal/arm/rte_power_intrinsics.c +11 -0
index e83f04072a..78f55b7203 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/include/generic/rte_cpuflags.h +2 -0
index 28a5aecde8..d35551e931 100644
@@ -24,6 +24,8 @@ struct rte_cpu_intrinsics {
 	/**< indicates support for rte_power_monitor function */
 	uint32_t power_pause : 1;
 	/**< indicates support for rte_power_pause function */
+	uint32_t power_monitor_multi : 1;
+	/**< indicates support for rte_power_monitor_multi function */
 };
 
 /**
lib/eal/include/generic/rte_power_intrinsics.h +35 -0
index c9aa52a86d..04e8c2ab37 100644
@@ -128,4 +128,39 @@ int rte_power_monitor_wakeup(const unsigned int lcore_id);
 __rte_experimental
 int rte_power_pause(const uint64_t tsc_timestamp);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Monitor a set of addresses for changes. This will cause the CPU to enter an
+ * architecture-defined optimized power state until either one of the specified
+ * memory addresses is written to, a certain TSC timestamp is reached, or other
+ * reasons cause the CPU to wake up.
+ *
+ * Additionally, `expected` 64-bit values and 64-bit masks 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 do not match, the entering of
+ * optimized power state may be aborted.
+ *
+ * @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 pmc
+ *   An array of monitoring condition structures.
+ * @param num
+ *   Length of the `pmc` array.
+ * @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
+int rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp);
+
 #endif /* _RTE_POWER_INTRINSIC_H_ */
lib/eal/ppc/rte_power_intrinsics.c +11 -0
index 7fc9586da7..f00b58ade5 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/version.map +3 -0
index 2df65c6903..887012d02a 100644
@@ -423,6 +423,9 @@ EXPERIMENTAL {
 	rte_version_release; # WINDOWS_NO_EXPORT
 	rte_version_suffix; # WINDOWS_NO_EXPORT
 	rte_version_year; # WINDOWS_NO_EXPORT
+
+	# added in 21.08
+	rte_power_monitor_multi; # WINDOWS_NO_EXPORT
 };
 
 INTERNAL {
lib/eal/x86/rte_cpuflags.c +2 -0
index a96312ff7f..d339734a8c 100644
@@ -189,5 +189,7 @@ rte_cpu_get_intrinsics_support(struct rte_cpu_intrinsics *intrinsics)
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_WAITPKG)) {
 		intrinsics->power_monitor = 1;
 		intrinsics->power_pause = 1;
+		if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_RTM))
+			intrinsics->power_monitor_multi = 1;
 	}
 }
lib/eal/x86/rte_power_intrinsics.c +73 -0

100 lines not on this page · raw message

Signature · 1 line
2.25.1
#127 lore raw

PATCH v8 4/7 power: remove thread safety from PMD power API's

In reply to Anatoly Burakov

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

---

Notes:
v2:
- Add check for stopped queue
- Clarified doc message
- Added release notes

 doc/guides/rel_notes/release_21_08.rst |   4 +
 lib/power/meson.build                  |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 133 ++++++++++---------------
 lib/power/rte_power_pmd_mgmt.h         |   6 ++
 4 files changed, 66 insertions(+), 80 deletions(-)
doc/guides/rel_notes/release_21_08.rst +4 -0
index 65910de348..33e66d746b 100644
@@ -137,6 +137,10 @@ API Changes
 
 * eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
 
+* rte_power: The experimental PMD power management API is no longer considered
+  to be thread safe; all Rx queues affected by the API will now need to be
+  stopped before making any changes to the power management scheme.
+
 
 ABI Changes
 -----------
lib/power/meson.build +3 -0
index c1097d32f1..4f6a242364 100644
@@ -21,4 +21,7 @@ headers = files(
         'rte_power_pmd_mgmt.h',
         'rte_power_guest_channel.h',
 )
+if cc.has_argument('-Wno-cast-qual')
+    cflags += '-Wno-cast-qual'
+endif
 deps += ['timer', 'ethdev']
lib/power/rte_power_pmd_mgmt.c +53 -80

209 lines not on this page · raw message

lib/power/rte_power_pmd_mgmt.h +6 -0
index 7a0ac24625..444e7b8a66 100644
@@ -43,6 +43,9 @@ enum rte_power_pmd_mgmt_type {
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue will be polled from.
  * @param port_id
@@ -69,6 +72,9 @@ rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id,
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue is polled from.
  * @param port_id
Signature · 1 line
2.25.1
#128 lore raw

PATCH v8 5/7 power: support callbacks for multiple Rx queues

In reply to Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v8:
- Added a comment explaining that we want to sleep on each empty poll after threshold has been reached

v7:
- Fix bug where initial sleep target was always set to zero
- Fix logic in handling of n_queues_ready_to_sleep counter
- Update documentation on hardware requirements

v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

 doc/guides/nics/features.rst           |  10 +
 doc/guides/prog_guide/power_man.rst    |  69 ++--
 doc/guides/rel_notes/release_21_08.rst |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 460 +++++++++++++++++++------
 4 files changed, 406 insertions(+), 136 deletions(-)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
doc/guides/prog_guide/power_man.rst +41 -28

78 lines not on this page · raw message

doc/guides/rel_notes/release_21_08.rst +3 -0
index 33e66d746b..181a767b5e 100644
@@ -103,6 +103,9 @@ New Features
   usecases. Configuration happens via standard rawdev enq/deq operations. See
   the :doc:`../rawdevs/cnxk_bphy` rawdev guide for more details on this driver.
 
+* rte_power: The experimental PMD power management API now supports managing
+  multiple Ethernet Rx queues per lcore.
+
 
 Removed Items
 -------------
lib/power/rte_power_pmd_mgmt.c +352 -108

575 lines not on this page · raw message

Signature · 1 line
2.25.1
#129 lore raw

Re: [dpdk-dev] [PATCH v8 5/7] power: support callbacks for multiple Rx queues

Quoted 8 lines · Anatoly Burakov
doc/guides/nics/features.rst +0 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 

doc/guides/nics/features.rst Supports to get Rx/Tx packet burst mode information. · line 924

Quoted 10 lines
doc/guides/nics/features.rst +10 -0
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+

- This new ethdev feature deserves its own commit.

- Adding ethdev maintainers.

We are missing a doc/guides/nics/features/default.ini. The name of the features proposed here is rather long. As far as I can see, features should be shorter than:
doc/guides/conf.py:feature_str_len = 30
I am not really inspired.. "Power mgmt address monitor"?

- pmd supporting this feature must have their .ini updated.

Quoted 3 lines
doc/guides/nics/features.rst +0 -0
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
Signature · 1 line
David Marchand
#130 lore raw

Re: [dpdk-dev] [PATCH v8 5/7] power: support callbacks for multiple Rx queues

Quoted 25 lines · David Marchand
Quoted 8 lines · Anatoly Burakov
doc/guides/nics/features.rst +0 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
  * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
  * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 

doc/guides/nics/features.rst Supports to get Rx/Tx packet burst mode information. · line 924

Quoted 10 lines
doc/guides/nics/features.rst +10 -0
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+

- This new ethdev feature deserves its own commit.

- Adding ethdev maintainers.

Quoted 15 lines

We are missing a doc/guides/nics/features/default.ini. The name of the features proposed here is rather long. As far as I can see, features should be shorter than:
doc/guides/conf.py:feature_str_len = 30
I am not really inspired.. "Power mgmt address monitor"?

- pmd supporting this feature must have their .ini updated.

Quoted 3 lines
doc/guides/nics/features.rst +0 -0
  .. _nic_features_other:
 
  Other dev ops not represented by a Feature

You'd have to walk me through/translate whatever it is that you just said as i have no idea what any of that means :D

Signature · 2 lines
Thanks,
Anatoly
#132 lore raw

Re: [dpdk-dev] [PATCH v8 5/7] power: support callbacks for multiple Rx queues

Quoted 8 lines · David Marchand
Quoted 2 lines · Anatoly

You'd have to walk me through/translate whatever it is that you just said as i have no idea what any of that means :D

https://git.dpdk.org/dpdk/commit/?id=fa5dbd825a

Thanks, i'll separate it out into a separate commit and try to come up with a good name for it!

Signature · 2 lines
Thanks,
Anatoly
#133 lore raw

PATCH v8 6/7 power: support monitoring multiple Rx queues

In reply to Anatoly Burakov

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Notes:
v6:
- Fix the missed feedback from v5

v4:
- Fix possible out of bounds access
- Added missing index increment

 doc/guides/prog_guide/power_man.rst | 15 ++++--
 lib/power/rte_power_pmd_mgmt.c      | 82 ++++++++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 7 deletions(-)
doc/guides/prog_guide/power_man.rst +10 -5
index 0e66878892..e387d7811e 100644
@@ -221,17 +221,22 @@ power saving whenever empty poll count reaches a certain number.
 The "monitor" mode is only supported in the following configurations and scenarios:
 
 * On Linux* x86_64, `rte_power_monitor()` requires WAITPKG instruction set being
-  supported by the CPU. Please refer to your platform documentation for further
-  information.
+  supported by the CPU, while `rte_power_monitor_multi()` requires WAITPKG and
+  RTM instruction sets being supported by the CPU. RTM instruction set may also
+  require booting the Linux with `tsx=on` command line parameter. Please refer
+  to your platform documentation for further information.
 
 * If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor_multi()`` function is supported by the platform, then
+  monitoring multiple Ethernet Rx queues for traffic will be supported.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that only
   ``rte_power_monitor()`` is supported by the platform, then monitoring will be
   limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
   monitored from a different lcore).
 
-* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
-  ``rte_power_monitor()`` function is not supported, then monitor mode will not
-  be supported.
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that neither of the
+  two monitoring functions are supported, then monitor mode will not be supported.
 
 * Not all Ethernet drivers support monitoring, even if the underlying
   platform may support the necessary CPU instructions. Please refer to
lib/power/rte_power_pmd_mgmt.c +80 -2

119 lines not on this page · raw message

Signature · 1 line
2.25.1
#134 lore raw

PATCH v8 7/7 l3fwd-power: support multiqueue in PMD pmgmt modes

In reply to Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

---
 examples/l3fwd-power/main.c | 6 ------
 1 file changed, 6 deletions(-)
examples/l3fwd-power/main.c +0 -6
index f8dfed1634..52f56dc405 100644
@@ -2723,12 +2723,6 @@ main(int argc, char **argv)
 		printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
 		fflush(stdout);
 
-		/* PMD power management mode can only do 1 queue per core */
-		if (app_mode == APP_MODE_PMD_MGMT && qconf->n_rx_queue > 1) {
-			rte_exit(EXIT_FAILURE,
-				"In PMD power management mode, only one queue per lcore is allowed\n");
-		}
-
 		/* init RX queues */
 		for(queue = 0; queue < qconf->n_rx_queue; ++queue) {
 			struct rte_eth_rxconf rxq_conf;
Signature · 1 line
2.25.1
#135 lore raw

Re: [dpdk-dev] [PATCH v8 7/7] l3fwd-power: support multiqueue in PMD pmgmt modes

Quoted 3 lines · Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

Title prefix is examples/l3fwd-power: for consistency sake.

Signature · 1 line
David Marchand
#136 lore raw

v9 cover Enhancements for PMD power management

In reply to Anatoly Burakov

This patchset introduces several changes related to PMD power management:

- Changed monitoring intrinsics to use callbacks as a comparison function, based on previous patchset [1] but incorporating feedback [2] - this hopefully will make it possible to add support for .get_monitor_addr in virtio
- Add a new intrinsic to monitor multiple addresses, based on RTM instruction set and the TPAUSE instruction
- Add support for PMD power management on multiple queues, as well as all accompanying infrastructure and example apps changes

v9:
- Added all missing Acks and Tests
- Added a new commit with NIC features
- Addressed minor issues raised in review

v8:
- Fixed checkpatch issue
- Added comment explaining empty poll handling (Konstantin)

v7:
- Fixed various bugs

v6:
- Improved the algorithm for multi-queue sleep
- Fixed segfault and addressed other feedback

v5:
- Removed "power save queue" API and replaced with mechanism suggested by Konstantin
- Addressed other feedback

v4:
- Replaced raw number with a macro
- Fixed all the bugs found by Konstantin
- Some other minor corrections

v3:
- Moved some doc updates to NIC features list

v2:
- Changed check inversion to callbacks
- Addressed feedback from Konstantin
- Added doc updates where necessary

[1] http://patches.dpdk.org/project/dpdk/list/?series=16930&state=*
[2] http://patches.dpdk.org/project/dpdk/patch/819ef1ace187365a615d3383e54579e3d9fb216e.1620747068.git.anatoly.burakov@intel.com/#133274

Anatoly Burakov (8):
eal: use callbacks for power monitoring comparison net/af_xdp: add power monitor support
doc: add PMD power management NIC feature
eal: add power monitor for multiple events
power: remove thread safety from PMD power API's
power: support callbacks for multiple Rx queues
power: support monitoring multiple Rx queues examples/l3fwd-power: support multiq in PMD modes

 doc/guides/nics/features.rst                  |  10 +
 doc/guides/nics/features/default.ini          |   1 +
 doc/guides/prog_guide/power_man.rst           |  74 +-
 doc/guides/rel_notes/release_21_08.rst        |  11 +
 drivers/event/dlb2/dlb2.c                     |  17 +-
 drivers/net/af_xdp/rte_eth_af_xdp.c           |  34 +
 drivers/net/i40e/i40e_rxtx.c                  |  20 +-
 drivers/net/iavf/iavf_rxtx.c                  |  20 +-
 drivers/net/ice/ice_rxtx.c                    |  20 +-
 drivers/net/ixgbe/ixgbe_rxtx.c                |  20 +-
 drivers/net/mlx5/mlx5_rx.c                    |  17 +-
 examples/l3fwd-power/main.c                   |   6 -
 lib/eal/arm/rte_power_intrinsics.c            |  11 +
 lib/eal/include/generic/rte_cpuflags.h        |   2 +
 .../include/generic/rte_power_intrinsics.h    |  68 +-
 lib/eal/ppc/rte_power_intrinsics.c            |  11 +
 lib/eal/version.map                           |   3 +
 lib/eal/x86/rte_cpuflags.c                    |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |  90 ++-
 lib/power/meson.build                         |   3 +
 lib/power/rte_power_pmd_mgmt.c                | 663 +++++++++++++-----
 lib/power/rte_power_pmd_mgmt.h                |   6 +
 22 files changed, 847 insertions(+), 262 deletions(-)
Signature · 1 line
2.25.1
#137 lore raw

PATCH v9 1/8 eal: use callbacks for power monitoring comparison

Previously, the semantics of power monitor were such that we were checking current value against the expected value, and if they matched, then the sleep was aborted. This is somewhat inflexible, because it only allowed us to check for a specific value in a specific way.

This commit replaces the comparison with a user callback mechanism, so that any PMD (or other code) using rte_power_monitor() can define their own comparison semantics and decision making on how to detect the need to abort the entering of power optimized state.

Existing implementations are adjusted to follow the new semantics.

---

Notes:
v4:
- Return error if callback is set to NULL
- Replace raw number with a macro in monitor condition opaque data

v2:
- Use callback mechanism for more flexibility
- Address feedback from Konstantin

 doc/guides/rel_notes/release_21_08.rst        |  2 ++
 drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
 drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
 drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
 drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
 drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
 drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
 .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
 lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
 9 files changed, 122 insertions(+), 44 deletions(-)
doc/guides/rel_notes/release_21_08.rst +2 -0
index 476822b47f..912fb13b84 100644
@@ -144,6 +144,8 @@ API Changes
 * eal: ``rte_strscpy`` sets ``rte_errno`` to ``E2BIG`` in case of string
   truncation.
 
+* eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
+
 
 ABI Changes
 -----------
drivers/event/dlb2/dlb2.c +15 -2
index eca183753f..252bbd8d5e 100644
@@ -3154,6 +3154,16 @@ dlb2_port_credits_inc(struct dlb2_port *qm_port, int num)
 	}
 }
 
+#define CLB_MASK_IDX 0
+#define CLB_VAL_IDX 1
+static int
+dlb2_monitor_callback(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	/* abort if the value matches */
+	return (val & opaque[CLB_MASK_IDX]) == opaque[CLB_VAL_IDX] ? -1 : 0;
+}
+
 static inline int
 dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 		  struct dlb2_eventdev_port *ev_port,
@@ -3194,8 +3204,11 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 			expected_value = 0;
 
 		pmc.addr = monitor_addr;
-		pmc.val = expected_value;
-		pmc.mask = qe_mask.raw_qe[1];
+		/* store expected value and comparison mask in opaque data */
+		pmc.opaque[CLB_VAL_IDX] = expected_value;
+		pmc.opaque[CLB_MASK_IDX] = qe_mask.raw_qe[1];
+		/* set up callback */
+		pmc.fn = dlb2_monitor_callback;
 		pmc.size = sizeof(uint64_t);
 
 		rte_power_monitor(&pmc, timeout + start_ticks);
drivers/net/i40e/i40e_rxtx.c +14 -6
index e518409fe5..8489f91f1d 100644
@@ -81,6 +81,18 @@
 #define I40E_TX_OFFLOAD_SIMPLE_NOTSUP_MASK \
 		(PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_SIMPLE_SUP_MASK)
 
+static int
+i40e_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << I40E_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -93,12 +105,8 @@ i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = i40e_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/iavf/iavf_rxtx.c +14 -6
index f817fbc49b..d61b32fcee 100644
@@ -57,6 +57,18 @@ iavf_proto_xtr_type_to_rxdid(uint8_t flex_type)
 				rxdid_map[flex_type] : IAVF_RXDID_COMMS_OVS_1;
 }
 
+static int
+iavf_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -69,12 +81,8 @@ iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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 << IAVF_RX_DESC_STATUS_DD_SHIFT);
-	pmc->mask = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/* comparison callback */
+	pmc->fn = iavf_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/ice/ice_rxtx.c +14 -6
index 3f6e735984..5d7ab4f047 100644
@@ -27,6 +27,18 @@ 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;
 
+static int
+ice_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_16(1 << ICE_RX_FLEX_DESC_STATUS0_DD_S);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -39,12 +51,8 @@ ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ice_monitor_callback;
 
 	/* register is 16-bit */
 	pmc->size = sizeof(uint16_t);
drivers/net/ixgbe/ixgbe_rxtx.c +14 -6
index d69f36e977..c814a28cb4 100644
@@ -1369,6 +1369,18 @@ const uint32_t
 		RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP,
 };
 
+static int
+ixgbe_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -1381,12 +1393,8 @@ ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ixgbe_monitor_callback;
 
 	/* the registers are 32-bit */
 	pmc->size = sizeof(uint32_t);
drivers/net/mlx5/mlx5_rx.c +15 -2
index 777a1d6e45..8d47637892 100644
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
 	return rx_queue_count(rxq);
 }
 
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx5_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t m = opaque[CLB_MSK_IDX];
+	const uint64_t v = opaque[CLB_VAL_IDX];
+
+	return (value & m) == v ? -1 : 0;
+}
+
 int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
 	struct mlx5_rxq_data *rxq = rx_queue;
@@ -282,8 +294,9 @@ int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 		return -rte_errno;
 	}
 	pmc->addr = &cqe->op_own;
-	pmc->val =  !!idx;
-	pmc->mask = MLX5_CQE_OWNER_MASK;
+	pmc->opaque[CLB_VAL_IDX] = !!idx;
+	pmc->opaque[CLB_MSK_IDX] = MLX5_CQE_OWNER_MASK;
+	pmc->fn = mlx5_monitor_callback;
 	pmc->size = sizeof(uint8_t);
 	return 0;
 }
lib/eal/include/generic/rte_power_intrinsics.h +26 -7
index dddca3d41c..c9aa52a86d 100644
@@ -18,19 +18,38 @@
  * which are architecture-dependent.
  */
 
+/** Size of the opaque data in monitor condition */
+#define RTE_POWER_MONITOR_OPAQUE_SZ 4
+
+/**
+ * Callback definition for monitoring conditions. Callbacks with this signature
+ * will be used by `rte_power_monitor()` to check if the entering of power
+ * optimized state should be aborted.
+ *
+ * @param val
+ *   The value read from memory.
+ * @param opaque
+ *   Callback-specific data.
+ *
+ * @return
+ *   0 if entering of power optimized state should proceed
+ *   -1 if entering of power optimized state should be aborted
+ */
+typedef int (*rte_power_monitor_clb_t)(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ]);
 struct rte_power_monitor_cond {
 	volatile void *addr;  /**< Address to monitor for changes */
-	uint64_t val;         /**< If the `mask` is non-zero, location pointed
-	                       *   to by `addr` will be read and compared
-	                       *   against this value.
-	                       */
-	uint64_t mask;   /**< 64-bit mask to extract value read from `addr` */
-	uint8_t size;    /**< Data size (in bytes) that will be used to compare
-	                  *   expected value (`val`) with data read from the
+	uint8_t size;    /**< Data size (in bytes) that will be read from the
 	                  *   monitored memory location (`addr`). Can be 1, 2,
 	                  *   4, or 8. Supplying any other value will result in
 	                  *   an error.
 	                  */
+	rte_power_monitor_clb_t fn; /**< Callback to be used to check if
+	                             *   entering power optimized state should
+	                             *   be aborted.
+	                             */
+	uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ];
+	/**< Callback-specific data */
 };
 
 /**
lib/eal/x86/rte_power_intrinsics.c +8 -9
index 39ea9fdecd..66fea28897 100644
@@ -76,6 +76,7 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
 	const unsigned int lcore_id = rte_lcore_id();
 	struct power_wait_status *s;
+	uint64_t cur_value;
 
 	/* prevent user from running this instruction if it's not supported */
 	if (!wait_supported)
@@ -91,6 +92,9 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	if (__check_val_size(pmc->size) < 0)
 		return -EINVAL;
 
+	if (pmc->fn == NULL)
+		return -EINVAL;
+
 	s = &wait_status[lcore_id];
 
 	/* update sleep address */
@@ -110,16 +114,11 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	/* now that we've put this address into monitor, we can unlock */
 	rte_spinlock_unlock(&s->lock);
 
-	/* if we have a comparison mask, we might not need to sleep at all */
-	if (pmc->mask) {
-		const uint64_t cur_value = __get_umwait_val(
-				pmc->addr, pmc->size);
-		const uint64_t masked = cur_value & pmc->mask;
+	cur_value = __get_umwait_val(pmc->addr, pmc->size);
 
-		/* if the masked value is already matching, abort */
-		if (masked == pmc->val)
-			goto end;
-	}
+	/* check if callback indicates we should abort */
+	if (pmc->fn(cur_value, pmc->opaque) != 0)
+		goto end;
 
 	/* execute UMWAIT */
 	asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
Signature · 1 line
2.25.1
#138 lore raw

PATCH v9 2/8 net/af_xdp: add power monitor support

In reply to Anatoly Burakov

Implement support for .get_monitor_addr in AF_XDP driver.

---

Notes:
v8:
- Fix checkpatch issue

v2:
- Rewrite using the callback mechanism

 drivers/net/af_xdp/rte_eth_af_xdp.c | 34 +++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
drivers/net/af_xdp/rte_eth_af_xdp.c +34 -0
index eb5660a3dc..989051dd6d 100644
@@ -37,6 +37,7 @@
 #include <rte_malloc.h>
 #include <rte_ring.h>
 #include <rte_spinlock.h>
+#include <rte_power_intrinsics.h>
 
 #include "compat.h"
 
@@ -788,6 +789,38 @@ eth_dev_configure(struct rte_eth_dev *dev)
 	return 0;
 }
 
+#define CLB_VAL_IDX 0
+static int
+eth_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t v = opaque[CLB_VAL_IDX];
+	const uint64_t m = (uint32_t)~0;
+
+	/* if the value has changed, abort entering power optimized state */
+	return (value & m) == v ? 0 : -1;
+}
+
+static int
+eth_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
+{
+	struct pkt_rx_queue *rxq = rx_queue;
+	unsigned int *prod = rxq->rx.producer;
+	const uint32_t cur_val = rxq->rx.cached_prod; /* use cached value */
+
+	/* watch for changes in producer ring */
+	pmc->addr = (void *)prod;
+
+	/* store current value */
+	pmc->opaque[CLB_VAL_IDX] = cur_val;
+	pmc->fn = eth_monitor_callback;
+
+	/* AF_XDP producer ring index is 32-bit */
+	pmc->size = sizeof(uint32_t);
+
+	return 0;
+}
+
 static int
 eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 {
@@ -1448,6 +1481,7 @@ static const struct eth_dev_ops ops = {
 	.link_update = eth_link_update,
 	.stats_get = eth_stats_get,
 	.stats_reset = eth_stats_reset,
+	.get_monitor_addr = eth_get_monitor_addr
 };
 
 /** parse busy_budget argument */
Signature · 1 line
2.25.1
#139 lore raw

PATCH v9 3/8 doc: add PMD power management NIC feature

In reply to Anatoly Burakov

At this point, multiple different Ethernet drivers from multiple vendors will support the PMD power management scheme. It would be useful to add it to the NIC feature table to indicate support for it.

---
 doc/guides/nics/features.rst         | 10 ++++++++++
 doc/guides/nics/features/default.ini |  1 +
 2 files changed, 11 insertions(+)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
doc/guides/nics/features/default.ini +1 -0
index 3b55e0ccb0..f1e947bd9e 100644
@@ -76,6 +76,7 @@ x86-64               =
 Usage doc            =
 Design doc           =
 Perf doc             =
+Power mgmt address monitor =
 
 [rte_flow items]
 ah                   =
Signature · 1 line
2.25.1
#140 lore raw

Re: [dpdk-dev] [PATCH v9 3/8] doc: add PMD power management NIC feature

Quoted 38 lines · Anatoly Burakov

At this point, multiple different Ethernet drivers from multiple vendors will support the PMD power management scheme. It would be useful to add it to the NIC feature table to indicate support for it.

---
  doc/guides/nics/features.rst         | 10 ++++++++++
  doc/guides/nics/features/default.ini |  1 +
  2 files changed, 11 insertions(+)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
  * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
  * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
  .. _nic_features_other:
 
  Other dev ops not represented by a Feature
doc/guides/nics/features/default.ini +0 -0
index 3b55e0ccb0..f1e947bd9e 100644

doc/guides/nics/features/default.ini x86-64 = · line 82

Quoted 7 lines
doc/guides/nics/features/default.ini +1 -0
@@ -76,6 +76,7 @@ x86-64               =
  Usage doc            =
  Design doc           =
  Perf doc             =
+Power mgmt address monitor =
 
  [rte_flow items]
  ah                   =

Apologies, forgot to git add the driver files to the commit. Will respin shortly.

Signature · 2 lines
Thanks,
Anatoly
#141 lore raw

PATCH v9 4/8 eal: add power monitor for multiple events

In reply to Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

 lib/eal/arm/rte_power_intrinsics.c            | 11 +++
 lib/eal/include/generic/rte_cpuflags.h        |  2 +
 .../include/generic/rte_power_intrinsics.h    | 35 +++++++++
 lib/eal/ppc/rte_power_intrinsics.c            | 11 +++
 lib/eal/version.map                           |  3 +
 lib/eal/x86/rte_cpuflags.c                    |  2 +
 lib/eal/x86/rte_power_intrinsics.c            | 73 +++++++++++++++++++
 7 files changed, 137 insertions(+)
lib/eal/arm/rte_power_intrinsics.c +11 -0
index e83f04072a..78f55b7203 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/include/generic/rte_cpuflags.h +2 -0
index 28a5aecde8..d35551e931 100644
@@ -24,6 +24,8 @@ struct rte_cpu_intrinsics {
 	/**< indicates support for rte_power_monitor function */
 	uint32_t power_pause : 1;
 	/**< indicates support for rte_power_pause function */
+	uint32_t power_monitor_multi : 1;
+	/**< indicates support for rte_power_monitor_multi function */
 };
 
 /**
lib/eal/include/generic/rte_power_intrinsics.h +35 -0
index c9aa52a86d..04e8c2ab37 100644
@@ -128,4 +128,39 @@ int rte_power_monitor_wakeup(const unsigned int lcore_id);
 __rte_experimental
 int rte_power_pause(const uint64_t tsc_timestamp);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Monitor a set of addresses for changes. This will cause the CPU to enter an
+ * architecture-defined optimized power state until either one of the specified
+ * memory addresses is written to, a certain TSC timestamp is reached, or other
+ * reasons cause the CPU to wake up.
+ *
+ * Additionally, `expected` 64-bit values and 64-bit masks 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 do not match, the entering of
+ * optimized power state may be aborted.
+ *
+ * @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 pmc
+ *   An array of monitoring condition structures.
+ * @param num
+ *   Length of the `pmc` array.
+ * @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
+int rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp);
+
 #endif /* _RTE_POWER_INTRINSIC_H_ */
lib/eal/ppc/rte_power_intrinsics.c +11 -0
index 7fc9586da7..f00b58ade5 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/version.map +3 -0
index 2df65c6903..887012d02a 100644
@@ -423,6 +423,9 @@ EXPERIMENTAL {
 	rte_version_release; # WINDOWS_NO_EXPORT
 	rte_version_suffix; # WINDOWS_NO_EXPORT
 	rte_version_year; # WINDOWS_NO_EXPORT
+
+	# added in 21.08
+	rte_power_monitor_multi; # WINDOWS_NO_EXPORT
 };
 
 INTERNAL {
lib/eal/x86/rte_cpuflags.c +2 -0
index a96312ff7f..d339734a8c 100644
@@ -189,5 +189,7 @@ rte_cpu_get_intrinsics_support(struct rte_cpu_intrinsics *intrinsics)
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_WAITPKG)) {
 		intrinsics->power_monitor = 1;
 		intrinsics->power_pause = 1;
+		if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_RTM))
+			intrinsics->power_monitor_multi = 1;
 	}
 }
lib/eal/x86/rte_power_intrinsics.c +73 -0

100 lines not on this page · raw message

Signature · 1 line
2.25.1
#142 lore raw

PATCH v9 5/8 power: remove thread safety from PMD power API's

In reply to Anatoly Burakov

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

---

Notes:
v2:
- Add check for stopped queue
- Clarified doc message
- Added release notes

 doc/guides/rel_notes/release_21_08.rst |   4 +
 lib/power/meson.build                  |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 133 ++++++++++---------------
 lib/power/rte_power_pmd_mgmt.h         |   6 ++
 4 files changed, 66 insertions(+), 80 deletions(-)
doc/guides/rel_notes/release_21_08.rst +4 -0
index 912fb13b84..b9a3caabf0 100644
@@ -146,6 +146,10 @@ API Changes
 
 * eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
 
+* rte_power: The experimental PMD power management API is no longer considered
+  to be thread safe; all Rx queues affected by the API will now need to be
+  stopped before making any changes to the power management scheme.
+
 
 ABI Changes
 -----------
lib/power/meson.build +3 -0
index 36e5a65874..bf937acde4 100644
@@ -22,4 +22,7 @@ headers = files(
         'rte_power_pmd_mgmt.h',
         'rte_power_guest_channel.h',
 )
+if cc.has_argument('-Wno-cast-qual')
+    cflags += '-Wno-cast-qual'
+endif
 deps += ['timer', 'ethdev']
lib/power/rte_power_pmd_mgmt.c +53 -80

209 lines not on this page · raw message

lib/power/rte_power_pmd_mgmt.h +6 -0
index 7a0ac24625..444e7b8a66 100644
@@ -43,6 +43,9 @@ enum rte_power_pmd_mgmt_type {
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue will be polled from.
  * @param port_id
@@ -69,6 +72,9 @@ rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id,
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue is polled from.
  * @param port_id
Signature · 1 line
2.25.1
#143 lore raw

PATCH v9 6/8 power: support callbacks for multiple Rx queues

In reply to Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v8:
- Added a comment explaining that we want to sleep on each empty poll after threshold has been reached

v7:
- Fix bug where initial sleep target was always set to zero
- Fix logic in handling of n_queues_ready_to_sleep counter
- Update documentation on hardware requirements

v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

 doc/guides/prog_guide/power_man.rst    |  69 ++--
 doc/guides/rel_notes/release_21_08.rst |   5 +
 lib/power/rte_power_pmd_mgmt.c         | 460 +++++++++++++++++++------
 3 files changed, 398 insertions(+), 136 deletions(-)
doc/guides/prog_guide/power_man.rst +41 -28
index c70ae128ac..0e66878892 100644
@@ -198,34 +198,45 @@ Ethernet 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 ``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).
+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
+   monitor the Ethernet PMD RX descriptor address, waking the CPU up whenever
+   there's new traffic. Support for this scheme may not be available on all
+   platforms, and further limitations may apply (see below).
+
+* 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 supported by the underlying platform, use ``rte_pause()``.
+
+* Frequency scaling
+   This power saving scheme will use ``librte_power`` library functionality to
+   scale the core frequency up/down depending on traffic volume.
+
+The "monitor" mode is only supported in the following configurations and scenarios:
+
+* On Linux* x86_64, `rte_power_monitor()` requires WAITPKG instruction set being
+  supported by the CPU. Please refer to your platform documentation for further
+  information.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor()`` is supported by the platform, then monitoring will be
+  limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
+  monitored from a different lcore).
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
+  ``rte_power_monitor()`` function is not supported, then monitor mode will not
+  be supported.
+
+* Not all Ethernet drivers support monitoring, even if the underlying
+  platform may support the necessary CPU instructions. Please refer to
+  :doc:`../nics/overview` for more information.
+
 
 API Overview for Ethernet PMD Power Management
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -242,3 +253,5 @@ References
 
 *   The :doc:`../sample_app_ug/vm_power_management`
     chapter in the :doc:`../sample_app_ug/index` section.
+
+*   The :doc:`../nics/overview` chapter in the :doc:`../nics/index` section
doc/guides/rel_notes/release_21_08.rst +5 -0
index b9a3caabf0..ca28ebe461 100644
@@ -112,6 +112,11 @@ New Features
 
   Added support for cppc_cpufreq driver which works on most arm64 platforms.
 
+* **Added multi-queue support to Ethernet PMD Power Management**
+
+  The experimental PMD power management API now supports managing
+  multiple Ethernet Rx queues per lcore.
+
 
 Removed Items
 -------------
lib/power/rte_power_pmd_mgmt.c +352 -108

575 lines not on this page · raw message

Signature · 1 line
2.25.1
#144 lore raw

PATCH v9 7/8 power: support monitoring multiple Rx queues

In reply to Anatoly Burakov

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Notes:
v6:
- Fix the missed feedback from v5

v4:
- Fix possible out of bounds access
- Added missing index increment

 doc/guides/prog_guide/power_man.rst | 15 ++++--
 lib/power/rte_power_pmd_mgmt.c      | 82 ++++++++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 7 deletions(-)
doc/guides/prog_guide/power_man.rst +10 -5
index 0e66878892..e387d7811e 100644
@@ -221,17 +221,22 @@ power saving whenever empty poll count reaches a certain number.
 The "monitor" mode is only supported in the following configurations and scenarios:
 
 * On Linux* x86_64, `rte_power_monitor()` requires WAITPKG instruction set being
-  supported by the CPU. Please refer to your platform documentation for further
-  information.
+  supported by the CPU, while `rte_power_monitor_multi()` requires WAITPKG and
+  RTM instruction sets being supported by the CPU. RTM instruction set may also
+  require booting the Linux with `tsx=on` command line parameter. Please refer
+  to your platform documentation for further information.
 
 * If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor_multi()`` function is supported by the platform, then
+  monitoring multiple Ethernet Rx queues for traffic will be supported.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that only
   ``rte_power_monitor()`` is supported by the platform, then monitoring will be
   limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
   monitored from a different lcore).
 
-* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
-  ``rte_power_monitor()`` function is not supported, then monitor mode will not
-  be supported.
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that neither of the
+  two monitoring functions are supported, then monitor mode will not be supported.
 
 * Not all Ethernet drivers support monitoring, even if the underlying
   platform may support the necessary CPU instructions. Please refer to
lib/power/rte_power_pmd_mgmt.c +80 -2

119 lines not on this page · raw message

Signature · 1 line
2.25.1
#145 lore raw

PATCH v9 8/8 examples/l3fwd-power: support multiq in PMD modes

In reply to Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

---
 examples/l3fwd-power/main.c | 6 ------
 1 file changed, 6 deletions(-)
examples/l3fwd-power/main.c +0 -6
index f8dfed1634..52f56dc405 100644
@@ -2723,12 +2723,6 @@ main(int argc, char **argv)
 		printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
 		fflush(stdout);
 
-		/* PMD power management mode can only do 1 queue per core */
-		if (app_mode == APP_MODE_PMD_MGMT && qconf->n_rx_queue > 1) {
-			rte_exit(EXIT_FAILURE,
-				"In PMD power management mode, only one queue per lcore is allowed\n");
-		}
-
 		/* init RX queues */
 		for(queue = 0; queue < qconf->n_rx_queue; ++queue) {
 			struct rte_eth_rxconf rxq_conf;
Signature · 1 line
2.25.1
#146 lore raw

v10 cover Enhancements for PMD power management

In reply to Anatoly Burakov

This patchset introduces several changes related to PMD power management:

- Changed monitoring intrinsics to use callbacks as a comparison function, based on previous patchset [1] but incorporating feedback [2] - this hopefully will make it possible to add support for .get_monitor_addr in virtio
- Add a new intrinsic to monitor multiple addresses, based on RTM instruction set and the TPAUSE instruction
- Add support for PMD power management on multiple queues, as well as all accompanying infrastructure and example apps changes

v10:
- Added missing changes to NIC feature .ini files

v9:
- Added all missing Acks and Tests
- Added a new commit with NIC features
- Addressed minor issues raised in review

v8:
- Fixed checkpatch issue
- Added comment explaining empty poll handling (Konstantin)

v7:
- Fixed various bugs

v6:
- Improved the algorithm for multi-queue sleep
- Fixed segfault and addressed other feedback

v5:
- Removed "power save queue" API and replaced with mechanism suggested by Konstantin
- Addressed other feedback

v4:
- Replaced raw number with a macro
- Fixed all the bugs found by Konstantin
- Some other minor corrections

v3:
- Moved some doc updates to NIC features list

v2:
- Changed check inversion to callbacks
- Addressed feedback from Konstantin
- Added doc updates where necessary

[1] http://patches.dpdk.org/project/dpdk/list/?series=16930&state=*
[2] http://patches.dpdk.org/project/dpdk/patch/819ef1ace187365a615d3383e54579e3d9fb216e.1620747068.git.anatoly.burakov@intel.com/#133274

Anatoly Burakov (8):
eal: use callbacks for power monitoring comparison net/af_xdp: add power monitor support
doc: add PMD power management NIC feature
eal: add power monitor for multiple events
power: remove thread safety from PMD power API's
power: support callbacks for multiple Rx queues
power: support monitoring multiple Rx queues examples/l3fwd-power: support multiq in PMD modes

 doc/guides/nics/features.rst                  |  10 +
 doc/guides/nics/features/af_xdp.ini           |   1 +
 doc/guides/nics/features/default.ini          |   1 +
 doc/guides/nics/features/i40e.ini             |   1 +
 doc/guides/nics/features/i40e_vf.ini          |   1 +
 doc/guides/nics/features/iavf.ini             |   1 +
 doc/guides/nics/features/ice.ini              |   1 +
 doc/guides/nics/features/ixgbe.ini            |   1 +
 doc/guides/nics/features/ixgbe_vf.ini         |   1 +
 doc/guides/nics/features/mlx5.ini             |   1 +
 doc/guides/prog_guide/power_man.rst           |  74 +-
 doc/guides/rel_notes/release_21_08.rst        |  11 +
 drivers/event/dlb2/dlb2.c                     |  17 +-
 drivers/net/af_xdp/rte_eth_af_xdp.c           |  34 +
 drivers/net/i40e/i40e_rxtx.c                  |  20 +-
 drivers/net/iavf/iavf_rxtx.c                  |  20 +-
 drivers/net/ice/ice_rxtx.c                    |  20 +-
 drivers/net/ixgbe/ixgbe_rxtx.c                |  20 +-
 drivers/net/mlx5/mlx5_rx.c                    |  17 +-
 examples/l3fwd-power/main.c                   |   6 -
 lib/eal/arm/rte_power_intrinsics.c            |  11 +
 lib/eal/include/generic/rte_cpuflags.h        |   2 +
 .../include/generic/rte_power_intrinsics.h    |  68 +-
 lib/eal/ppc/rte_power_intrinsics.c            |  11 +
 lib/eal/version.map                           |   3 +
 lib/eal/x86/rte_cpuflags.c                    |   2 +
 lib/eal/x86/rte_power_intrinsics.c            |  90 ++-
 lib/power/meson.build                         |   3 +
 lib/power/rte_power_pmd_mgmt.c                | 663 +++++++++++++-----
 lib/power/rte_power_pmd_mgmt.h                |   6 +
 30 files changed, 855 insertions(+), 262 deletions(-)
Signature · 1 line
2.25.1
#147 lore raw

PATCH v10 1/8 eal: use callbacks for power monitoring comparison

Previously, the semantics of power monitor were such that we were checking current value against the expected value, and if they matched, then the sleep was aborted. This is somewhat inflexible, because it only allowed us to check for a specific value in a specific way.

This commit replaces the comparison with a user callback mechanism, so that any PMD (or other code) using rte_power_monitor() can define their own comparison semantics and decision making on how to detect the need to abort the entering of power optimized state.

Existing implementations are adjusted to follow the new semantics.

---

Notes:
v4:
- Return error if callback is set to NULL
- Replace raw number with a macro in monitor condition opaque data

v2:
- Use callback mechanism for more flexibility
- Address feedback from Konstantin

 doc/guides/rel_notes/release_21_08.rst        |  2 ++
 drivers/event/dlb2/dlb2.c                     | 17 ++++++++--
 drivers/net/i40e/i40e_rxtx.c                  | 20 +++++++----
 drivers/net/iavf/iavf_rxtx.c                  | 20 +++++++----
 drivers/net/ice/ice_rxtx.c                    | 20 +++++++----
 drivers/net/ixgbe/ixgbe_rxtx.c                | 20 +++++++----
 drivers/net/mlx5/mlx5_rx.c                    | 17 ++++++++--
 .../include/generic/rte_power_intrinsics.h    | 33 +++++++++++++++----
 lib/eal/x86/rte_power_intrinsics.c            | 17 +++++-----
 9 files changed, 122 insertions(+), 44 deletions(-)
doc/guides/rel_notes/release_21_08.rst +2 -0
index 476822b47f..912fb13b84 100644
@@ -144,6 +144,8 @@ API Changes
 * eal: ``rte_strscpy`` sets ``rte_errno`` to ``E2BIG`` in case of string
   truncation.
 
+* eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
+
 
 ABI Changes
 -----------
drivers/event/dlb2/dlb2.c +15 -2
index eca183753f..252bbd8d5e 100644
@@ -3154,6 +3154,16 @@ dlb2_port_credits_inc(struct dlb2_port *qm_port, int num)
 	}
 }
 
+#define CLB_MASK_IDX 0
+#define CLB_VAL_IDX 1
+static int
+dlb2_monitor_callback(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	/* abort if the value matches */
+	return (val & opaque[CLB_MASK_IDX]) == opaque[CLB_VAL_IDX] ? -1 : 0;
+}
+
 static inline int
 dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 		  struct dlb2_eventdev_port *ev_port,
@@ -3194,8 +3204,11 @@ dlb2_dequeue_wait(struct dlb2_eventdev *dlb2,
 			expected_value = 0;
 
 		pmc.addr = monitor_addr;
-		pmc.val = expected_value;
-		pmc.mask = qe_mask.raw_qe[1];
+		/* store expected value and comparison mask in opaque data */
+		pmc.opaque[CLB_VAL_IDX] = expected_value;
+		pmc.opaque[CLB_MASK_IDX] = qe_mask.raw_qe[1];
+		/* set up callback */
+		pmc.fn = dlb2_monitor_callback;
 		pmc.size = sizeof(uint64_t);
 
 		rte_power_monitor(&pmc, timeout + start_ticks);
drivers/net/i40e/i40e_rxtx.c +14 -6
index e518409fe5..8489f91f1d 100644
@@ -81,6 +81,18 @@
 #define I40E_TX_OFFLOAD_SIMPLE_NOTSUP_MASK \
 		(PKT_TX_OFFLOAD_MASK ^ I40E_TX_OFFLOAD_SIMPLE_SUP_MASK)
 
+static int
+i40e_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << I40E_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -93,12 +105,8 @@ i40e_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = i40e_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/iavf/iavf_rxtx.c +14 -6
index f817fbc49b..d61b32fcee 100644
@@ -57,6 +57,18 @@ iavf_proto_xtr_type_to_rxdid(uint8_t flex_type)
 				rxdid_map[flex_type] : IAVF_RXDID_COMMS_OVS_1;
 }
 
+static int
+iavf_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -69,12 +81,8 @@ iavf_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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 << IAVF_RX_DESC_STATUS_DD_SHIFT);
-	pmc->mask = rte_cpu_to_le_64(1 << IAVF_RX_DESC_STATUS_DD_SHIFT);
+	/* comparison callback */
+	pmc->fn = iavf_monitor_callback;
 
 	/* registers are 64-bit */
 	pmc->size = sizeof(uint64_t);
drivers/net/ice/ice_rxtx.c +14 -6
index 3f6e735984..5d7ab4f047 100644
@@ -27,6 +27,18 @@ 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;
 
+static int
+ice_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_16(1 << ICE_RX_FLEX_DESC_STATUS0_DD_S);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -39,12 +51,8 @@ ice_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ice_monitor_callback;
 
 	/* register is 16-bit */
 	pmc->size = sizeof(uint16_t);
drivers/net/ixgbe/ixgbe_rxtx.c +14 -6
index d69f36e977..c814a28cb4 100644
@@ -1369,6 +1369,18 @@ const uint32_t
 		RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_UDP,
 };
 
+static int
+ixgbe_monitor_callback(const uint64_t value,
+		const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t m = rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD);
+	/*
+	 * we expect the DD bit to be set to 1 if this descriptor was already
+	 * written to.
+	 */
+	return (value & m) == m ? -1 : 0;
+}
+
 int
 ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
@@ -1381,12 +1393,8 @@ ixgbe_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 	/* 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);
+	/* comparison callback */
+	pmc->fn = ixgbe_monitor_callback;
 
 	/* the registers are 32-bit */
 	pmc->size = sizeof(uint32_t);
drivers/net/mlx5/mlx5_rx.c +15 -2
index 777a1d6e45..8d47637892 100644
@@ -269,6 +269,18 @@ mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
 	return rx_queue_count(rxq);
 }
 
+#define CLB_VAL_IDX 0
+#define CLB_MSK_IDX 1
+static int
+mlx5_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t m = opaque[CLB_MSK_IDX];
+	const uint64_t v = opaque[CLB_VAL_IDX];
+
+	return (value & m) == v ? -1 : 0;
+}
+
 int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 {
 	struct mlx5_rxq_data *rxq = rx_queue;
@@ -282,8 +294,9 @@ int mlx5_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
 		return -rte_errno;
 	}
 	pmc->addr = &cqe->op_own;
-	pmc->val =  !!idx;
-	pmc->mask = MLX5_CQE_OWNER_MASK;
+	pmc->opaque[CLB_VAL_IDX] = !!idx;
+	pmc->opaque[CLB_MSK_IDX] = MLX5_CQE_OWNER_MASK;
+	pmc->fn = mlx5_monitor_callback;
 	pmc->size = sizeof(uint8_t);
 	return 0;
 }
lib/eal/include/generic/rte_power_intrinsics.h +26 -7
index dddca3d41c..c9aa52a86d 100644
@@ -18,19 +18,38 @@
  * which are architecture-dependent.
  */
 
+/** Size of the opaque data in monitor condition */
+#define RTE_POWER_MONITOR_OPAQUE_SZ 4
+
+/**
+ * Callback definition for monitoring conditions. Callbacks with this signature
+ * will be used by `rte_power_monitor()` to check if the entering of power
+ * optimized state should be aborted.
+ *
+ * @param val
+ *   The value read from memory.
+ * @param opaque
+ *   Callback-specific data.
+ *
+ * @return
+ *   0 if entering of power optimized state should proceed
+ *   -1 if entering of power optimized state should be aborted
+ */
+typedef int (*rte_power_monitor_clb_t)(const uint64_t val,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ]);
 struct rte_power_monitor_cond {
 	volatile void *addr;  /**< Address to monitor for changes */
-	uint64_t val;         /**< If the `mask` is non-zero, location pointed
-	                       *   to by `addr` will be read and compared
-	                       *   against this value.
-	                       */
-	uint64_t mask;   /**< 64-bit mask to extract value read from `addr` */
-	uint8_t size;    /**< Data size (in bytes) that will be used to compare
-	                  *   expected value (`val`) with data read from the
+	uint8_t size;    /**< Data size (in bytes) that will be read from the
 	                  *   monitored memory location (`addr`). Can be 1, 2,
 	                  *   4, or 8. Supplying any other value will result in
 	                  *   an error.
 	                  */
+	rte_power_monitor_clb_t fn; /**< Callback to be used to check if
+	                             *   entering power optimized state should
+	                             *   be aborted.
+	                             */
+	uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ];
+	/**< Callback-specific data */
 };
 
 /**
lib/eal/x86/rte_power_intrinsics.c +8 -9
index 39ea9fdecd..66fea28897 100644
@@ -76,6 +76,7 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
 	const unsigned int lcore_id = rte_lcore_id();
 	struct power_wait_status *s;
+	uint64_t cur_value;
 
 	/* prevent user from running this instruction if it's not supported */
 	if (!wait_supported)
@@ -91,6 +92,9 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	if (__check_val_size(pmc->size) < 0)
 		return -EINVAL;
 
+	if (pmc->fn == NULL)
+		return -EINVAL;
+
 	s = &wait_status[lcore_id];
 
 	/* update sleep address */
@@ -110,16 +114,11 @@ rte_power_monitor(const struct rte_power_monitor_cond *pmc,
 	/* now that we've put this address into monitor, we can unlock */
 	rte_spinlock_unlock(&s->lock);
 
-	/* if we have a comparison mask, we might not need to sleep at all */
-	if (pmc->mask) {
-		const uint64_t cur_value = __get_umwait_val(
-				pmc->addr, pmc->size);
-		const uint64_t masked = cur_value & pmc->mask;
+	cur_value = __get_umwait_val(pmc->addr, pmc->size);
 
-		/* if the masked value is already matching, abort */
-		if (masked == pmc->val)
-			goto end;
-	}
+	/* check if callback indicates we should abort */
+	if (pmc->fn(cur_value, pmc->opaque) != 0)
+		goto end;
 
 	/* execute UMWAIT */
 	asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
Signature · 1 line
2.25.1
#148 lore raw

PATCH v10 2/8 net/af_xdp: add power monitor support

In reply to Anatoly Burakov

Implement support for .get_monitor_addr in AF_XDP driver.

---

Notes:
v8:
- Fix checkpatch issue

v2:
- Rewrite using the callback mechanism

 drivers/net/af_xdp/rte_eth_af_xdp.c | 34 +++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
drivers/net/af_xdp/rte_eth_af_xdp.c +34 -0
index eb5660a3dc..989051dd6d 100644
@@ -37,6 +37,7 @@
 #include <rte_malloc.h>
 #include <rte_ring.h>
 #include <rte_spinlock.h>
+#include <rte_power_intrinsics.h>
 
 #include "compat.h"
 
@@ -788,6 +789,38 @@ eth_dev_configure(struct rte_eth_dev *dev)
 	return 0;
 }
 
+#define CLB_VAL_IDX 0
+static int
+eth_monitor_callback(const uint64_t value,
+		const uint64_t opaque[RTE_POWER_MONITOR_OPAQUE_SZ])
+{
+	const uint64_t v = opaque[CLB_VAL_IDX];
+	const uint64_t m = (uint32_t)~0;
+
+	/* if the value has changed, abort entering power optimized state */
+	return (value & m) == v ? 0 : -1;
+}
+
+static int
+eth_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
+{
+	struct pkt_rx_queue *rxq = rx_queue;
+	unsigned int *prod = rxq->rx.producer;
+	const uint32_t cur_val = rxq->rx.cached_prod; /* use cached value */
+
+	/* watch for changes in producer ring */
+	pmc->addr = (void *)prod;
+
+	/* store current value */
+	pmc->opaque[CLB_VAL_IDX] = cur_val;
+	pmc->fn = eth_monitor_callback;
+
+	/* AF_XDP producer ring index is 32-bit */
+	pmc->size = sizeof(uint32_t);
+
+	return 0;
+}
+
 static int
 eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 {
@@ -1448,6 +1481,7 @@ static const struct eth_dev_ops ops = {
 	.link_update = eth_link_update,
 	.stats_get = eth_stats_get,
 	.stats_reset = eth_stats_reset,
+	.get_monitor_addr = eth_get_monitor_addr
 };
 
 /** parse busy_budget argument */
Signature · 1 line
2.25.1
#149 lore raw

PATCH v10 3/8 doc: add PMD power management NIC feature

In reply to Anatoly Burakov

At this point, multiple different Ethernet drivers from multiple vendors will support the PMD power management scheme. It would be useful to add it to the NIC feature table to indicate support for it.

---

Notes:
v10:
- Added missing NIC feature support in ini files

 doc/guides/nics/features.rst          | 10 ++++++++++
 doc/guides/nics/features/af_xdp.ini   |  1 +
 doc/guides/nics/features/default.ini  |  1 +
 doc/guides/nics/features/i40e.ini     |  1 +
 doc/guides/nics/features/i40e_vf.ini  |  1 +
 doc/guides/nics/features/iavf.ini     |  1 +
 doc/guides/nics/features/ice.ini      |  1 +
 doc/guides/nics/features/ixgbe.ini    |  1 +
 doc/guides/nics/features/ixgbe_vf.ini |  1 +
 doc/guides/nics/features/mlx5.ini     |  1 +
 10 files changed, 19 insertions(+)
doc/guides/nics/features.rst +10 -0
index 403c2b03a3..a96e12d155 100644
@@ -912,6 +912,16 @@ Supports to get Rx/Tx packet burst mode information.
 * **[implements] eth_dev_ops**: ``rx_burst_mode_get``, ``tx_burst_mode_get``.
 * **[related] API**: ``rte_eth_rx_burst_mode_get()``, ``rte_eth_tx_burst_mode_get()``.
 
+.. _nic_features_get_monitor_addr:
+
+PMD power management using monitor addresses
+--------------------------------------------
+
+Supports getting a monitoring condition to use together with Ethernet PMD power
+management (see :doc:`../prog_guide/power_man` for more details).
+
+* **[implements] eth_dev_ops**: ``get_monitor_addr``
+
 .. _nic_features_other:
 
 Other dev ops not represented by a Feature
doc/guides/nics/features/af_xdp.ini +1 -0
index 36953c2dec..4e3f638bf5 100644
@@ -9,3 +9,4 @@ MTU update           = Y
 Promiscuous mode     = Y
 Stats per queue      = Y
 x86-64               = Y
+Power mgmt address monitor = Y
doc/guides/nics/features/default.ini +1 -0
index 3b55e0ccb0..f1e947bd9e 100644
@@ -76,6 +76,7 @@ x86-64               =
 Usage doc            =
 Design doc           =
 Perf doc             =
+Power mgmt address monitor =
 
 [rte_flow items]
 ah                   =
doc/guides/nics/features/i40e.ini +1 -0
index 1f3f5eb3ff..b6765d0e5a 100644
@@ -51,6 +51,7 @@ x86-32               = Y
 x86-64               = Y
 ARMv8                = Y
 Power8               = Y
+Power mgmt address monitor = Y
 
 [rte_flow items]
 ah                   = Y
doc/guides/nics/features/i40e_vf.ini +1 -0
index bac1bb4344..d5b163c1c1 100644
@@ -37,3 +37,4 @@ FreeBSD              = Y
 Linux                = Y
 x86-32               = Y
 x86-64               = Y
+Power mgmt address monitor = Y
doc/guides/nics/features/iavf.ini +1 -0
index 43a84a3bda..146b004da2 100644
@@ -33,6 +33,7 @@ FreeBSD              = Y
 Linux                = Y
 x86-32               = Y
 x86-64               = Y
+Power mgmt address monitor = Y
 
 [rte_flow items]
 ah                   = Y
doc/guides/nics/features/ice.ini +1 -0
index 1b9228c678..fbc81c654d 100644
@@ -42,6 +42,7 @@ Linux                = Y
 Windows              = Y
 x86-32               = Y
 x86-64               = Y
+Power mgmt address monitor = Y
 
 [rte_flow items]
 ah                   = Y
doc/guides/nics/features/ixgbe.ini +1 -0
index 93a9cc18ab..92228fe194 100644
@@ -54,6 +54,7 @@ Linux                = Y
 ARMv8                = Y
 x86-32               = Y
 x86-64               = Y
+Power mgmt address monitor = Y
 
 [rte_flow items]
 eth                  = Y
doc/guides/nics/features/ixgbe_vf.ini +1 -0
index 7161e61f9a..ea8342f2c9 100644
@@ -38,3 +38,4 @@ Linux                = Y
 ARMv8                = Y
 x86-32               = Y
 x86-64               = Y
+Power mgmt address monitor = Y
doc/guides/nics/features/mlx5.ini +1 -0
index 3b82ce41fd..2c7d9f6e8c 100644
@@ -51,6 +51,7 @@ Power8               = Y
 x86-32               = Y
 x86-64               = Y
 Usage doc            = Y
+Power mgmt address monitor = Y
 
 [rte_flow items]
 conntrack            = Y
Signature · 1 line
2.25.1
#150 lore raw

PATCH v10 4/8 eal: add power monitor for multiple events

In reply to Anatoly Burakov

Use RTM and WAITPKG instructions to perform a wait-for-writes similar to what UMWAIT does, but without the limitation of having to listen for just one event. This works because the optimized power state used by the TPAUSE instruction will cause a wake up on RTM transaction abort, so if we add the addresses we're interested in to the read-set, any write to those addresses will wake us up.

---

Notes:
v4:
- Fixed bugs in accessing the monitor condition
- Abort on any monitor condition not having a defined callback

v2:
- Adapt to callback mechanism

 lib/eal/arm/rte_power_intrinsics.c            | 11 +++
 lib/eal/include/generic/rte_cpuflags.h        |  2 +
 .../include/generic/rte_power_intrinsics.h    | 35 +++++++++
 lib/eal/ppc/rte_power_intrinsics.c            | 11 +++
 lib/eal/version.map                           |  3 +
 lib/eal/x86/rte_cpuflags.c                    |  2 +
 lib/eal/x86/rte_power_intrinsics.c            | 73 +++++++++++++++++++
 7 files changed, 137 insertions(+)
lib/eal/arm/rte_power_intrinsics.c +11 -0
index e83f04072a..78f55b7203 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/include/generic/rte_cpuflags.h +2 -0
index 28a5aecde8..d35551e931 100644
@@ -24,6 +24,8 @@ struct rte_cpu_intrinsics {
 	/**< indicates support for rte_power_monitor function */
 	uint32_t power_pause : 1;
 	/**< indicates support for rte_power_pause function */
+	uint32_t power_monitor_multi : 1;
+	/**< indicates support for rte_power_monitor_multi function */
 };
 
 /**
lib/eal/include/generic/rte_power_intrinsics.h +35 -0
index c9aa52a86d..04e8c2ab37 100644
@@ -128,4 +128,39 @@ int rte_power_monitor_wakeup(const unsigned int lcore_id);
 __rte_experimental
 int rte_power_pause(const uint64_t tsc_timestamp);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Monitor a set of addresses for changes. This will cause the CPU to enter an
+ * architecture-defined optimized power state until either one of the specified
+ * memory addresses is written to, a certain TSC timestamp is reached, or other
+ * reasons cause the CPU to wake up.
+ *
+ * Additionally, `expected` 64-bit values and 64-bit masks 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 do not match, the entering of
+ * optimized power state may be aborted.
+ *
+ * @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 pmc
+ *   An array of monitoring condition structures.
+ * @param num
+ *   Length of the `pmc` array.
+ * @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
+int rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp);
+
 #endif /* _RTE_POWER_INTRINSIC_H_ */
lib/eal/ppc/rte_power_intrinsics.c +11 -0
index 7fc9586da7..f00b58ade5 100644
@@ -38,3 +38,14 @@ rte_power_monitor_wakeup(const unsigned int lcore_id)
 
 	return -ENOTSUP;
 }
+
+int
+rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
+		const uint32_t num, const uint64_t tsc_timestamp)
+{
+	RTE_SET_USED(pmc);
+	RTE_SET_USED(num);
+	RTE_SET_USED(tsc_timestamp);
+
+	return -ENOTSUP;
+}
lib/eal/version.map +3 -0
index 2df65c6903..887012d02a 100644
@@ -423,6 +423,9 @@ EXPERIMENTAL {
 	rte_version_release; # WINDOWS_NO_EXPORT
 	rte_version_suffix; # WINDOWS_NO_EXPORT
 	rte_version_year; # WINDOWS_NO_EXPORT
+
+	# added in 21.08
+	rte_power_monitor_multi; # WINDOWS_NO_EXPORT
 };
 
 INTERNAL {
lib/eal/x86/rte_cpuflags.c +2 -0
index a96312ff7f..d339734a8c 100644
@@ -189,5 +189,7 @@ rte_cpu_get_intrinsics_support(struct rte_cpu_intrinsics *intrinsics)
 	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_WAITPKG)) {
 		intrinsics->power_monitor = 1;
 		intrinsics->power_pause = 1;
+		if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_RTM))
+			intrinsics->power_monitor_multi = 1;
 	}
 }
lib/eal/x86/rte_power_intrinsics.c +73 -0

100 lines not on this page · raw message

Signature · 1 line
2.25.1
#151 lore raw

PATCH v10 5/8 power: remove thread safety from PMD power API's

In reply to Anatoly Burakov

Currently, we expect that only one callback can be active at any given moment, for a particular queue configuration, which is relatively easy to implement in a thread-safe way. However, we're about to add support for multiple queues per lcore, which will greatly increase the possibility of various race conditions.

We could have used something like an RCU for this use case, but absent of a pressing need for thread safety we'll go the easy way and just mandate that the API's are to be called when all affected ports are stopped, and document this limitation. This greatly simplifies the rte_power_monitor-related code.

---

Notes:
v2:
- Add check for stopped queue
- Clarified doc message
- Added release notes

 doc/guides/rel_notes/release_21_08.rst |   4 +
 lib/power/meson.build                  |   3 +
 lib/power/rte_power_pmd_mgmt.c         | 133 ++++++++++---------------
 lib/power/rte_power_pmd_mgmt.h         |   6 ++
 4 files changed, 66 insertions(+), 80 deletions(-)
doc/guides/rel_notes/release_21_08.rst +4 -0
index 912fb13b84..b9a3caabf0 100644
@@ -146,6 +146,10 @@ API Changes
 
 * eal: the ``rte_power_intrinsics`` API changed to use a callback mechanism.
 
+* rte_power: The experimental PMD power management API is no longer considered
+  to be thread safe; all Rx queues affected by the API will now need to be
+  stopped before making any changes to the power management scheme.
+
 
 ABI Changes
 -----------
lib/power/meson.build +3 -0
index 36e5a65874..bf937acde4 100644
@@ -22,4 +22,7 @@ headers = files(
         'rte_power_pmd_mgmt.h',
         'rte_power_guest_channel.h',
 )
+if cc.has_argument('-Wno-cast-qual')
+    cflags += '-Wno-cast-qual'
+endif
 deps += ['timer', 'ethdev']
lib/power/rte_power_pmd_mgmt.c +53 -80

209 lines not on this page · raw message

lib/power/rte_power_pmd_mgmt.h +6 -0
index 7a0ac24625..444e7b8a66 100644
@@ -43,6 +43,9 @@ enum rte_power_pmd_mgmt_type {
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue will be polled from.
  * @param port_id
@@ -69,6 +72,9 @@ rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id,
  *
  * @note This function is not thread-safe.
  *
+ * @warning This function must be called when all affected Ethernet queues are
+ *   stopped and no Rx/Tx is in progress!
+ *
  * @param lcore_id
  *   The lcore the Rx queue is polled from.
  * @param port_id
Signature · 1 line
2.25.1
#152 lore raw

PATCH v10 6/8 power: support callbacks for multiple Rx queues

In reply to Anatoly Burakov

Currently, there is a hard limitation on the PMD power management support that only allows it to support a single queue per lcore. This is not ideal as most DPDK use cases will poll multiple queues per core.

The PMD power management mechanism relies on ethdev Rx callbacks, so it is very difficult to implement such support because callbacks are effectively stateless and have no visibility into what the other ethdev devices are doing. This places limitations on what we can do within the framework of Rx callbacks, but the basics of this implementation are as follows:

- Replace per-queue structures with per-lcore ones, so that any device polled from the same lcore can share data
- Any queue that is going to be polled from a specific lcore has to be added to the list of queues to poll, so that the callback is aware of other queues being polled by the same lcore
- Both the empty poll counter and the actual power saving mechanism is shared between all queues polled on a particular lcore, and is only activated when all queues in the list were polled and were determined to have no traffic.
- The limitation on UMWAIT-based polling is not removed because UMWAIT is incapable of monitoring more than one address.

Also, while we're at it, update and improve the docs.

---

Notes:
v8:
- Added a comment explaining that we want to sleep on each empty poll after threshold has been reached

v7:
- Fix bug where initial sleep target was always set to zero
- Fix logic in handling of n_queues_ready_to_sleep counter
- Update documentation on hardware requirements

v6:
- Track each individual queue sleep status (Konstantin)
- Fix segfault (Dave)

v5:
- Remove the "power save queue" API and replace it with mechanism suggested by Konstantin

v3:
- Move the list of supported NICs to NIC feature table

v2:
- Use a TAILQ for queues instead of a static array
- Address feedback from Konstantin
- Add additional checks for stopped queues

 doc/guides/prog_guide/power_man.rst    |  69 ++--
 doc/guides/rel_notes/release_21_08.rst |   5 +
 lib/power/rte_power_pmd_mgmt.c         | 460 +++++++++++++++++++------
 3 files changed, 398 insertions(+), 136 deletions(-)
doc/guides/prog_guide/power_man.rst +41 -28
index c70ae128ac..0e66878892 100644
@@ -198,34 +198,45 @@ Ethernet 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 ``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).
+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
+   monitor the Ethernet PMD RX descriptor address, waking the CPU up whenever
+   there's new traffic. Support for this scheme may not be available on all
+   platforms, and further limitations may apply (see below).
+
+* 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 supported by the underlying platform, use ``rte_pause()``.
+
+* Frequency scaling
+   This power saving scheme will use ``librte_power`` library functionality to
+   scale the core frequency up/down depending on traffic volume.
+
+The "monitor" mode is only supported in the following configurations and scenarios:
+
+* On Linux* x86_64, `rte_power_monitor()` requires WAITPKG instruction set being
+  supported by the CPU. Please refer to your platform documentation for further
+  information.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor()`` is supported by the platform, then monitoring will be
+  limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
+  monitored from a different lcore).
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
+  ``rte_power_monitor()`` function is not supported, then monitor mode will not
+  be supported.
+
+* Not all Ethernet drivers support monitoring, even if the underlying
+  platform may support the necessary CPU instructions. Please refer to
+  :doc:`../nics/overview` for more information.
+
 
 API Overview for Ethernet PMD Power Management
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -242,3 +253,5 @@ References
 
 *   The :doc:`../sample_app_ug/vm_power_management`
     chapter in the :doc:`../sample_app_ug/index` section.
+
+*   The :doc:`../nics/overview` chapter in the :doc:`../nics/index` section
doc/guides/rel_notes/release_21_08.rst +5 -0
index b9a3caabf0..ca28ebe461 100644
@@ -112,6 +112,11 @@ New Features
 
   Added support for cppc_cpufreq driver which works on most arm64 platforms.
 
+* **Added multi-queue support to Ethernet PMD Power Management**
+
+  The experimental PMD power management API now supports managing
+  multiple Ethernet Rx queues per lcore.
+
 
 Removed Items
 -------------
lib/power/rte_power_pmd_mgmt.c +352 -108

575 lines not on this page · raw message

Signature · 1 line
2.25.1
#153 lore raw

PATCH v10 7/8 power: support monitoring multiple Rx queues

In reply to Anatoly Burakov

Use the new multi-monitor intrinsic to allow monitoring multiple ethdev Rx queues while entering the energy efficient power state. The multi version will be used unconditionally if supported, and the UMWAIT one will only be used when multi-monitor is not supported by the hardware.

---

Notes:
v6:
- Fix the missed feedback from v5

v4:
- Fix possible out of bounds access
- Added missing index increment

 doc/guides/prog_guide/power_man.rst | 15 ++++--
 lib/power/rte_power_pmd_mgmt.c      | 82 ++++++++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 7 deletions(-)
doc/guides/prog_guide/power_man.rst +10 -5
index 0e66878892..e387d7811e 100644
@@ -221,17 +221,22 @@ power saving whenever empty poll count reaches a certain number.
 The "monitor" mode is only supported in the following configurations and scenarios:
 
 * On Linux* x86_64, `rte_power_monitor()` requires WAITPKG instruction set being
-  supported by the CPU. Please refer to your platform documentation for further
-  information.
+  supported by the CPU, while `rte_power_monitor_multi()` requires WAITPKG and
+  RTM instruction sets being supported by the CPU. RTM instruction set may also
+  require booting the Linux with `tsx=on` command line parameter. Please refer
+  to your platform documentation for further information.
 
 * If ``rte_cpu_get_intrinsics_support()`` function indicates that
+  ``rte_power_monitor_multi()`` function is supported by the platform, then
+  monitoring multiple Ethernet Rx queues for traffic will be supported.
+
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that only
   ``rte_power_monitor()`` is supported by the platform, then monitoring will be
   limited to a mapping of 1 core 1 queue (thus, each Rx queue will have to be
   monitored from a different lcore).
 
-* If ``rte_cpu_get_intrinsics_support()`` function indicates that the
-  ``rte_power_monitor()`` function is not supported, then monitor mode will not
-  be supported.
+* If ``rte_cpu_get_intrinsics_support()`` function indicates that neither of the
+  two monitoring functions are supported, then monitor mode will not be supported.
 
 * Not all Ethernet drivers support monitoring, even if the underlying
   platform may support the necessary CPU instructions. Please refer to
lib/power/rte_power_pmd_mgmt.c +80 -2

119 lines not on this page · raw message

Signature · 1 line
2.25.1
#154 lore raw

PATCH v10 8/8 examples/l3fwd-power: support multiq in PMD modes

In reply to Anatoly Burakov

Currently, l3fwd-power enforces the limitation of having one queue per lcore. This is no longer necessary, so remove the limitation.

---
 examples/l3fwd-power/main.c | 6 ------
 1 file changed, 6 deletions(-)
examples/l3fwd-power/main.c +0 -6
index f8dfed1634..52f56dc405 100644
@@ -2723,12 +2723,6 @@ main(int argc, char **argv)
 		printf("\nInitializing rx queues on lcore %u ... ", lcore_id );
 		fflush(stdout);
 
-		/* PMD power management mode can only do 1 queue per core */
-		if (app_mode == APP_MODE_PMD_MGMT && qconf->n_rx_queue > 1) {
-			rte_exit(EXIT_FAILURE,
-				"In PMD power management mode, only one queue per lcore is allowed\n");
-		}
-
 		/* init RX queues */
 		for(queue = 0; queue < qconf->n_rx_queue; ++queue) {
 			struct rte_eth_rxconf rxq_conf;
Signature · 1 line
2.25.1
#155 lore raw
In reply to Anatoly Burakov
Quoted 51 lines · Anatoly Burakov

This patchset introduces several changes related to PMD power management:

- Changed monitoring intrinsics to use callbacks as a comparison function, based on previous patchset [1] but incorporating feedback [2] - this hopefully will make it possible to add support for .get_monitor_addr in virtio
- Add a new intrinsic to monitor multiple addresses, based on RTM instruction set and the TPAUSE instruction
- Add support for PMD power management on multiple queues, as well as all accompanying infrastructure and example apps changes

v10:
- Added missing changes to NIC feature .ini files

v9:
- Added all missing Acks and Tests
- Added a new commit with NIC features
- Addressed minor issues raised in review

v8:
- Fixed checkpatch issue
- Added comment explaining empty poll handling (Konstantin)

v7:
- Fixed various bugs

v6:
- Improved the algorithm for multi-queue sleep
- Fixed segfault and addressed other feedback

v5:
- Removed "power save queue" API and replaced with mechanism suggested by Konstantin
- Addressed other feedback

v4:
- Replaced raw number with a macro
- Fixed all the bugs found by Konstantin
- Some other minor corrections

v3:
- Moved some doc updates to NIC features list

v2:
- Changed check inversion to callbacks
- Addressed feedback from Konstantin
- Added doc updates where necessary

[1] http://patches.dpdk.org/project/dpdk/list/?series=16930&state=*
[2] http://patches.dpdk.org/project/dpdk/patch/819ef1ace187365a615d3383e54579e3d9fb216e.1620747068.git.anatoly.burakov@intel.com/#133274

Quoted 9 lines

Anatoly Burakov (8):
eal: use callbacks for power monitoring comparison net/af_xdp: add power monitor support
doc: add PMD power management NIC feature
eal: add power monitor for multiple events
power: remove thread safety from PMD power API's
power: support callbacks for multiple Rx queues
power: support monitoring multiple Rx queues examples/l3fwd-power: support multiq in PMD modes

Overall, the series lgtm.

I still have a comment on the opaque pointer passed in callbacks. This is not blocking, we can still go with followup patches in this release.

It would be great if drivers maintainers could implement this new ops in their driver or give feedback on what should be enhanced.

Series applied, thanks.

Signature · 1 line
David Marchand