get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/87107/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 87107,
    "url": "http://patchwork.dpdk.org/api/patches/87107/?format=api",
    "web_url": "http://patchwork.dpdk.org/project/dpdk/patch/827276e0f328b0e67a45e64fb9244a12b109aeea.1611335511.git.anatoly.burakov@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<827276e0f328b0e67a45e64fb9244a12b109aeea.1611335511.git.anatoly.burakov@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/827276e0f328b0e67a45e64fb9244a12b109aeea.1611335511.git.anatoly.burakov@intel.com",
    "date": "2021-01-22T17:12:16",
    "name": "[v20,3/4] power: add PMD power management API and callback",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "ebda94650bfbac2c7684dc491787337a0201001a",
    "submitter": {
        "id": 4,
        "url": "http://patchwork.dpdk.org/api/people/4/?format=api",
        "name": "Anatoly Burakov",
        "email": "anatoly.burakov@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patchwork.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patchwork.dpdk.org/project/dpdk/patch/827276e0f328b0e67a45e64fb9244a12b109aeea.1611335511.git.anatoly.burakov@intel.com/mbox/",
    "series": [
        {
            "id": 14908,
            "url": "http://patchwork.dpdk.org/api/series/14908/?format=api",
            "web_url": "http://patchwork.dpdk.org/project/dpdk/list/?series=14908",
            "date": "2021-01-22T17:12:13",
            "name": "Add PMD power management",
            "version": 20,
            "mbox": "http://patchwork.dpdk.org/series/14908/mbox/"
        }
    ],
    "comments": "http://patchwork.dpdk.org/api/patches/87107/comments/",
    "check": "success",
    "checks": "http://patchwork.dpdk.org/api/patches/87107/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 2EA76A0A0A;\n\tFri, 22 Jan 2021 18:13:05 +0100 (CET)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 83043141066;\n\tFri, 22 Jan 2021 18:12:30 +0100 (CET)",
            "from mga04.intel.com (mga04.intel.com [192.55.52.120])\n by mails.dpdk.org (Postfix) with ESMTP id 1FC7814105A\n for <dev@dpdk.org>; Fri, 22 Jan 2021 18:12:23 +0100 (CET)",
            "from orsmga005.jf.intel.com ([10.7.209.41])\n by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 22 Jan 2021 09:12:23 -0800",
            "from silpixa00399498.ir.intel.com (HELO\n silpixa00399498.ger.corp.intel.com) ([10.237.222.179])\n by orsmga005.jf.intel.com with ESMTP; 22 Jan 2021 09:12:21 -0800"
        ],
        "IronPort-SDR": [
            "\n nfXjoqbPwgI6Qkhnv/ntJjet6TLZotZNvC12QU6Js1XFmO48MA5gCHIVcaJZH1oLofhj86iLMk\n X6b0k3oeJ1sg==",
            "\n z/wnFPHUYiu8KB3waiThB0H60CjV9/AH4ZDnvfU/QhEr7Fukxf/z+7LneIIvJdkCoc9fWuo4oh\n PVFFh2PuL8tg=="
        ],
        "X-IronPort-AV": [
            "E=McAfee;i=\"6000,8403,9872\"; a=\"176901764\"",
            "E=Sophos;i=\"5.79,367,1602572400\"; d=\"scan'208\";a=\"176901764\"",
            "E=Sophos;i=\"5.79,367,1602572400\"; d=\"scan'208\";a=\"571191495\""
        ],
        "X-ExtLoop1": "1",
        "From": "Anatoly Burakov <anatoly.burakov@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Liang Ma <liang.j.ma@intel.com>, David Hunt <david.hunt@intel.com>,\n Ray Kinsella <mdr@ashroe.eu>, Neil Horman <nhorman@tuxdriver.com>,\n thomas@monjalon.net",
        "Date": "Fri, 22 Jan 2021 17:12:16 +0000",
        "Message-Id": "\n <827276e0f328b0e67a45e64fb9244a12b109aeea.1611335511.git.anatoly.burakov@intel.com>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<cover.1611335511.git.anatoly.burakov@intel.com>",
        "References": "<cover.1611143367.git.anatoly.burakov@intel.com>\n <cover.1611335511.git.anatoly.burakov@intel.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v20 3/4] power: add PMD power management API and\n callback",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "From: Liang Ma <liang.j.ma@intel.com>\n\nAdd a simple on/off switch that will enable saving power when no\npackets are arriving. It is based on counting the number of empty\npolls and, when the number reaches a certain threshold, entering an\narchitecture-defined optimized power state that will either wait\nuntil a TSC timestamp expires, or when packets arrive.\n\nThis API mandates a core-to-single-queue mapping (that is, multiple\nqueued per device are supported, but they have to be polled on different\ncores).\n\nThis design is using PMD RX callbacks.\n\n1. UMWAIT/UMONITOR:\n\n   When a certain threshold of empty polls is reached, the core will go\n   into a power optimized sleep while waiting on an address of next RX\n   descriptor to be written to.\n\n2. TPAUSE/Pause instruction\n\n   This method uses the pause (or TPAUSE, if available) instruction to\n   avoid busy polling.\n\n3. Frequency scaling\n   Reuse existing DPDK power library to scale up/down core frequency\n   depending on traffic volume.\n\nSigned-off-by: Liang Ma <liang.j.ma@intel.com>\nSigned-off-by: Anatoly Burakov <anatoly.burakov@intel.com>\nAcked-by: David Hunt <david.hunt@intel.com>\n---\n\nNotes:\n    v17:\n    - Added memory barriers suggested by Konstantin\n    - Removed the BUSY state\n\n doc/guides/prog_guide/power_man.rst    |  41 +++\n doc/guides/rel_notes/release_21_02.rst |  10 +\n lib/librte_power/meson.build           |   5 +-\n lib/librte_power/rte_power_pmd_mgmt.c  | 365 +++++++++++++++++++++++++\n lib/librte_power/rte_power_pmd_mgmt.h  |  91 ++++++\n lib/librte_power/version.map           |   5 +\n 6 files changed, 515 insertions(+), 2 deletions(-)\n create mode 100644 lib/librte_power/rte_power_pmd_mgmt.c\n create mode 100644 lib/librte_power/rte_power_pmd_mgmt.h",
    "diff": "diff --git a/doc/guides/prog_guide/power_man.rst b/doc/guides/prog_guide/power_man.rst\nindex 0a3755a901..f36ba0027c 100644\n--- a/doc/guides/prog_guide/power_man.rst\n+++ b/doc/guides/prog_guide/power_man.rst\n@@ -192,6 +192,47 @@ User Cases\n ----------\n The mechanism can applied to any device which is based on polling. e.g. NIC, FPGA.\n \n+Ethernet PMD Power Management API\n+---------------------------------\n+\n+Abstract\n+~~~~~~~~\n+Existing power management mechanisms require developers to change application\n+design or change code to make use of it. The PMD power management API provides a\n+convenient alternative by utilizing Ethernet PMD RX callbacks, and triggering\n+power saving whenever empty poll count reaches a certain number.\n+\n+  * Monitor\n+\n+   This power saving scheme will put the CPU into optimized power state and use\n+   the ``rte_power_monitor()`` function to monitor the Ethernet PMD RX\n+   descriptor address, and wake the CPU up whenever there's new traffic.\n+\n+  * Pause\n+\n+   This power saving scheme will avoid busy polling by either entering\n+   power-optimized sleep state with ``rte_power_pause()`` function, or, if it's\n+   not available, use ``rte_pause()``.\n+\n+  * Frequency scaling\n+\n+   This power saving scheme will use existing ``librte_power`` library\n+   functionality to scale the core frequency up/down depending on traffic\n+   volume.\n+\n+\n+.. note::\n+\n+   Currently, this power management API is limited to mandatory mapping of 1\n+   queue to 1 core (multiple queues are supported, but they must be polled from\n+   different cores).\n+\n+API Overview for Ethernet PMD Power Management\n+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+* **Queue Enable**: Enable specific power scheme for certain queue/port/core.\n+\n+* **Queue Disable**: Disable power scheme for certain queue/port/core.\n+\n References\n ----------\n \ndiff --git a/doc/guides/rel_notes/release_21_02.rst b/doc/guides/rel_notes/release_21_02.rst\nindex ae36b6a3fa..81b9d93cd0 100644\n--- a/doc/guides/rel_notes/release_21_02.rst\n+++ b/doc/guides/rel_notes/release_21_02.rst\n@@ -122,6 +122,16 @@ New Features\n   * Added support for aes-cbc sha256-128-hmac cipher combination in OCTEON TX2\n     crypto PMD lookaside protocol offload for IPsec.\n \n+* **Added Ethernet PMD power management helper API.**\n+\n+  A new helper API has been added to make using Ethernet PMD power management\n+  easier for the user: ``rte_power_ethdev_pmgmt_queue_enable()``. Three power\n+  management schemes are supported initially:\n+\n+  * Power saving based on UMWAIT instruction (x86 only)\n+  * Power saving based on ``rte_pause()`` (generic) or TPAUSE instruction (x86 only)\n+  * Power saving based on frequency scaling through the ``librte_power`` library\n+\n \n Removed Items\n -------------\ndiff --git a/lib/librte_power/meson.build b/lib/librte_power/meson.build\nindex 4b4cf1b90b..e5a11cb834 100644\n--- a/lib/librte_power/meson.build\n+++ b/lib/librte_power/meson.build\n@@ -9,6 +9,7 @@ sources = files('rte_power.c', 'power_acpi_cpufreq.c',\n \t\t'power_kvm_vm.c', 'guest_channel.c',\n \t\t'rte_power_empty_poll.c',\n \t\t'power_pstate_cpufreq.c',\n+\t\t'rte_power_pmd_mgmt.c',\n \t\t'power_common.c')\n-headers = files('rte_power.h','rte_power_empty_poll.h')\n-deps += ['timer']\n+headers = files('rte_power.h','rte_power_empty_poll.h','rte_power_pmd_mgmt.h')\n+deps += ['timer', 'ethdev']\ndiff --git a/lib/librte_power/rte_power_pmd_mgmt.c b/lib/librte_power/rte_power_pmd_mgmt.c\nnew file mode 100644\nindex 0000000000..454ef7091e\n--- /dev/null\n+++ b/lib/librte_power/rte_power_pmd_mgmt.c\n@@ -0,0 +1,365 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Intel Corporation\n+ */\n+\n+#include <rte_lcore.h>\n+#include <rte_cycles.h>\n+#include <rte_cpuflags.h>\n+#include <rte_malloc.h>\n+#include <rte_ethdev.h>\n+#include <rte_power_intrinsics.h>\n+\n+#include \"rte_power_pmd_mgmt.h\"\n+\n+#define EMPTYPOLL_MAX  512\n+\n+/* store some internal state */\n+static struct pmd_conf_data {\n+\t/** what do we support? */\n+\tstruct rte_cpu_intrinsics intrinsics_support;\n+\t/** pre-calculated tsc diff for 1us */\n+\tuint64_t tsc_per_us;\n+\t/** how many rte_pause can we fit in a microsecond? */\n+\tuint64_t pause_per_us;\n+} global_data;\n+\n+/**\n+ * Possible power management states of an ethdev port.\n+ */\n+enum pmd_mgmt_state {\n+\t/** Device power management is disabled. */\n+\tPMD_MGMT_DISABLED = 0,\n+\t/** Device power management is enabled. */\n+\tPMD_MGMT_ENABLED\n+};\n+\n+struct pmd_queue_cfg {\n+\tvolatile enum pmd_mgmt_state pwr_mgmt_state;\n+\t/**< State of power management for this queue */\n+\tenum rte_power_pmd_mgmt_type cb_mode;\n+\t/**< Callback mode for this queue */\n+\tconst struct rte_eth_rxtx_callback *cur_cb;\n+\t/**< Callback instance */\n+\tvolatile bool umwait_in_progress;\n+\t/**< are we currently sleeping? */\n+\tuint64_t empty_poll_stats;\n+\t/**< Number of empty polls */\n+} __rte_cache_aligned;\n+\n+static struct pmd_queue_cfg port_cfg[RTE_MAX_ETHPORTS][RTE_MAX_QUEUES_PER_PORT];\n+\n+static void\n+calc_tsc(void)\n+{\n+\tconst uint64_t hz = rte_get_timer_hz();\n+\tconst uint64_t tsc_per_us = hz / US_PER_S; /* 1us */\n+\n+\tglobal_data.tsc_per_us = tsc_per_us;\n+\n+\t/* only do this if we don't have tpause */\n+\tif (!global_data.intrinsics_support.power_pause) {\n+\t\tconst uint64_t start = rte_rdtsc_precise();\n+\t\tconst uint32_t n_pauses = 10000;\n+\t\tdouble us, us_per_pause;\n+\t\tuint64_t end;\n+\t\tunsigned int i;\n+\n+\t\t/* estimate number of rte_pause() calls per us*/\n+\t\tfor (i = 0; i < n_pauses; i++)\n+\t\t\trte_pause();\n+\n+\t\tend = rte_rdtsc_precise();\n+\t\tus = (end - start) / (double)tsc_per_us;\n+\t\tus_per_pause = us / n_pauses;\n+\n+\t\tglobal_data.pause_per_us = (uint64_t)(1.0 / us_per_pause);\n+\t}\n+}\n+\n+static uint16_t\n+clb_umwait(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts __rte_unused,\n+\t\tuint16_t nb_rx, uint16_t max_pkts __rte_unused,\n+\t\tvoid *addr __rte_unused)\n+{\n+\n+\tstruct pmd_queue_cfg *q_conf;\n+\n+\tq_conf = &port_cfg[port_id][qidx];\n+\n+\tif (unlikely(nb_rx == 0)) {\n+\t\tq_conf->empty_poll_stats++;\n+\t\tif (unlikely(q_conf->empty_poll_stats > EMPTYPOLL_MAX)) {\n+\t\t\tstruct rte_power_monitor_cond pmc;\n+\t\t\tuint16_t ret;\n+\n+\t\t\t/*\n+\t\t\t * we might get a cancellation request while being\n+\t\t\t * inside the callback, in which case the wakeup\n+\t\t\t * wouldn't work because it would've arrived too early.\n+\t\t\t *\n+\t\t\t * to get around this, we notify the other thread that\n+\t\t\t * we're sleeping, so that it can spin until we're done.\n+\t\t\t * unsolicited wakeups are perfectly safe.\n+\t\t\t */\n+\t\t\tq_conf->umwait_in_progress = true;\n+\n+\t\t\trte_atomic_thread_fence(__ATOMIC_SEQ_CST);\n+\n+\t\t\t/* check if we need to cancel sleep */\n+\t\t\tif (q_conf->pwr_mgmt_state == PMD_MGMT_ENABLED) {\n+\t\t\t\t/* use monitoring condition to sleep */\n+\t\t\t\tret = rte_eth_get_monitor_addr(port_id, qidx,\n+\t\t\t\t\t\t&pmc);\n+\t\t\t\tif (ret == 0)\n+\t\t\t\t\trte_power_monitor(&pmc, -1ULL);\n+\t\t\t}\n+\t\t\tq_conf->umwait_in_progress = false;\n+\n+\t\t\trte_atomic_thread_fence(__ATOMIC_SEQ_CST);\n+\t\t}\n+\t} else\n+\t\tq_conf->empty_poll_stats = 0;\n+\n+\treturn nb_rx;\n+}\n+\n+static uint16_t\n+clb_pause(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts __rte_unused,\n+\t\tuint16_t nb_rx, uint16_t max_pkts __rte_unused,\n+\t\tvoid *addr __rte_unused)\n+{\n+\tstruct pmd_queue_cfg *q_conf;\n+\n+\tq_conf = &port_cfg[port_id][qidx];\n+\n+\tif (unlikely(nb_rx == 0)) {\n+\t\tq_conf->empty_poll_stats++;\n+\t\t/* sleep for 1 microsecond */\n+\t\tif (unlikely(q_conf->empty_poll_stats > EMPTYPOLL_MAX)) {\n+\t\t\t/* use tpause if we have it */\n+\t\t\tif (global_data.intrinsics_support.power_pause) {\n+\t\t\t\tconst uint64_t cur = rte_rdtsc();\n+\t\t\t\tconst uint64_t wait_tsc =\n+\t\t\t\t\t\tcur + global_data.tsc_per_us;\n+\t\t\t\trte_power_pause(wait_tsc);\n+\t\t\t} else {\n+\t\t\t\tuint64_t i;\n+\t\t\t\tfor (i = 0; i < global_data.pause_per_us; i++)\n+\t\t\t\t\trte_pause();\n+\t\t\t}\n+\t\t}\n+\t} else\n+\t\tq_conf->empty_poll_stats = 0;\n+\n+\treturn nb_rx;\n+}\n+\n+static uint16_t\n+clb_scale_freq(uint16_t port_id, uint16_t qidx,\n+\t\tstruct rte_mbuf **pkts __rte_unused, uint16_t nb_rx,\n+\t\tuint16_t max_pkts __rte_unused, void *_  __rte_unused)\n+{\n+\tstruct pmd_queue_cfg *q_conf;\n+\n+\tq_conf = &port_cfg[port_id][qidx];\n+\n+\tif (unlikely(nb_rx == 0)) {\n+\t\tq_conf->empty_poll_stats++;\n+\t\tif (unlikely(q_conf->empty_poll_stats > EMPTYPOLL_MAX))\n+\t\t\t/* scale down freq */\n+\t\t\trte_power_freq_min(rte_lcore_id());\n+\t} else {\n+\t\tq_conf->empty_poll_stats = 0;\n+\t\t/* scale up freq */\n+\t\trte_power_freq_max(rte_lcore_id());\n+\t}\n+\n+\treturn nb_rx;\n+}\n+\n+int\n+rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id, uint16_t port_id,\n+\t\tuint16_t queue_id, enum rte_power_pmd_mgmt_type mode)\n+{\n+\tstruct pmd_queue_cfg *queue_cfg;\n+\tstruct rte_eth_dev_info info;\n+\tint ret;\n+\n+\tRTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);\n+\n+\tif (queue_id >= RTE_MAX_QUEUES_PER_PORT || lcore_id >= RTE_MAX_LCORE) {\n+\t\tret = -EINVAL;\n+\t\tgoto end;\n+\t}\n+\n+\tif (rte_eth_dev_info_get(port_id, &info) < 0) {\n+\t\tret = -EINVAL;\n+\t\tgoto end;\n+\t}\n+\n+\t/* check if queue id is valid */\n+\tif (queue_id >= info.nb_rx_queues) {\n+\t\tret = -EINVAL;\n+\t\tgoto end;\n+\t}\n+\n+\tqueue_cfg = &port_cfg[port_id][queue_id];\n+\n+\tif (queue_cfg->pwr_mgmt_state != PMD_MGMT_DISABLED) {\n+\t\tret = -EINVAL;\n+\t\tgoto end;\n+\t}\n+\n+\t/* we need this in various places */\n+\trte_cpu_get_intrinsics_support(&global_data.intrinsics_support);\n+\n+\tswitch (mode) {\n+\tcase RTE_POWER_MGMT_TYPE_MONITOR:\n+\t{\n+\t\tstruct rte_power_monitor_cond dummy;\n+\n+\t\t/* check if rte_power_monitor is supported */\n+\t\tif (!global_data.intrinsics_support.power_monitor) {\n+\t\t\tRTE_LOG(DEBUG, POWER, \"Monitoring intrinsics are not supported\\n\");\n+\t\t\tret = -ENOTSUP;\n+\t\t\tgoto end;\n+\t\t}\n+\n+\t\t/* check if the device supports the necessary PMD API */\n+\t\tif (rte_eth_get_monitor_addr(port_id, queue_id,\n+\t\t\t\t&dummy) == -ENOTSUP) {\n+\t\t\tRTE_LOG(DEBUG, POWER, \"The device does not support rte_eth_get_monitor_addr\\n\");\n+\t\t\tret = -ENOTSUP;\n+\t\t\tgoto end;\n+\t\t}\n+\t\t/* initialize data before enabling the callback */\n+\t\tqueue_cfg->empty_poll_stats = 0;\n+\t\tqueue_cfg->cb_mode = mode;\n+\t\tqueue_cfg->umwait_in_progress = false;\n+\t\tqueue_cfg->pwr_mgmt_state = PMD_MGMT_ENABLED;\n+\n+\t\t/* ensure we update our state before callback starts */\n+\t\trte_atomic_thread_fence(__ATOMIC_SEQ_CST);\n+\n+\t\tqueue_cfg->cur_cb = rte_eth_add_rx_callback(port_id, queue_id,\n+\t\t\t\tclb_umwait, NULL);\n+\t\tbreak;\n+\t}\n+\tcase RTE_POWER_MGMT_TYPE_SCALE:\n+\t{\n+\t\tenum power_management_env env;\n+\t\t/* only PSTATE and ACPI modes are supported */\n+\t\tif (!rte_power_check_env_supported(PM_ENV_ACPI_CPUFREQ) &&\n+\t\t\t\t!rte_power_check_env_supported(\n+\t\t\t\t\tPM_ENV_PSTATE_CPUFREQ)) {\n+\t\t\tRTE_LOG(DEBUG, POWER, \"Neither ACPI nor PSTATE modes are supported\\n\");\n+\t\t\tret = -ENOTSUP;\n+\t\t\tgoto end;\n+\t\t}\n+\t\t/* ensure we could initialize the power library */\n+\t\tif (rte_power_init(lcore_id)) {\n+\t\t\tret = -EINVAL;\n+\t\t\tgoto end;\n+\t\t}\n+\t\t/* ensure we initialized the correct env */\n+\t\tenv = rte_power_get_env();\n+\t\tif (env != PM_ENV_ACPI_CPUFREQ &&\n+\t\t\t\tenv != PM_ENV_PSTATE_CPUFREQ) {\n+\t\t\tRTE_LOG(DEBUG, POWER, \"Neither ACPI nor PSTATE modes were initialized\\n\");\n+\t\t\tret = -ENOTSUP;\n+\t\t\tgoto end;\n+\t\t}\n+\t\t/* initialize data before enabling the callback */\n+\t\tqueue_cfg->empty_poll_stats = 0;\n+\t\tqueue_cfg->cb_mode = mode;\n+\t\tqueue_cfg->pwr_mgmt_state = PMD_MGMT_ENABLED;\n+\n+\t\t/* this is not necessary here, but do it anyway */\n+\t\trte_atomic_thread_fence(__ATOMIC_SEQ_CST);\n+\n+\t\tqueue_cfg->cur_cb = rte_eth_add_rx_callback(port_id,\n+\t\t\t\tqueue_id, clb_scale_freq, NULL);\n+\t\tbreak;\n+\t}\n+\tcase RTE_POWER_MGMT_TYPE_PAUSE:\n+\t\t/* figure out various time-to-tsc conversions */\n+\t\tif (global_data.tsc_per_us == 0)\n+\t\t\tcalc_tsc();\n+\n+\t\t/* initialize data before enabling the callback */\n+\t\tqueue_cfg->empty_poll_stats = 0;\n+\t\tqueue_cfg->cb_mode = mode;\n+\t\tqueue_cfg->pwr_mgmt_state = PMD_MGMT_ENABLED;\n+\n+\t\t/* this is not necessary here, but do it anyway */\n+\t\trte_atomic_thread_fence(__ATOMIC_SEQ_CST);\n+\n+\t\tqueue_cfg->cur_cb = rte_eth_add_rx_callback(port_id, queue_id,\n+\t\t\t\tclb_pause, NULL);\n+\t\tbreak;\n+\t}\n+\tret = 0;\n+end:\n+\treturn ret;\n+}\n+\n+int\n+rte_power_ethdev_pmgmt_queue_disable(unsigned int lcore_id,\n+\t\tuint16_t port_id, uint16_t queue_id)\n+{\n+\tstruct pmd_queue_cfg *queue_cfg;\n+\n+\tRTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);\n+\n+\tif (lcore_id >= RTE_MAX_LCORE || queue_id >= RTE_MAX_QUEUES_PER_PORT)\n+\t\treturn -EINVAL;\n+\n+\t/* no need to check queue id as wrong queue id would not be enabled */\n+\tqueue_cfg = &port_cfg[port_id][queue_id];\n+\n+\tif (queue_cfg->pwr_mgmt_state != PMD_MGMT_ENABLED)\n+\t\treturn -EINVAL;\n+\n+\t/* stop any callbacks from progressing */\n+\tqueue_cfg->pwr_mgmt_state = PMD_MGMT_DISABLED;\n+\n+\t/* ensure we update our state before continuing */\n+\trte_atomic_thread_fence(__ATOMIC_SEQ_CST);\n+\n+\tswitch (queue_cfg->cb_mode) {\n+\tcase RTE_POWER_MGMT_TYPE_MONITOR:\n+\t{\n+\t\tbool exit = false;\n+\t\tdo {\n+\t\t\t/*\n+\t\t\t * we may request cancellation while the other thread\n+\t\t\t * has just entered the callback but hasn't started\n+\t\t\t * sleeping yet, so keep waking it up until we know it's\n+\t\t\t * done sleeping.\n+\t\t\t */\n+\t\t\tif (queue_cfg->umwait_in_progress)\n+\t\t\t\trte_power_monitor_wakeup(lcore_id);\n+\t\t\telse\n+\t\t\t\texit = true;\n+\t\t} while (!exit);\n+\t}\n+\t/* fall-through */\n+\tcase RTE_POWER_MGMT_TYPE_PAUSE:\n+\t\trte_eth_remove_rx_callback(port_id, queue_id,\n+\t\t\t\tqueue_cfg->cur_cb);\n+\t\tbreak;\n+\tcase RTE_POWER_MGMT_TYPE_SCALE:\n+\t\trte_power_freq_max(lcore_id);\n+\t\trte_eth_remove_rx_callback(port_id, queue_id,\n+\t\t\t\tqueue_cfg->cur_cb);\n+\t\trte_power_exit(lcore_id);\n+\t\tbreak;\n+\t}\n+\t/*\n+\t * we don't free the RX callback here because it is unsafe to do so\n+\t * unless we know for a fact that all data plane threads have stopped.\n+\t */\n+\tqueue_cfg->cur_cb = NULL;\n+\n+\treturn 0;\n+}\ndiff --git a/lib/librte_power/rte_power_pmd_mgmt.h b/lib/librte_power/rte_power_pmd_mgmt.h\nnew file mode 100644\nindex 0000000000..7a0ac24625\n--- /dev/null\n+++ b/lib/librte_power/rte_power_pmd_mgmt.h\n@@ -0,0 +1,91 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2020 Intel Corporation\n+ */\n+\n+#ifndef _RTE_POWER_PMD_MGMT_H\n+#define _RTE_POWER_PMD_MGMT_H\n+\n+/**\n+ * @file\n+ * RTE PMD Power Management\n+ */\n+\n+#include <stdint.h>\n+#include <stdbool.h>\n+\n+#include <rte_common.h>\n+#include <rte_byteorder.h>\n+#include <rte_log.h>\n+#include <rte_power.h>\n+#include <rte_atomic.h>\n+\n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+/**\n+ * PMD Power Management Type\n+ */\n+enum rte_power_pmd_mgmt_type {\n+\t/** Use power-optimized monitoring to wait for incoming traffic */\n+\tRTE_POWER_MGMT_TYPE_MONITOR = 1,\n+\t/** Use power-optimized sleep to avoid busy polling */\n+\tRTE_POWER_MGMT_TYPE_PAUSE,\n+\t/** Use frequency scaling when traffic is low */\n+\tRTE_POWER_MGMT_TYPE_SCALE,\n+};\n+\n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice.\n+ *\n+ * Enable power management on a specified Ethernet device Rx queue and lcore.\n+ *\n+ * @note This function is not thread-safe.\n+ *\n+ * @param lcore_id\n+ *   The lcore the Rx queue will be polled from.\n+ * @param port_id\n+ *   The port identifier of the Ethernet device.\n+ * @param queue_id\n+ *   The queue identifier of the Ethernet device.\n+ * @param mode\n+ *   The power management scheme to use for specified Rx queue.\n+ * @return\n+ *   0 on success\n+ *   <0 on error\n+ */\n+__rte_experimental\n+int\n+rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id,\n+\t\tuint16_t port_id, uint16_t queue_id,\n+\t\tenum rte_power_pmd_mgmt_type mode);\n+\n+/**\n+ * @warning\n+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice.\n+ *\n+ * Disable power management on a specified Ethernet device Rx queue and lcore.\n+ *\n+ * @note This function is not thread-safe.\n+ *\n+ * @param lcore_id\n+ *   The lcore the Rx queue is polled from.\n+ * @param port_id\n+ *   The port identifier of the Ethernet device.\n+ * @param queue_id\n+ *   The queue identifier of the Ethernet device.\n+ * @return\n+ *   0 on success\n+ *   <0 on error\n+ */\n+__rte_experimental\n+int\n+rte_power_ethdev_pmgmt_queue_disable(unsigned int lcore_id,\n+\t\tuint16_t port_id, uint16_t queue_id);\n+\n+#ifdef __cplusplus\n+}\n+#endif\n+\n+#endif\ndiff --git a/lib/librte_power/version.map b/lib/librte_power/version.map\nindex 69ca9af616..f38a380212 100644\n--- a/lib/librte_power/version.map\n+++ b/lib/librte_power/version.map\n@@ -34,4 +34,9 @@ EXPERIMENTAL {\n \trte_power_guest_channel_receive_msg;\n \trte_power_poll_stat_fetch;\n \trte_power_poll_stat_update;\n+\n+\t# added in 21.02\n+\trte_power_ethdev_pmgmt_queue_disable;\n+\trte_power_ethdev_pmgmt_queue_enable;\n+\n };\n",
    "prefixes": [
        "v20",
        "3/4"
    ]
}