For Arm arch, use WFE instruction to enable power monitor API, and use SEV instruction to enable wake up API.
Signed-off-by: Feifei Wang <feifei.wa...@arm.com> Reviewed-by: Ruifeng Wang <ruifeng.w...@arm.com> Acked-by: David Hunt <david.h...@intel.com> --- doc/guides/rel_notes/release_23_03.rst | 4 ++ lib/eal/arm/include/rte_pause_64.h | 5 ++- lib/eal/arm/rte_cpuflags.c | 5 +++ lib/eal/arm/rte_power_intrinsics.c | 55 ++++++++++++++++++++++++-- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/doc/guides/rel_notes/release_23_03.rst b/doc/guides/rel_notes/release_23_03.rst index 7da3d19278..4a5995f2c8 100644 --- a/doc/guides/rel_notes/release_23_03.rst +++ b/doc/guides/rel_notes/release_23_03.rst @@ -192,6 +192,10 @@ New Features * Added support to capture packets at each graph node with packet metadata and node name. +* **Added ARM support for power monitor in the power management library.** + * Added power monitor and wake up API support with WFE/SVE + instructions for Arm architecture. + Removed Items ------------- diff --git a/lib/eal/arm/include/rte_pause_64.h b/lib/eal/arm/include/rte_pause_64.h index c21600ca96..5f70e97481 100644 --- a/lib/eal/arm/include/rte_pause_64.h +++ b/lib/eal/arm/include/rte_pause_64.h @@ -25,9 +25,12 @@ static inline void rte_pause(void) #ifdef RTE_WAIT_UNTIL_EQUAL_ARCH_DEFINED -/* Send an event to quit WFE. */ +/* Send a local event to quit WFE. */ #define __RTE_ARM_SEVL() { asm volatile("sevl" : : : "memory"); } +/* Send a global event to quit WFE for all cores. */ +#define __RTE_ARM_SEV() { asm volatile("sev" : : : "memory"); } + /* Put processor into low power WFE(Wait For Event) state. */ #define __RTE_ARM_WFE() { asm volatile("wfe" : : : "memory"); } diff --git a/lib/eal/arm/rte_cpuflags.c b/lib/eal/arm/rte_cpuflags.c index 93461191c7..90b80709fd 100644 --- a/lib/eal/arm/rte_cpuflags.c +++ b/lib/eal/arm/rte_cpuflags.c @@ -163,4 +163,9 @@ void rte_cpu_get_intrinsics_support(struct rte_cpu_intrinsics *intrinsics) { memset(intrinsics, 0, sizeof(*intrinsics)); + +#ifdef RTE_ARM_USE_WFE + intrinsics->power_monitor = 1; +#endif + } diff --git a/lib/eal/arm/rte_power_intrinsics.c b/lib/eal/arm/rte_power_intrinsics.c index 13f6a3264d..b518401225 100644 --- a/lib/eal/arm/rte_power_intrinsics.c +++ b/lib/eal/arm/rte_power_intrinsics.c @@ -7,16 +7,57 @@ #include "rte_power_intrinsics.h" /** - * This function is not supported on ARM. + * This function uses WFE instruction to make lcore suspend + * execution on ARM. + * Note that timestamp based timeout is not supported yet. */ int rte_power_monitor(const struct rte_power_monitor_cond *pmc, const uint64_t tsc_timestamp) { - RTE_SET_USED(pmc); RTE_SET_USED(tsc_timestamp); +#ifdef RTE_ARM_USE_WFE + const unsigned int lcore_id = rte_lcore_id(); + uint64_t cur_value; + + /* prevent non-EAL thread from using this API */ + if (lcore_id >= RTE_MAX_LCORE) + return -EINVAL; + + if (pmc == NULL) + return -EINVAL; + + if (pmc->fn == NULL) + return -EINVAL; + + switch (pmc->size) { + case sizeof(uint8_t): + __RTE_ARM_LOAD_EXC_8(pmc->addr, cur_value, __ATOMIC_RELAXED); + __RTE_ARM_WFE() + break; + case sizeof(uint16_t): + __RTE_ARM_LOAD_EXC_16(pmc->addr, cur_value, __ATOMIC_RELAXED); + __RTE_ARM_WFE() + break; + case sizeof(uint32_t): + __RTE_ARM_LOAD_EXC_32(pmc->addr, cur_value, __ATOMIC_RELAXED); + __RTE_ARM_WFE() + break; + case sizeof(uint64_t): + __RTE_ARM_LOAD_EXC_64(pmc->addr, cur_value, __ATOMIC_RELAXED); + __RTE_ARM_WFE() + break; + default: + return -EINVAL; /* unexpected size */ + } + + return 0; +#else + RTE_SET_USED(pmc); + return -ENOTSUP; +#endif } /** @@ -31,14 +72,22 @@ rte_power_pause(const uint64_t tsc_timestamp) } /** - * This function is not supported on ARM. + * This function uses SEV instruction to wake up all cores + * on ARM. + * Note that lcore_id is not used here. */ int rte_power_monitor_wakeup(const unsigned int lcore_id) { RTE_SET_USED(lcore_id); +#ifdef RTE_ARM_USE_WFE + __RTE_ARM_SEV() + + return 0; +#else return -ENOTSUP; +#endif } int -- 2.25.1