From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id DACB44603E; Fri, 10 Jan 2025 05:36:36 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6FBDA41148; Fri, 10 Jan 2025 05:36:36 +0100 (CET) Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) by mails.dpdk.org (Postfix) with ESMTP id CE29C40612 for ; Fri, 10 Jan 2025 05:36:34 +0100 (CET) Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-2ef8c012913so2175002a91.3 for ; Thu, 09 Jan 2025 20:36:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=networkplumber-org.20230601.gappssmtp.com; s=20230601; t=1736483794; x=1737088594; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=2pNEAy+vUQZMfSfgwdY7vAOgU6CIaC1F6R0Hdth2NiU=; b=1dU9lq+8dL3dcOcMOoqQr2nHB0lkZdeb+MaaOXMuc1xylP8dEvwVu7r+HXSon1PFSK LLjeoKQrBr2Q+jUGk6z93MI4alh9KVxP1XaPS5IJy/JshE3LEHV4xC8i0R/FxCwxYPo1 8+vgfeUPM04Cnah9FmVpDiNUDKtvVdqm5EUX0uwrnm48lRW4f4x7o2L4d6n/3sGg5Ry8 G3752mBCPNpNNOctqJzMrVYK7jSgbQkR2diuQdkY2Y46CjIRdntVnNF9IQ9WlkPAUNk2 Hdo1Va1PJLpyRMvszLi4fUSo87MaNvZZ1Lht1tBcQLMGDTy+PPT2ORMYUK8ZE6Gd2ej6 +R2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1736483794; x=1737088594; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2pNEAy+vUQZMfSfgwdY7vAOgU6CIaC1F6R0Hdth2NiU=; b=Co5isSCEBjyyVgQog7BE6FmVqFxVMRP77uKsLH8zyMmby2tNAqFuCBbACtIcIuLHMJ rioj3z//VZRKoQgeTAlagajNRqN/dxmkor/wfYSf+OBEQtxg4glaJWJ826fX+QA1dTWO fb2B8h7TSMyRyf1qBVhZ2CV291INXtcwR7nI4P+lNSvBI0/YnnC1hpjPIzQaHk1G4wuK 0iWf4De3ch/enmvahS0769DuMV+PqhvGwSZacpKGDzin2s+DEoSt1fo9kNPOQFKhsyHe idTwKD49+Au/HiuXjN61TqkQq2cj1QsyknGQZdD/2wasU0E+0Z/0yAPEWd7dSZSUw+wO aMsA== X-Gm-Message-State: AOJu0YyoVx40UX/tduhA2ry2yPSIm/NuiNfMTg5Whrf5MstDx4jOXKNC Ux/u5eYMrPD5zeUQ4WUZBoyRofkNgMfphB+DM8BfN7gzF3+DaZDWagwAiepTtSw= X-Gm-Gg: ASbGncuJJ7jYB89sxYdhDovN6nC12PbrzqtgMd2jkO0ER4JUjszZ3a5Qei86wWBTS37 Ro94xGs5Ou05wsh2/1xzpnFqTYixWXB0trU6xM+zIcYC61pMhhWonyHqxUhF3m6UlqJcLrWChcP AMCIkPoug93gYvi12Lpm32KMUzwuKiPJY3F2sy9r7ZwxfA3O4CMqLFe/k2MW26KaXbZWV9w5zVs OYtNjiSuZcPydVqneu2Z25Xim9iCAtTsAPs6B5UAl8pGNwFQNo/VW8YmFcoZhg0FxCphvI8worC tsfuQVzveDmvsn5IZi1cp0bNWbdBm0oTwg== X-Google-Smtp-Source: AGHT+IFVn5sfu0eYxmdyu+NG6/8PjwxueRet2+/DNU/kfpK3FCJt025ScDVEIUHZXEAT3VciPVHM1g== X-Received: by 2002:a17:90b:288a:b0:2f2:a664:df19 with SMTP id 98e67ed59e1d1-2f548e991b9mr14480881a91.7.1736483792852; Thu, 09 Jan 2025 20:36:32 -0800 (PST) Received: from hermes.local (204-195-96-226.wavecable.com. [204.195.96.226]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2f55942f6f7sm2525226a91.29.2025.01.09.20.36.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 09 Jan 2025 20:36:32 -0800 (PST) Date: Thu, 9 Jan 2025 20:36:30 -0800 From: Stephen Hemminger To: Shani Peretz Cc: , Aman Singh , Dariusz Sosnowski , Viacheslav Ovsiienko , Bing Zhao , Ori Kam , Suanming Mou , Matan Azrad , Thomas Monjalon , Ferruh Yigit , Andrew Rybchenko , Anatoly Burakov Subject: Re: [RFC PATCH] ethdev: add new API for enable/disable xstat counters by ID Message-ID: <20250109203630.13322c14@hermes.local> In-Reply-To: <20241222153819.62519-1-shperetz@nvidia.com> References: <20241222153819.62519-1-shperetz@nvidia.com> MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org On Sun, 22 Dec 2024 17:38:19 +0200 Shani Peretz wrote: > This change introduces a new API to dynamically enable or disable > xstat counters by their IDs. Some counters may require hardware > resources that are potentially limited, so providing the ability > to toggle them on or off makes sense. > In addition, adding an API to query the current state > (enabled, disabled, or not supported) of an xstat counter is added. > New APIs: > - rte_eth_xstats_enable_counter(struct rte_eth_dev *dev, uint64_t id); > - rte_eth_xstats_disable_counter(struct rte_eth_dev *dev, uint64_t id); > - rte_eth_xstats_query_state(struct rte_eth_dev *dev, uint64_t id); > > Added the following testpmd subcommands: > toggle the counter on and off > > port (port_id) enable|disable > query the state of counters: > > set xstats-show-state on|off > > Note that by default, generic stats (like those provided by > eth_basic_stats_get()) will be considered unsupported and > will not be toggleable. > Also all xstats will be considered unsupported for dynamic enable/disable, > and each PMD will be able to override this in its implementation. > > Specifically in mlx5 PMD, expose a new xstat to track packet drops of > hairpin Rx queue: > - Hairpin_out_of_buffer - Port-level counter - > counts drops from all the hairpin queues > - Hairpin_out_of_buffer_rxq* - Queue-level counter - > counts drops of a specific queue > Both the port-level and per-queue counters can be > enabled, disabled, and queried. > > Signed-off-by: Shani Peretz > --- > app/test-pmd/cmdline.c | 92 ++++++++ > app/test-pmd/config.c | 89 ++++++++ > app/test-pmd/testpmd.c | 5 + > app/test-pmd/testpmd.h | 3 + > drivers/common/mlx5/mlx5_devx_cmds.c | 8 +- > drivers/common/mlx5/mlx5_devx_cmds.h | 2 +- > drivers/common/mlx5/mlx5_prm.h | 3 + > drivers/net/mlx5/linux/mlx5_ethdev_os.c | 5 + > drivers/net/mlx5/linux/mlx5_os.c | 21 +- > drivers/net/mlx5/mlx5.c | 292 +++++++++++++++++++++++- > drivers/net/mlx5/mlx5.h | 29 ++- > drivers/net/mlx5/mlx5_devx.c | 75 ++---- > drivers/net/mlx5/mlx5_rx.h | 20 ++ > drivers/net/mlx5/mlx5_rxq.c | 36 +++ > drivers/net/mlx5/mlx5_stats.c | 292 +++++++++++++++++++++++- > drivers/net/mlx5/windows/mlx5_os.c | 3 +- > lib/ethdev/ethdev_driver.h | 13 ++ > lib/ethdev/rte_ethdev.c | 66 ++++++ > lib/ethdev/rte_ethdev.h | 44 ++++ > lib/ethdev/version.map | 3 + > 20 files changed, 1010 insertions(+), 91 deletions(-) Please resubmit as three patches since there maybe bugfixes to only one. 1. Ethdev changes 2. Testpmd changes 3. Mlx5 > > diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c > index 7e0666e9f6..76b68b74ae 100644 > --- a/app/test-pmd/cmdline.c > +++ b/app/test-pmd/cmdline.c > @@ -7960,6 +7960,96 @@ static cmdline_parse_inst_t cmd_set_xstats_hide_zero = { > }, > }; > > +/* *** SET OPTION TO DISPLAY XSTAT STATE *** */ > +struct cmd_set_xstats_show_state_result { > + cmdline_fixed_string_t keyword; > + cmdline_fixed_string_t name; > + cmdline_fixed_string_t on_off; > +}; > + > +static void > +cmd_set_xstats_show_state_parsed(void *parsed_result, > + __rte_unused struct cmdline *cl, > + __rte_unused void *data) > +{ > + struct cmd_set_xstats_show_state_result *res; > + uint16_t on_off = 0; Initialization not needed, you are setting it two lines later. > + res = parsed_result; > + on_off = !strcmp(res->on_off, "on") ? 1 : 0; > + set_xstats_show_state(on_off); > +} I wonder if on/off bool should be handled generically like etheraddr ipaddr num string etc. If you are copy/paste the same code multiple times, indicates to me it should be an object. > +static cmdline_parse_token_string_t cmd_set_xstats_show_state_keyword = > + TOKEN_STRING_INITIALIZER(struct cmd_set_xstats_show_state_result, > + keyword, "set"); > +static cmdline_parse_token_string_t cmd_set_xstats_show_state_name = > + TOKEN_STRING_INITIALIZER(struct cmd_set_xstats_show_state_result, > + name, "xstats-show-state"); > +static cmdline_parse_token_string_t cmd_set_xstats_show_state_on_off = > + TOKEN_STRING_INITIALIZER(struct cmd_set_xstats_show_state_result, > + on_off, "on#off"); > + > +static cmdline_parse_inst_t cmd_set_xstats_show_state = { > + .f = cmd_set_xstats_show_state_parsed, > + .data = NULL, > + .help_str = "set xstats-show-state on|off", > + .tokens = { > + (void *)&cmd_set_xstats_show_state_keyword, > + (void *)&cmd_set_xstats_show_state_name, > + (void *)&cmd_set_xstats_show_state_on_off, > + NULL, > + }, > +}; > + > +/* *** enable/disable counter by name *** */ > +struct cmd_operate_set_counter_result { > + cmdline_fixed_string_t port; > + portid_t port_id; > + cmdline_fixed_string_t what; > + cmdline_multi_string_t counter_name; > +}; > + > +static void > +cmd_operate_set_counter_parsed(void *parsed_result, > + __rte_unused struct cmdline *cl, > + __rte_unused void *data) > +{ > + struct cmd_operate_set_counter_result *res = parsed_result; > + uint16_t on_off = 0; ditto > + on_off = strcmp(res->what, "enable") ? 0 : 1; > + > + if ((strcmp(res->port, "port") == 0)) > + nic_xstats_set_counter(res->port_id, res->counter_name, on_off); > +} > + > +static cmdline_parse_token_string_t cmd_operate_set_counter_port = > + TOKEN_STRING_INITIALIZER(struct cmd_operate_set_counter_result, > + port, "port"); > +static cmdline_parse_token_num_t cmd_operate_set_counter_port_id = > + TOKEN_NUM_INITIALIZER(struct cmd_operate_set_counter_result, > + port_id, RTE_UINT16); > +static cmdline_parse_token_string_t cmd_operate_set_counter_what = > + TOKEN_STRING_INITIALIZER(struct cmd_operate_set_counter_result, > + what, "enable#disable"); > +static cmdline_parse_token_string_t cmd_operate_set_counter_name = > + TOKEN_STRING_INITIALIZER(struct cmd_operate_set_counter_result, > + counter_name, TOKEN_STRING_MULTI); > + > +static cmdline_parse_inst_t cmd_operate_set_counter = { > + .f = cmd_operate_set_counter_parsed, > + .data = NULL, > + .help_str = "port (port_id) enable|disable ", > + .tokens = { > + (void *)&cmd_operate_set_counter_port, > + (void *)&cmd_operate_set_counter_port_id, > + (void *)&cmd_operate_set_counter_what, > + (void *)&cmd_operate_set_counter_name, > + NULL, > + }, > +}; > + > /* *** SET OPTION TO ENABLE MEASUREMENT OF CPU CYCLES *** */ > struct cmd_set_record_core_cycles_result { > cmdline_fixed_string_t keyword; > @@ -13648,6 +13738,8 @@ static cmdline_parse_ctx_t builtin_ctx[] = { > &cmd_set_fwd_eth_peer, > &cmd_set_qmap, > &cmd_set_xstats_hide_zero, > + &cmd_set_xstats_show_state, > + &cmd_operate_set_counter, > &cmd_set_record_core_cycles, > &cmd_set_record_burst_stats, > &cmd_operate_port, > diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c > index 4e7fb69183..a0a5736857 100644 > --- a/app/test-pmd/config.c > +++ b/app/test-pmd/config.c > @@ -393,6 +393,7 @@ nic_xstats_display(portid_t port_id) > struct rte_eth_xstat *xstats; > int cnt_xstats, idx_xstat; > struct rte_eth_xstat_name *xstats_names; > + int state; > > if (port_id_is_invalid(port_id, ENABLED_WARN)) { > print_valid_ports(); > @@ -442,6 +443,12 @@ nic_xstats_display(portid_t port_id) > for (idx_xstat = 0; idx_xstat < cnt_xstats; idx_xstat++) { > if (xstats_hide_zero && !xstats[idx_xstat].value) > continue; > + if (xstats_show_state) { > + char opt[3] = {'D', 'E', '-'}; > + state = rte_eth_xstats_query_state(port_id, idx_xstat); > + printf("state: %c ", state < 0 ? opt[2] : opt[state]); > + } Since there is hide_zero, why not hide_disabled? > printf("%s: %"PRIu64"\n", > xstats_names[idx_xstat].name, > xstats[idx_xstat].value); > @@ -6437,6 +6444,82 @@ rx_vlan_strip_set_on_queue(portid_t port_id, uint16_t queue_id, int on) > __func__, port_id, queue_id, on, diag); > } > > +void > +nic_xstats_set_counter(portid_t port_id, char *counter_name, int on) > +{ > + struct rte_eth_xstat_name *xstats_names; > + int cnt_xstats; > + int ret; > + uint64_t id; > + > + if (port_id_is_invalid(port_id, ENABLED_WARN)) { > + print_valid_ports(); > + return; > + } > + if (!rte_eth_dev_is_valid_port(port_id)) { > + fprintf(stderr, "Error: Invalid port number %i\n", port_id); > + return; > + } > + > + if (counter_name == NULL) { > + fprintf(stderr, "Error: Invalid counter name\n"); > + return; > + } > + > + /* Get count */ > + cnt_xstats = rte_eth_xstats_get_names(port_id, NULL, 0); > + if (cnt_xstats < 0) { > + fprintf(stderr, "Error: Cannot get count of xstats\n"); > + return; > + } > + > + /* Get id-name lookup table */ > + xstats_names = malloc(sizeof(struct rte_eth_xstat_name) * cnt_xstats); > + if (xstats_names == NULL) { > + fprintf(stderr, "Cannot allocate memory for xstats lookup\n"); > + return; > + } > + if (cnt_xstats != rte_eth_xstats_get_names( > + port_id, xstats_names, cnt_xstats)) { > + fprintf(stderr, "Error: Cannot get xstats lookup\n"); > + free(xstats_names); > + return; > + } > + > + if (rte_eth_xstats_get_id_by_name(port_id, counter_name, &id) < 0) { > + fprintf(stderr, "Cannot find xstats with a given name\n"); > + free(xstats_names); > + return; > + } > + > + /* set counter */ > + if (on) > + ret = rte_eth_xstats_enable_counter(port_id, id); > + else > + ret = rte_eth_xstats_disable_counter(port_id, id); > + > + if (ret < 0) { > + switch (ret) { > + case -EINVAL: > + fprintf(stderr, "failed to find %s\n", counter_name); > + break; > + case -ENOTSUP: > + fprintf(stderr, "operation not supported by device\n"); > + break; > + case -ENOSPC: > + fprintf(stderr, "there were not enough available counters\n"); > + break; > + case -EPERM: > + fprintf(stderr, "operation not premitted\n"); > + break; > + default: > + fprintf(stderr, "operation failed - diag=%d\n", ret); > + break; > + } > + } > + free(xstats_names); > +} > + > void > rx_vlan_filter_set(portid_t port_id, int on) > { > @@ -6665,6 +6748,12 @@ set_xstats_hide_zero(uint8_t on_off) > xstats_hide_zero = on_off; > } > > +void > +set_xstats_show_state(uint8_t on_off) > +{ > + xstats_show_state = on_off; > +} > + > void > set_record_core_cycles(uint8_t on_off) > { > diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c > index ac654048df..24be87204c 100644 > --- a/app/test-pmd/testpmd.c > +++ b/app/test-pmd/testpmd.c > @@ -500,6 +500,11 @@ volatile int test_done = 1; /* stop packet forwarding when set to 1. */ > */ > uint8_t xstats_hide_zero; > > +/* > + * Display of xstats without their state disabled by default > + */ > +uint8_t xstats_show_state; > + > /* > * Measure of CPU cycles disabled by default > */ > diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h > index 314482e69c..16a0348eb2 100644 > --- a/app/test-pmd/testpmd.h > +++ b/app/test-pmd/testpmd.h > @@ -498,6 +498,7 @@ enum dcb_mode_enable > }; > > extern uint8_t xstats_hide_zero; /**< Hide zero values for xstats display */ > +extern uint8_t xstats_show_state; /**< Show xstat state in xstats display */ > > /* globals used for configuration */ > extern uint8_t record_core_cycles; /**< Enables measurement of CPU cycles */ > @@ -933,6 +934,7 @@ void nic_stats_display(portid_t port_id); > void nic_stats_clear(portid_t port_id); > void nic_xstats_display(portid_t port_id); > void nic_xstats_clear(portid_t port_id); > +void nic_xstats_set_counter(portid_t port_id, char *counter_name, int on); > void device_infos_display(const char *identifier); > void port_infos_display(portid_t port_id); > void port_summary_display(portid_t port_id); > @@ -1111,6 +1113,7 @@ void tx_vlan_pvid_set(portid_t port_id, uint16_t vlan_id, int on); > void set_qmap(portid_t port_id, uint8_t is_rx, uint16_t queue_id, uint8_t map_value); > > void set_xstats_hide_zero(uint8_t on_off); > +void set_xstats_show_state(uint8_t on_off); > > void set_record_core_cycles(uint8_t on_off); > void set_record_burst_stats(uint8_t on_off); > diff --git a/drivers/common/mlx5/mlx5_devx_cmds.c b/drivers/common/mlx5/mlx5_devx_cmds.c > index a75f011750..2149449885 100644 > --- a/drivers/common/mlx5/mlx5_devx_cmds.c > +++ b/drivers/common/mlx5/mlx5_devx_cmds.c > @@ -3091,14 +3091,17 @@ mlx5_devx_cmd_wq_query(void *wq, uint32_t *counter_set_id) > * > * @param[in] ctx > * Context returned from mlx5 open_device() glue function. > + * @param[out] syndrome > + * Get syndrome of devx command response. > * > * @return > * Pointer to counter object on success, a NULL value otherwise and > * rte_errno is set. > */ > struct mlx5_devx_obj * > -mlx5_devx_cmd_queue_counter_alloc(void *ctx) > +mlx5_devx_cmd_queue_counter_alloc(void *ctx, int *syndrome) > { > + int status; > struct mlx5_devx_obj *dcs = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*dcs), 0, > SOCKET_ID_ANY); > uint32_t in[MLX5_ST_SZ_DW(alloc_q_counter_in)] = {0}; > @@ -3113,6 +3116,9 @@ mlx5_devx_cmd_queue_counter_alloc(void *ctx) > sizeof(out)); > if (!dcs->obj) { > DEVX_DRV_LOG(DEBUG, out, "create q counter set", NULL, 0); > + status = MLX5_GET(alloc_q_counter_out, out, status); > + if (status && syndrome) > + *syndrome = MLX5_GET(alloc_q_counter_out, out, syndrome); > mlx5_free(dcs); > return NULL; > } > diff --git a/drivers/common/mlx5/mlx5_devx_cmds.h b/drivers/common/mlx5/mlx5_devx_cmds.h > index f523bf8529..38548b4c9f 100644 > --- a/drivers/common/mlx5/mlx5_devx_cmds.h > +++ b/drivers/common/mlx5/mlx5_devx_cmds.h > @@ -848,7 +848,7 @@ __rte_internal > int mlx5_devx_cmd_wq_query(void *wq, uint32_t *counter_set_id); > > __rte_internal > -struct mlx5_devx_obj *mlx5_devx_cmd_queue_counter_alloc(void *ctx); > +struct mlx5_devx_obj *mlx5_devx_cmd_queue_counter_alloc(void *ctx, int *syndrome); > __rte_internal > int mlx5_devx_cmd_queue_counter_query(struct mlx5_devx_obj *dcs, int clear, > uint32_t *out_of_buffers); > diff --git a/drivers/common/mlx5/mlx5_prm.h b/drivers/common/mlx5/mlx5_prm.h > index 2d82807bc2..fded81d43d 100644 > --- a/drivers/common/mlx5/mlx5_prm.h > +++ b/drivers/common/mlx5/mlx5_prm.h > @@ -275,6 +275,9 @@ > #define MLX5_ERROR_CQE_SYNDROME_OFFSET 52 > #endif > > +/* Firmware error code for allocating the maximum number of queue counters */ > +#define MLX5_Q_COUNTERS_LIMIT_REACHED 0x587239 > + > /* The completion mode offset in the WQE control segment line 2. */ > #define MLX5_COMP_MODE_OFFSET 2 > > diff --git a/drivers/net/mlx5/linux/mlx5_ethdev_os.c b/drivers/net/mlx5/linux/mlx5_ethdev_os.c > index 5d64984022..63277fc4ed 100644 > --- a/drivers/net/mlx5/linux/mlx5_ethdev_os.c > +++ b/drivers/net/mlx5/linux/mlx5_ethdev_os.c > @@ -1425,6 +1425,11 @@ static const struct mlx5_counter_ctrl mlx5_counters_init[] = { > .dpdk_name = "hairpin_out_of_buffer", > .ctr_name = "hairpin_out_of_buffer", > .dev = 1, > + .ctrl = { > + .enable = mlx5_enable_port_level_hairpin_counter, > + .disable = mlx5_disable_port_level_hairpin_counter, > + .enabled = 0, > + } > }, > { > .dpdk_name = "dev_internal_queue_oob", > diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c > index 69a80b9ddc..0c63ab228e 100644 > --- a/drivers/net/mlx5/linux/mlx5_os.c > +++ b/drivers/net/mlx5/linux/mlx5_os.c > @@ -973,7 +973,7 @@ mlx5_queue_counter_id_prepare(struct rte_eth_dev *dev) > struct mlx5_priv *priv = dev->data->dev_private; > void *ctx = priv->sh->cdev->ctx; > > - priv->q_counters = mlx5_devx_cmd_queue_counter_alloc(ctx); > + priv->q_counters = mlx5_devx_cmd_queue_counter_alloc(ctx, NULL); > if (!priv->q_counters) { > struct ibv_cq *cq = mlx5_glue->create_cq(ctx, 1, NULL, NULL, 0); > struct ibv_wq *wq; > @@ -981,7 +981,6 @@ mlx5_queue_counter_id_prepare(struct rte_eth_dev *dev) > DRV_LOG(DEBUG, "Port %d queue counter object cannot be created " > "by DevX - fall-back to use the kernel driver global " > "queue counter.", dev->data->port_id); > - priv->q_counters_allocation_failure = 1; > > /* Create WQ by kernel and query its queue counter ID. */ > if (cq) { > @@ -3052,23 +3051,11 @@ mlx5_os_read_dev_stat(struct mlx5_priv *priv, const char *ctr_name, > if (priv->sh) { > if (priv->q_counters != NULL && > strcmp(ctr_name, "out_of_buffer") == 0) { > - if (rte_eal_process_type() == RTE_PROC_SECONDARY) { > - DRV_LOG(WARNING, "DevX out_of_buffer counter is not supported in the secondary process"); > - rte_errno = ENOTSUP; > - return 1; > - } > - return mlx5_devx_cmd_queue_counter_query > - (priv->q_counters, 0, (uint32_t *)stat); > + return mlx5_read_queue_counter(priv->q_counters, ctr_name, stat); > } > - if (priv->q_counters_hairpin != NULL && > + if (priv->q_counter_hairpin != NULL && > strcmp(ctr_name, "hairpin_out_of_buffer") == 0) { > - if (rte_eal_process_type() == RTE_PROC_SECONDARY) { > - DRV_LOG(WARNING, "DevX out_of_buffer counter is not supported in the secondary process"); > - rte_errno = ENOTSUP; > - return 1; > - } > - return mlx5_devx_cmd_queue_counter_query > - (priv->q_counters_hairpin, 0, (uint32_t *)stat); > + return mlx5_read_queue_counter(priv->q_counter_hairpin, ctr_name, stat); > } > MKSTR(path, "%s/ports/%d/hw_counters/%s", > priv->sh->ibdev_path, > diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c > index 6e4473e2f4..1e48c43da0 100644 > --- a/drivers/net/mlx5/mlx5.c > +++ b/drivers/net/mlx5/mlx5.c > @@ -2373,6 +2373,7 @@ mlx5_dev_close(struct rte_eth_dev *dev) > priv->ptype_rss_groups = NULL; > } > #endif > + mlx5_q_counters_destroy(dev); > if (priv->rxq_privs != NULL) { > /* XXX race condition if mlx5_rx_burst() is still running. */ > rte_delay_us_sleep(1000); > @@ -2393,14 +2394,6 @@ mlx5_dev_close(struct rte_eth_dev *dev) > mlx5_proc_priv_uninit(dev); > if (priv->drop_queue.hrxq) > mlx5_drop_action_destroy(dev); > - if (priv->q_counters) { > - mlx5_devx_cmd_destroy(priv->q_counters); > - priv->q_counters = NULL; > - } > - if (priv->q_counters_hairpin) { > - mlx5_devx_cmd_destroy(priv->q_counters_hairpin); > - priv->q_counters_hairpin = NULL; > - } > mlx5_mprq_free_mp(dev); > mlx5_os_free_shared_dr(priv); > #ifdef HAVE_MLX5_HWS_SUPPORT > @@ -2520,6 +2513,11 @@ const struct eth_dev_ops mlx5_dev_ops = { > .xstats_get = mlx5_xstats_get, > .xstats_reset = mlx5_xstats_reset, > .xstats_get_names = mlx5_xstats_get_names, > + > + .xstats_enable = mlx5_xstats_enable, > + .xstats_disable = mlx5_xstats_disable, > + .xstats_query_state = mlx5_xstats_query_state, > + > .fw_version_get = mlx5_fw_version_get, > .dev_infos_get = mlx5_dev_infos_get, > .representor_info_get = mlx5_representor_info_get, > @@ -3393,6 +3391,284 @@ mlx5_eth_find_next(uint16_t port_id, struct rte_device *odev) > return port_id; > } > > +static int > +mlx5_hairpin_queue_counter_supported(struct mlx5_priv *priv) > +{ > + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { > + DRV_LOG(WARNING, > + "DevX counter is not supported in the secondary process"); > + return -ENOTSUP; > + } > + > + if (priv->obj_ops.rxq_obj_modify_counter_set_id == NULL) { > + DRV_LOG(WARNING, > + "DevX counter is not supported in this device"); > + return -ENOTSUP; > + } > + return 0; > +} > + > +/** > + * Disables the port-level hairpin counter. > + * > + * This function iterates over each RXQ, detaches it from the global > + * counter if it's a hairpin counter, and then destroys the global counter object if > + * it exists. > + * > + * @param dev > + * Pointer to Ethernet device. > + * @param id > + * The counter ID to disable (not used in this implementation). > + * > + * @return > + * 0 on success, error code otherwise. > + */ > +int > +mlx5_disable_port_level_hairpin_counter(struct rte_eth_dev *dev, uint64_t id __rte_unused) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + unsigned int num_rxqs = priv->rxqs_n; > + unsigned int i; > + > + if (priv->q_counter_hairpin == NULL) > + return 0; > + > + /* Detach each RXQ from the global hairpin counter */ > + for (i = 0; i < num_rxqs; ++i) { > + struct mlx5_rxq_priv *rxq = mlx5_rxq_get(dev, i); > + > + if (rxq == NULL || rxq->ctrl->obj->rq == NULL || !rxq->ctrl->is_hairpin) > + continue; > + > + if (priv->obj_ops.rxq_obj_modify_counter_set_id(rxq, 0) != 0) > + DRV_LOG(ERR, "Port %u failed to modify rq object %s", > + priv->dev_data->port_id, strerror(rte_errno)); > + } > + > + mlx5_devx_cmd_destroy(priv->q_counter_hairpin); > + priv->q_counter_hairpin = NULL; > + > + /* Reset oob stats. */ > + mlx5_reset_xstats_by_name(priv, "hairpin_out_of_buffer"); > + return 0; > +} > + > +/** > + * Enables the port-level hairpin counter. > + * > + * This function iterates over each RXQ, allocate a q counter and attach it to each > + * hairpin queue. > + * > + * @param dev > + * Pointer to Ethernet device. > + * @param id > + * The counter ID to disable (not used in this implementation). > + * > + * @return > + * 0 on success, error code otherwise. > + */ > +int > +mlx5_enable_port_level_hairpin_counter(struct rte_eth_dev *dev, uint64_t id __rte_unused) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_rxq_priv *rxq; > + unsigned int i; > + int ret = 0; > + > + ret = mlx5_hairpin_queue_counter_supported(priv); > + if (ret) { > + DRV_LOG(DEBUG, "Hairpin out of buffer counter " > + "is not available on this NIC."); > + return ret; > + } > + > + /* check if counter is enable per queue - if yes - fail to enable per port */ > + if (priv->num_of_hairpin_q_counter_enabled != 0) { > + DRV_LOG(WARNING, "Hairpin out of buffer counter is enabled per queue."); > + return -EINVAL; > + } > + > + /* Alloc global hairpin queue counter. */ > + priv->q_counter_hairpin = mlx5_devx_cmd_queue_counter_alloc > + (priv->sh->cdev->ctx, NULL); > + > + if (!priv->q_counter_hairpin) { > + if (ret == MLX5_Q_COUNTERS_LIMIT_REACHED) { > + DRV_LOG(WARNING, "Maximum number of queue counters reached. " > + "Unable to create counter object for Port %d using DevX.", > + priv->dev_data->port_id); > + return -ENOSPC; > + } > + DRV_LOG(WARNING, "Port %d global hairpin queue counter object cannot be created " > + "by DevX.", priv->dev_data->port_id); > + return -ENOMEM; > + } > + > + /* go over each queue and attach to global counter */ > + for (i = 0; (i != priv->rxqs_n); ++i) { > + rxq = mlx5_rxq_get(dev, i); > + > + if (rxq == NULL || rxq->ctrl->obj->rq == NULL || !rxq->ctrl->is_hairpin) > + continue; > + > + ret = priv->obj_ops.rxq_obj_modify_counter_set_id(rxq, priv->q_counter_hairpin->id); > + if (ret) { > + DRV_LOG(ERR, "failed to modify rq object for port %u" > + "%s", priv->dev_data->port_id, strerror(rte_errno)); > + return ret; > + } > + } > + > + /* Reset oob stats. */ > + mlx5_reset_xstats_by_name(priv, "hairpin_out_of_buffer"); > + return 0; > +} > + > +/** > + * Creates a queue counter for hairpin Rx queue. > + * > + * @param dev > + * Pointer to Ethernet device structure. > + * @param id > + * Index of the RX queue to disable the hairpin queue counter for. > + * @return > + * 0 on success, a negative errno value otherwise and rte_errno is set. > + */ > +int > +mlx5_enable_per_queue_hairpin_counter(struct rte_eth_dev *dev, uint64_t id) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_rxq_priv *rxq; > + struct mlx5_rxq_data *rxq_data; > + > + int ret = mlx5_hairpin_queue_counter_supported(priv); > + if (ret) { > + DRV_LOG(DEBUG, "Hairpin out of buffer counter " > + "is not available on this NIC."); > + return ret; > + } > + > + /* check if we have port level counter enabled. if yes, don't set the queue level counter */ > + if (priv->q_counter_hairpin) { > + DRV_LOG(WARNING, "Hairpin out of buffer counter is enabled per port."); > + return -EINVAL; > + } > + > + rxq = mlx5_rxq_get(dev, id); > + if (rxq == NULL || rxq->ctrl->obj->rq == NULL || !rxq->ctrl->is_hairpin) > + return -EINVAL; > + > + if (rxq->q_counter != NULL) > + return 0; > + > + /* Alloc hairpin queue counter. */ > + rxq->q_counter = mlx5_devx_cmd_queue_counter_alloc > + (priv->sh->cdev->ctx, NULL); > + if (rxq->q_counter == NULL) { > + if (ret == MLX5_Q_COUNTERS_LIMIT_REACHED) { > + DRV_LOG(WARNING, "Maximum number of queue counters reached. " > + "Unable to create counter object for Port %d, Queue %d " > + "using DevX. The counter from this queue will not increment.", > + priv->dev_data->port_id, rxq->idx); > + return -ENOSPC; > + } > + DRV_LOG(WARNING, "Port %d queue %d counter object cannot be created " > + "by DevX. Counter from this queue will not increment.", > + priv->dev_data->port_id, rxq->idx); > + return -ENOMEM; > + } > + > + ret = priv->obj_ops.rxq_obj_modify_counter_set_id(rxq, rxq->q_counter->id); > + if (ret) { > + DRV_LOG(ERR, "failed to modify rq object for port %u" > + "%s", priv->dev_data->port_id, strerror(rte_errno)); > + return ret; > + } > + > + rxq_data = mlx5_rxq_data_get(dev, id); > + if (rxq_data != NULL) > + rxq_data->stats.oobs.ctrl.enabled = 1; > + > + priv->num_of_hairpin_q_counter_enabled++; > + return 0; > +} > + > +/** > + * Disables the hairpin queue counter for a specified RX queue. > + * > + * @param dev > + * Pointer to Ethernet device structure. > + * @param id > + * Index of the RX queue to disable the hairpin queue counter for. > + * @return > + * 0 on success, a negative errno value otherwise and rte_errno is set. > + */ > +int > +mlx5_disable_per_queue_hairpin_counter(struct rte_eth_dev *dev, uint64_t id) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_rxq_priv *rxq; > + struct mlx5_rxq_data *rxq_data; > + int ret = 0; > + > + rxq = mlx5_rxq_get(dev, id); > + rxq_data = mlx5_rxq_data_get(dev, id); > + > + if (rxq == NULL || rxq->ctrl->obj->rq == NULL || !rxq->ctrl->is_hairpin) > + return 0; > + > + if (rxq->q_counter != NULL) { > + /* Modify rxq. */ > + ret = priv->obj_ops.rxq_obj_modify_counter_set_id(rxq, 0); > + if (ret) > + DRV_LOG(ERR, "Port %u failed to modify rq object " > + " %s", priv->dev_data->port_id, strerror(rte_errno)); > + > + mlx5_devx_cmd_destroy(rxq->q_counter); > + rxq->q_counter = NULL; > + } > + > + /* Reset queue oob stats. */ > + if (rxq_data != NULL) { > + rxq_data->stats.oobs.count = 0; > + rxq_data->stats_reset.oobs.count = 0; > + rxq_data->stats.oobs.ctrl.enabled = 0; > + } > + > + priv->num_of_hairpin_q_counter_enabled--; > + return 0; > +} > + > +/** > + * Read statistics per queue by a named counter. > + * > + * @param[in] q_counter > + * Pointer to the queue's counter object. > + * @param[in] ctr_name > + * Pointer to the name of the statistic counter to read > + * @param[out] stat > + * Pointer to read statistic value. > + * @return > + * 0 on success and stat is valid, 1 if failed to read the value > + * rte_errno is set. > + * > + */ > +int > +mlx5_read_queue_counter(struct mlx5_devx_obj *q_counter, const char *ctr_name, > + uint64_t *stat) > +{ > + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { > + DRV_LOG(WARNING, > + "DevX %s counter is not supported in the secondary process", ctr_name); > + return -ENOTSUP; > + } > + > + if (q_counter == NULL) > + return -EINVAL; > + > + return mlx5_devx_cmd_queue_counter_query(q_counter, 0, (uint32_t *)stat); > +} > + > /** > * Callback to remove a device. > * > diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h > index 89d277b523..a67d7166cc 100644 > --- a/drivers/net/mlx5/mlx5.h > +++ b/drivers/net/mlx5/mlx5.h > @@ -261,17 +261,32 @@ struct mlx5_local_data { > > extern struct mlx5_shared_data *mlx5_shared_data; > > +int mlx5_xstats_enable(struct rte_eth_dev *dev, uint64_t id); > +int mlx5_xstats_disable(struct rte_eth_dev *dev, uint64_t id); > +int mlx5_xstats_query_state(struct rte_eth_dev *dev, uint64_t id); > + > /* Dev ops structs */ > extern const struct eth_dev_ops mlx5_dev_ops; > extern const struct eth_dev_ops mlx5_dev_sec_ops; > extern const struct eth_dev_ops mlx5_dev_ops_isolate; > > +typedef int (*mlx5_enable_counter_t)(struct rte_eth_dev *dev, uint64_t id); > +typedef int (*mlx5_disable_counter_t)(struct rte_eth_dev *dev, uint64_t id); > +typedef int (*mlx5_xstats_query_state_t)(struct rte_eth_dev *dev, uint64_t id); > + > +struct mlx5_stat_counter_ctrl { > + mlx5_enable_counter_t enable; > + mlx5_disable_counter_t disable; > + uint32_t enabled; > +}; > + > struct mlx5_counter_ctrl { > /* Name of the counter. */ > char dpdk_name[RTE_ETH_XSTATS_NAME_SIZE]; > /* Name of the counter on the device table. */ > char ctr_name[RTE_ETH_XSTATS_NAME_SIZE]; > uint32_t dev:1; /**< Nonzero for dev counters. */ > + struct mlx5_stat_counter_ctrl ctrl; > }; > > struct mlx5_xstats_ctrl { > @@ -1783,6 +1798,7 @@ struct mlx5_priv; > /* HW objects operations structure. */ > struct mlx5_obj_ops { > int (*rxq_obj_modify_vlan_strip)(struct mlx5_rxq_priv *rxq, int on); > + int (*rxq_obj_modify_counter_set_id)(struct mlx5_rxq_priv *rxq, uint32_t counter_set_id); > int (*rxq_obj_new)(struct mlx5_rxq_priv *rxq); > int (*rxq_event_get)(struct mlx5_rxq_obj *rxq_obj); > int (*rxq_obj_modify)(struct mlx5_rxq_priv *rxq, uint8_t type); > @@ -2044,12 +2060,12 @@ struct mlx5_priv { > LIST_HEAD(fdir, mlx5_fdir_flow) fdir_flows; /* fdir flows. */ > rte_spinlock_t shared_act_sl; /* Shared actions spinlock. */ > uint32_t rss_shared_actions; /* RSS shared actions. */ > - /* If true, indicates that we failed to allocate a q counter in the past. */ > - bool q_counters_allocation_failure; > + /**< Total number of hairpin queues attach to q counters. */ > + uint64_t num_of_hairpin_q_counter_enabled; > struct mlx5_devx_obj *q_counters; /* DevX queue counter object. */ > uint32_t counter_set_id; /* Queue counter ID to set in DevX objects. */ > /* DevX queue counter object for all hairpin queues of the port. */ > - struct mlx5_devx_obj *q_counters_hairpin; > + struct mlx5_devx_obj *q_counter_hairpin; > uint32_t lag_affinity_idx; /* LAG mode queue 0 affinity starting. */ > rte_spinlock_t flex_item_sl; /* Flex item list spinlock. */ > struct mlx5_flex_item flex_item[MLX5_PORT_FLEX_ITEM_NUM]; > @@ -2224,6 +2240,10 @@ bool mlx5_is_sf_repr(struct rte_eth_dev *dev); > void mlx5_age_event_prepare(struct mlx5_dev_ctx_shared *sh); > int mlx5_lwm_setup(struct mlx5_priv *priv); > void mlx5_lwm_unset(struct mlx5_dev_ctx_shared *sh); > +int mlx5_enable_port_level_hairpin_counter(struct rte_eth_dev *dev, uint64_t id); > +int mlx5_disable_port_level_hairpin_counter(struct rte_eth_dev *dev, uint64_t id); > +int mlx5_enable_per_queue_hairpin_counter(struct rte_eth_dev *dev, uint64_t id); > +int mlx5_disable_per_queue_hairpin_counter(struct rte_eth_dev *dev, uint64_t id); > > /* Macro to iterate over all valid ports for mlx5 driver. */ > #define MLX5_ETH_FOREACH_DEV(port_id, dev) \ > @@ -2257,6 +2277,7 @@ int mlx5_flow_aso_ct_mng_init(struct mlx5_dev_ctx_shared *sh); > struct mlx5_physical_device * > mlx5_get_locked_physical_device(struct mlx5_priv *priv); > void mlx5_unlock_physical_device(void); > +int mlx5_read_queue_counter(struct mlx5_devx_obj *q_counter, const char *ctr_name, uint64_t *stat); > > /* mlx5_ethdev.c */ > > @@ -2364,6 +2385,8 @@ int mlx5_xstats_reset(struct rte_eth_dev *dev); > int mlx5_xstats_get_names(struct rte_eth_dev *dev __rte_unused, > struct rte_eth_xstat_name *xstats_names, > unsigned int n); > +void mlx5_reset_xstats_by_name(struct mlx5_priv *priv, const char *ctr_name); > +void mlx5_reset_xstats_rq(struct rte_eth_dev *dev); > > /* mlx5_vlan.c */ > > diff --git a/drivers/net/mlx5/mlx5_devx.c b/drivers/net/mlx5/mlx5_devx.c > index 8ebe784000..a12891a983 100644 > --- a/drivers/net/mlx5/mlx5_devx.c > +++ b/drivers/net/mlx5/mlx5_devx.c > @@ -91,6 +91,30 @@ mlx5_rxq_obj_modify_rq_vlan_strip(struct mlx5_rxq_priv *rxq, int on) > return mlx5_devx_cmd_modify_rq(rxq->devx_rq.rq, &rq_attr); > } > > +/** > + * Modify the q counter of a given RQ > + * > + * @param rxq > + * Rx queue. > + * @param counter_set_id > + * Q counter id to set > + * > + * @return > + * 0 on success, non-0 otherwise > + */ > +static int > +mlx5_rxq_obj_modify_counter(struct mlx5_rxq_priv *rxq, uint32_t counter_set_id) > +{ > + struct mlx5_devx_modify_rq_attr rq_attr; > + > + memset(&rq_attr, 0, sizeof(rq_attr)); > + rq_attr.rq_state = MLX5_RQC_STATE_RDY; > + rq_attr.state = MLX5_RQC_STATE_RDY; > + rq_attr.counter_set_id = counter_set_id; > + rq_attr.modify_bitmask = MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_RQ_COUNTER_SET_ID; > + return mlx5_devx_cmd_modify_rq(rxq->ctrl->obj->rq, &rq_attr); > +} > + > /** > * Modify RQ using DevX API. > * > @@ -496,55 +520,6 @@ mlx5_rxq_create_devx_cq_resources(struct mlx5_rxq_priv *rxq) > return 0; > } > > -/** > - * Create a global queue counter for all the port hairpin queues. > - * > - * @param priv > - * Device private data. > - * > - * @return > - * The counter_set_id of the queue counter object, 0 otherwise. > - */ > -static uint32_t > -mlx5_set_hairpin_queue_counter_obj(struct mlx5_priv *priv) > -{ > - if (priv->q_counters_hairpin != NULL) > - return priv->q_counters_hairpin->id; > - > - /* Queue counter allocation failed in the past - don't try again. */ > - if (priv->q_counters_allocation_failure != 0) > - return 0; > - > - if (priv->pci_dev == NULL) { > - DRV_LOG(DEBUG, "Hairpin out of buffer counter is " > - "only supported on PCI device."); > - priv->q_counters_allocation_failure = 1; > - return 0; > - } > - > - switch (priv->pci_dev->id.device_id) { > - /* Counting out of buffer drops on hairpin queues is supported only on CX7 and up. */ > - case PCI_DEVICE_ID_MELLANOX_CONNECTX7: > - case PCI_DEVICE_ID_MELLANOX_CONNECTXVF: > - case PCI_DEVICE_ID_MELLANOX_BLUEFIELD3: > - case PCI_DEVICE_ID_MELLANOX_BLUEFIELDVF: > - > - priv->q_counters_hairpin = mlx5_devx_cmd_queue_counter_alloc(priv->sh->cdev->ctx); > - if (priv->q_counters_hairpin == NULL) { > - /* Failed to allocate */ > - DRV_LOG(DEBUG, "Some of the statistics of port %d " > - "will not be available.", priv->dev_data->port_id); > - priv->q_counters_allocation_failure = 1; > - return 0; > - } > - return priv->q_counters_hairpin->id; > - default: > - DRV_LOG(DEBUG, "Hairpin out of buffer counter " > - "is not available on this NIC."); > - priv->q_counters_allocation_failure = 1; > - return 0; > - } > -} > > /** > * Create the Rx hairpin queue object. > @@ -592,7 +567,6 @@ mlx5_rxq_obj_hairpin_new(struct mlx5_rxq_priv *rxq) > unlocked_attr.wq_attr.log_hairpin_data_sz - > MLX5_HAIRPIN_QUEUE_STRIDE; > > - unlocked_attr.counter_set_id = mlx5_set_hairpin_queue_counter_obj(priv); > > rxq_ctrl->rxq.delay_drop = priv->config.hp_delay_drop; > unlocked_attr.delay_drop_en = priv->config.hp_delay_drop; > @@ -1710,6 +1684,7 @@ mlx5_txq_devx_obj_release(struct mlx5_txq_obj *txq_obj) > > struct mlx5_obj_ops devx_obj_ops = { > .rxq_obj_modify_vlan_strip = mlx5_rxq_obj_modify_rq_vlan_strip, > + .rxq_obj_modify_counter_set_id = mlx5_rxq_obj_modify_counter, > .rxq_obj_new = mlx5_rxq_devx_obj_new, > .rxq_event_get = mlx5_rx_devx_get_event, > .rxq_obj_modify = mlx5_devx_modify_rq, > diff --git a/drivers/net/mlx5/mlx5_rx.h b/drivers/net/mlx5/mlx5_rx.h > index 1a6f174c40..f80a2e3227 100644 > --- a/drivers/net/mlx5/mlx5_rx.h > +++ b/drivers/net/mlx5/mlx5_rx.h > @@ -30,6 +30,12 @@ > /* First entry must be NULL for comparison. */ > #define mlx5_mr_btree_len(bt) ((bt)->len - 1) > > +struct mlx5_rxq_stat { > + int id; > + uint64_t count; > + struct mlx5_stat_counter_ctrl ctrl; > +}; > + > struct mlx5_rxq_stats { > #ifdef MLX5_PMD_SOFT_COUNTERS > uint64_t ipackets; /**< Total of successfully received packets. */ > @@ -37,6 +43,18 @@ struct mlx5_rxq_stats { > #endif > uint64_t idropped; /**< Total of packets dropped when RX ring full. */ > uint64_t rx_nombuf; /**< Total of RX mbuf allocation failures. */ > + struct mlx5_rxq_stat oobs; /**< Total of hairpin queue out of buffers. */ > +}; > + > +/* store statistics names and its offset in stats structure */ > +struct mlx5_xstats_name_off { > + char name[RTE_ETH_XSTATS_NAME_SIZE]; > + unsigned int offset; > +}; > + > +struct mlx5_rq_stats { > + /** Total number of hairpin queue packets received that are dropped. */ > + uint64_t q_oobs[RTE_ETHDEV_QUEUE_STAT_CNTRS]; > }; > > /* Compressed CQE context. */ > @@ -183,6 +201,7 @@ struct mlx5_rxq_priv { > uint32_t lwm:16; > uint32_t lwm_event_pending:1; > uint32_t lwm_devx_subscribed:1; > + struct mlx5_devx_obj *q_counter; /* DevX hairpin queue counter object. */ > }; > > /* mlx5_rxq.c */ > @@ -208,6 +227,7 @@ void mlx5_rx_intr_vec_disable(struct rte_eth_dev *dev); > int mlx5_rx_intr_enable(struct rte_eth_dev *dev, uint16_t rx_queue_id); > int mlx5_rx_intr_disable(struct rte_eth_dev *dev, uint16_t rx_queue_id); > int mlx5_rxq_obj_verify(struct rte_eth_dev *dev); > +void mlx5_q_counters_destroy(struct rte_eth_dev *dev); > struct mlx5_rxq_ctrl *mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, > uint16_t desc, unsigned int socket, > const struct rte_eth_rxconf *conf, > diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c > index 126b1970e6..a5971b5cdd 100644 > --- a/drivers/net/mlx5/mlx5_rxq.c > +++ b/drivers/net/mlx5/mlx5_rxq.c > @@ -1314,6 +1314,42 @@ mlx5_rxq_obj_verify(struct rte_eth_dev *dev) > return ret; > } > > +/** > + * Destroy all queue counters. > + * > + * @param dev > + * Pointer to Ethernet device. > + */ > +void > +mlx5_q_counters_destroy(struct rte_eth_dev *dev) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + unsigned int i; > + > + /* Destroy port q counter */ > + if (priv->q_counters) { > + mlx5_devx_cmd_destroy(priv->q_counters); > + priv->q_counters = NULL; > + } > + > + /* Destroy port global hairpin q counter */ > + if (priv->q_counter_hairpin) { > + mlx5_devx_cmd_destroy(priv->q_counter_hairpin); > + priv->q_counter_hairpin = NULL; > + } > + > + /* Destroy per hairpin queue counter */ > + for (i = 0; i != priv->rxqs_n; ++i) { > + struct mlx5_rxq_priv *rxq = mlx5_rxq_get(dev, i); > + > + if (rxq == NULL || rxq->q_counter == NULL) > + continue; > + > + mlx5_devx_cmd_destroy(rxq->q_counter); > + rxq->q_counter = NULL; > + } > +} > + > /** > * Callback function to initialize mbufs for Multi-Packet RQ. > */ > diff --git a/drivers/net/mlx5/mlx5_stats.c b/drivers/net/mlx5/mlx5_stats.c > index f4ac58e2f9..fce774c849 100644 > --- a/drivers/net/mlx5/mlx5_stats.c > +++ b/drivers/net/mlx5/mlx5_stats.c > @@ -20,6 +20,107 @@ > #include "mlx5_tx.h" > #include "mlx5_malloc.h" > > + > +static const struct mlx5_xstats_name_off mlx5_rxq_stats_strings[] = { > + {"out_of_buffer", offsetof(struct mlx5_rq_stats, q_oobs)}, > +}; > + > +#define NB_RXQ_STATS RTE_DIM(mlx5_rxq_stats_strings) > + > +/** > + * Retrieve extended device statistics > + * for Rx queues. It appends the specific statistics > + * before the parts filled by preceding modules (eth stats, etc.) > + * > + * @param dev > + * Pointer to Ethernet device. > + * @param[out] stats > + * Pointer to an array to store the retrieved statistics. > + * @return > + * Number of extended stats is filled, > + * negative on error and rte_errno is set. > + */ > +static int > +mlx5_rq_xstats_get(struct rte_eth_dev *dev, > + struct rte_eth_xstat *stats) > +{ > + uint16_t n_stats_rq = RTE_MIN(dev->data->nb_rx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS); > + int cnt_used_entries = 0; > + > + for (unsigned int idx = 0; idx < n_stats_rq; idx++) { > + struct mlx5_rxq_data *rxq_data = mlx5_rxq_data_get(dev, idx); > + struct mlx5_rxq_priv *rxq_priv = mlx5_rxq_get(dev, idx); > + > + if (rxq_data == NULL) > + continue; > + > + struct mlx5_rxq_stat *rxq_stat = &rxq_data->stats.oobs; > + if (rxq_stat == NULL) > + continue; > + > + /* Handle initial stats setup - Flag uninitialized stat */ > + rxq_stat->id = -1; > + > + /* Handle hairpin statistics */ > + if (rxq_priv && rxq_priv->ctrl->is_hairpin) { > + if (stats) { > + mlx5_read_queue_counter(rxq_priv->q_counter, "hairpin_out_of_buffer", > + &rxq_stat->count); > + rxq_stat->ctrl.enable = mlx5_enable_per_queue_hairpin_counter; > + rxq_stat->ctrl.disable = mlx5_disable_per_queue_hairpin_counter; > + > + stats[cnt_used_entries].id = cnt_used_entries; > + stats[cnt_used_entries].value = rxq_stat->count - > + rxq_data->stats_reset.oobs.count; > + } > + rxq_stat->id = cnt_used_entries; > + cnt_used_entries++; > + } > + } > + return cnt_used_entries; > +} > + > +/** > + * Retrieve names of extended device statistics > + * for Rx queues. It appends the specific stats names > + * before the parts filled by preceding modules (eth stats, etc.) > + * > + * @param dev > + * Pointer to Ethernet device structure. > + * @param[out] xstats_names > + * Buffer to insert names into. > + * > + * @return > + * Number of xstats names. > + */ > +static int > +mlx5_rq_xstats_get_names(struct rte_eth_dev *dev __rte_unused, > + struct rte_eth_xstat_name *xstats_names) > +{ > + struct mlx5_rxq_priv *rxq; > + unsigned int i; > + int cnt_used_entries = 0; > + > + uint16_t n_stats_rq = RTE_MIN(dev->data->nb_rx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS); > + > + for (i = 0; (i != n_stats_rq); ++i) { > + rxq = mlx5_rxq_get(dev, i); > + > + if (rxq == NULL) > + continue; > + > + if (rxq->ctrl->is_hairpin) { > + if (xstats_names) > + snprintf(xstats_names[cnt_used_entries].name, > + sizeof(xstats_names[0].name), > + "hairpin_%s_rxq%u", > + mlx5_rxq_stats_strings[0].name, i); > + cnt_used_entries++; > + } > + } > + return cnt_used_entries; > +} > + > /** > * DPDK callback to get extended device statistics. > * > @@ -46,6 +147,7 @@ mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats, > uint16_t stats_n_2nd = 0; > uint16_t mlx5_stats_n = xstats_ctrl->mlx5_stats_n; > bool bond_master = (priv->master && priv->pf_bond >= 0); > + int n_used = mlx5_rq_xstats_get(dev, stats); > > if (n >= mlx5_stats_n && stats) { > int ret; > @@ -69,27 +171,27 @@ mlx5_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *stats, > if (ret < 0) > return ret; > for (i = 0; i != mlx5_stats_n; i++) { > - stats[i].id = i; > + stats[i + n_used].id = i + n_used; > if (xstats_ctrl->info[i].dev) { > uint64_t wrap_n; > uint64_t hw_stat = xstats_ctrl->hw_stats[i]; > > - stats[i].value = (counters[i] - > + stats[i + n_used].value = (counters[i] - > xstats_ctrl->base[i]) & > (uint64_t)UINT32_MAX; > wrap_n = hw_stat >> 32; > - if (stats[i].value < > + if (stats[i + n_used].value < > (hw_stat & (uint64_t)UINT32_MAX)) > wrap_n++; > - stats[i].value |= (wrap_n) << 32; > - xstats_ctrl->hw_stats[i] = stats[i].value; > + stats[i + n_used].value |= (wrap_n) << 32; > + xstats_ctrl->hw_stats[i] = stats[i + n_used].value; > } else { > - stats[i].value = > + stats[i + n_used].value = > (counters[i] - xstats_ctrl->base[i]); > } > } > } > - mlx5_stats_n = mlx5_txpp_xstats_get(dev, stats, n, mlx5_stats_n); > + mlx5_stats_n = mlx5_txpp_xstats_get(dev, stats, n, mlx5_stats_n + n_used); > return mlx5_stats_n; > } > > @@ -273,11 +375,58 @@ mlx5_xstats_reset(struct rte_eth_dev *dev) > xstats_ctrl->base[i] = counters[i]; > xstats_ctrl->hw_stats[i] = 0; > } > + mlx5_reset_xstats_rq(dev); > mlx5_txpp_xstats_reset(dev); > mlx5_free(counters); > return 0; > } > > +void > +mlx5_reset_xstats_by_name(struct mlx5_priv *priv, const char *ctr_name) > +{ > + struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; > + unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n; > + unsigned int i; > + > + for (i = 0; i != mlx5_xstats_n; ++i) { > + if (strcmp(xstats_ctrl->info[i].ctr_name, ctr_name) == 0) { > + xstats_ctrl->base[i] = 0; > + xstats_ctrl->hw_stats[i] = 0; > + xstats_ctrl->xstats[i] = 0; > + return; > + } > + } > +} > + > +/** > + * Clear device extended statistics for each Rx queue. > + * > + * @param dev > + * Pointer to Ethernet device structure. > + */ > +void > +mlx5_reset_xstats_rq(struct rte_eth_dev *dev) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_rxq_priv *rxq; > + struct mlx5_rxq_data *rxq_data; > + unsigned int i; > + > + for (i = 0; (i != priv->rxqs_n); ++i) { > + rxq = mlx5_rxq_get(dev, i); > + rxq_data = mlx5_rxq_data_get(dev, i); > + > + if (rxq == NULL || rxq_data == NULL || rxq->q_counter == NULL) > + continue; > + if (rxq->ctrl->is_hairpin) > + mlx5_read_queue_counter(rxq->q_counter, > + "hairpin_out_of_buffer", &rxq_data->stats_reset.oobs.count); > + else > + mlx5_read_queue_counter(rxq->q_counter, > + "out_of_buffer", &rxq_data->stats_reset.oobs.count); > + } > +} > + > /** > * DPDK callback to retrieve names of extended device statistics > * > @@ -299,15 +448,140 @@ mlx5_xstats_get_names(struct rte_eth_dev *dev, > struct mlx5_priv *priv = dev->data->dev_private; > struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; > unsigned int mlx5_xstats_n = xstats_ctrl->mlx5_stats_n; > + unsigned int n_used = mlx5_rq_xstats_get_names(dev, xstats_names); > > if (n >= mlx5_xstats_n && xstats_names) { > for (i = 0; i != mlx5_xstats_n; ++i) { > - strlcpy(xstats_names[i].name, > + rte_strscpy(xstats_names[i + n_used].name, > xstats_ctrl->info[i].dpdk_name, > RTE_ETH_XSTATS_NAME_SIZE); > + xstats_names[i + n_used].name[RTE_ETH_XSTATS_NAME_SIZE - 1] = 0; > } > } > mlx5_xstats_n = mlx5_txpp_xstats_get_names(dev, xstats_names, > - n, mlx5_xstats_n); > + n, mlx5_xstats_n + n_used); > return mlx5_xstats_n; > } > + > +static struct mlx5_stat_counter_ctrl* > +mlx5_rxq_get_counter_by_id(struct rte_eth_dev *dev, uint64_t id, uint64_t *rq_id) > +{ > + uint16_t n_stats_rq = RTE_MIN(dev->data->nb_rx_queues, RTE_ETHDEV_QUEUE_STAT_CNTRS); > + > + for (int i = 0; (i != n_stats_rq); i++) { > + struct mlx5_rxq_data *rxq_data = mlx5_rxq_data_get(dev, i); > + if (rxq_data == NULL || rxq_data->stats.oobs.id == -1) > + continue; > + > + if ((uint64_t)rxq_data->stats.oobs.id == id) { > + *rq_id = rxq_data->idx; > + return &rxq_data->stats.oobs.ctrl; > + } > + } > + > + return NULL; > +} > + > +/** > + * Callback to enable an xstat counter of the given id. > + * > + * @param dev > + * Pointer to Ethernet device structure. > + * @param id > + * The ID of the counter to enable > + * > + * @return > + * 1 xstat is enabled, 0 if xstat is disabled, > + * -ENOTSUP if enabling/disabling is not implemented and -EINVAL if xstat id is invalid. > + */ > +int > +mlx5_xstats_enable(struct rte_eth_dev *dev, uint64_t id) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; > + struct mlx5_stat_counter_ctrl *counter_ctrl = NULL; > + uint16_t n_stats_rq = mlx5_rq_xstats_get(dev, NULL); > + > + if (id < n_stats_rq) > + counter_ctrl = mlx5_rxq_get_counter_by_id(dev, id, &id); > + else > + counter_ctrl = &xstats_ctrl->info[id - n_stats_rq].ctrl; > + > + if (counter_ctrl == NULL) > + return -EINVAL; > + > + if (counter_ctrl->enable == NULL) > + return -ENOTSUP; > + > + counter_ctrl->enabled = counter_ctrl->enable(dev, id) == 0 ? 1 : 0; > + return counter_ctrl->enabled; > +} > + > +/** > + * Callback to disable an xstat counter of the given id. > + * > + * @param dev > + * Pointer to Ethernet device structure. > + * @param id > + * The ID of the counter to enable > + * > + * @return > + * 1 if xstat is disabled, 0 xstat is enabled, > + * -ENOTSUP if enabling/disabling is not implemented and -EINVAL if xstat id is invalid. > + */ > +int > +mlx5_xstats_disable(struct rte_eth_dev *dev, uint64_t id) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; > + struct mlx5_stat_counter_ctrl *counter_ctrl = NULL; > + > + uint16_t n_stats_rq = mlx5_rq_xstats_get(dev, NULL); > + if (id < n_stats_rq) > + counter_ctrl = mlx5_rxq_get_counter_by_id(dev, id, &id); > + else > + counter_ctrl = &xstats_ctrl->info[id - n_stats_rq].ctrl; > + > + if (counter_ctrl == NULL) > + return -EINVAL; > + > + if (counter_ctrl->disable == NULL) > + return -ENOTSUP; > + > + counter_ctrl->enabled = counter_ctrl->disable(dev, id) == 0 ? 0 : 1; > + return counter_ctrl->enabled; > +} > + > +/** > + * Query the state of the xstat counter. > + * > + * @param dev > + * Pointer to Ethernet device structure. > + * @param id > + * The ID of the counter to enable > + * > + * @return > + * 1 if xstat is disabled, 0 xstat is enabled, > + * -ENOTSUP if enabling/disabling is not implemented and -EINVAL if xstat id is invalid. > + */ > +int > +mlx5_xstats_query_state(struct rte_eth_dev *dev, uint64_t id) > +{ > + struct mlx5_priv *priv = dev->data->dev_private; > + struct mlx5_xstats_ctrl *xstats_ctrl = &priv->xstats_ctrl; > + struct mlx5_stat_counter_ctrl *counter_ctrl = NULL; > + > + uint16_t n_stats_rq = mlx5_rq_xstats_get(dev, NULL); > + if (id < n_stats_rq) > + counter_ctrl = mlx5_rxq_get_counter_by_id(dev, id, &id); > + else > + counter_ctrl = &xstats_ctrl->info[id - n_stats_rq].ctrl; > + > + if (counter_ctrl == NULL) > + return -EINVAL; > + > + if (counter_ctrl->disable == NULL) > + return -ENOTSUP; > + > + return counter_ctrl->enabled; > +} > diff --git a/drivers/net/mlx5/windows/mlx5_os.c b/drivers/net/mlx5/windows/mlx5_os.c > index 268598f209..d583730066 100644 > --- a/drivers/net/mlx5/windows/mlx5_os.c > +++ b/drivers/net/mlx5/windows/mlx5_os.c > @@ -78,12 +78,11 @@ mlx5_queue_counter_id_prepare(struct rte_eth_dev *dev) > struct mlx5_priv *priv = dev->data->dev_private; > void *ctx = priv->sh->cdev->ctx; > > - priv->q_counters = mlx5_devx_cmd_queue_counter_alloc(ctx); > + priv->q_counters = mlx5_devx_cmd_queue_counter_alloc(ctx, NULL); > if (!priv->q_counters) { > DRV_LOG(ERR, "Port %d queue counter object cannot be created " > "by DevX - imissed counter will be unavailable", > dev->data->port_id); > - priv->q_counters_allocation_failure = 1; > return; > } > priv->counter_set_id = priv->q_counters->id; > diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h > index 1fd4562b40..74995df7f4 100644 > --- a/lib/ethdev/ethdev_driver.h > +++ b/lib/ethdev/ethdev_driver.h > @@ -570,6 +570,15 @@ typedef const uint32_t *(*eth_dev_supported_ptypes_get_t)(struct rte_eth_dev *de > typedef int (*eth_dev_ptypes_set_t)(struct rte_eth_dev *dev, > uint32_t ptype_mask); > > +/** @internal Enable an xstat of an Ethernet device. */ > +typedef int (*eth_xstats_enable_counter_t)(struct rte_eth_dev *dev, uint64_t id); > + > +/** @internal Disable an xstat of an Ethernet device. */ > +typedef int (*eth_xstats_disable_counter_t)(struct rte_eth_dev *dev, uint64_t id); > + > +/** @internal Query the state of an xstat the can be enabled and disabled in runtime. */ > +typedef int (*eth_xstats_query_state_t)(struct rte_eth_dev *dev, uint64_t id); > + > /** @internal Start Rx and Tx of a queue of an Ethernet device. */ > typedef int (*eth_queue_start_t)(struct rte_eth_dev *dev, > uint16_t queue_id); > @@ -1528,6 +1537,10 @@ struct eth_dev_ops { > /** Get name of extended device statistics by ID */ > eth_xstats_get_names_by_id_t xstats_get_names_by_id; > > + eth_xstats_enable_counter_t xstats_enable; > + eth_xstats_disable_counter_t xstats_disable; > + eth_xstats_query_state_t xstats_query_state; > + > /** Get Traffic Management (TM) operations */ > eth_tm_ops_get_t tm_ops_get; > > diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c > index 6413c54e3b..6964d69901 100644 > --- a/lib/ethdev/rte_ethdev.c > +++ b/lib/ethdev/rte_ethdev.c > @@ -3776,6 +3776,72 @@ rte_eth_xstats_reset(uint16_t port_id) > return rte_eth_stats_reset(port_id); > } > > +int > +rte_eth_xstats_enable_counter(uint16_t port_id, uint64_t id) > +{ > + struct rte_eth_dev *dev; > + unsigned int basic_count; > + > + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV); > + > + if (rte_eth_xstats_query_state(port_id, id) == 1) > + return -EEXIST; > + > + dev = &rte_eth_devices[port_id]; > + basic_count = eth_dev_get_xstats_basic_count(dev); > + if (id < basic_count) > + return -EINVAL; > + > + /* implemented by the driver */ > + if (dev->dev_ops->xstats_enable != NULL) > + return (*dev->dev_ops->xstats_enable)(dev, id - basic_count); > + > + return -ENOTSUP; > +} > + > +int > +rte_eth_xstats_disable_counter(uint16_t port_id, uint64_t id) > +{ > + struct rte_eth_dev *dev; > + unsigned int basic_count; > + > + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV); > + > + if (rte_eth_xstats_query_state(port_id, id) == 0) > + return 0; > + > + dev = &rte_eth_devices[port_id]; > + basic_count = eth_dev_get_xstats_basic_count(dev); > + if (id < basic_count) > + return -EINVAL; > + > + /* implemented by the driver */ > + if (dev->dev_ops->xstats_disable != NULL) > + return (*dev->dev_ops->xstats_disable)(dev, id - basic_count); > + > + return -ENOTSUP; > +} > + > +int > +rte_eth_xstats_query_state(uint16_t port_id, uint64_t id) > +{ > + struct rte_eth_dev *dev; > + unsigned int basic_count; > + > + RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV); > + > + dev = &rte_eth_devices[port_id]; > + basic_count = eth_dev_get_xstats_basic_count(dev); > + if (id < basic_count) > + return -ENOTSUP; > + > + /* implemented by the driver */ > + if (dev->dev_ops->xstats_query_state != NULL) > + return (*dev->dev_ops->xstats_query_state)(dev, id - basic_count); > + > + return -ENOTSUP; > +} > + > static int > eth_dev_set_queue_stats_mapping(uint16_t port_id, uint16_t queue_id, > uint8_t stat_idx, uint8_t is_rx) > diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h > index 1f71cad244..c3b3761f15 100644 > --- a/lib/ethdev/rte_ethdev.h > +++ b/lib/ethdev/rte_ethdev.h > @@ -3386,6 +3386,50 @@ int rte_eth_xstats_get_by_id(uint16_t port_id, const uint64_t *ids, > int rte_eth_xstats_get_id_by_name(uint16_t port_id, const char *xstat_name, > uint64_t *id); > > +/** > + * Enable the xstat counter of the given id. > + * > + * @param port_id The port to look up statistics from > + * @param id The ID of the counter to enable > + * @return > + * - (0) on success > + * - (-EEXIST) counter already enabled > + * - (-ENOTSUP) enable is not implemented > + * - (-EINVAL) xstat id is invalid > + * - (-EPERM) enabling this counter is not permitted > + * - (-ENOSPC) no resources > + */ > +__rte_experimental > +int rte_eth_xstats_enable_counter(uint16_t port_id, uint64_t id); > + > +/** > + * Disable the xstat counter of the given id. > + * > + * @param port_id The port to look up statistics from > + * @param id The ID of the counter to disable > + * @return > + * - (0) disabled successfully or already disabled > + * - (-ENOTSUP) enable is not implemented > + * - (-EINVAL) xstat id is invalid > + * - (-EPERM) disabling this counter is not permitted > + */ > +__rte_experimental > +int rte_eth_xstats_disable_counter(uint16_t port_id, uint64_t id); Rather than two calls, why not one hook with enable/disable bool argument. Lots of the code ends up being duplicated. > +/** > + * Query the state of the xstat counter. > + * > + * @param port_id The port to look up statistics from > + * @param id The ID of the counter to query > + * @return > + * - (0) xstat is enabled > + * - (1) xstat is disabled > + * - (-ENOTSUP) enable/disabling is not implemented > + * - (-EINVAL) xstat id is invalid > + */ > +__rte_experimental > +int rte_eth_xstats_query_state(uint16_t port_id, uint64_t id); > + > /** > * Reset extended statistics of an Ethernet device. > * > diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map > index 12f48c70a0..51eeaf2103 100644 > --- a/lib/ethdev/version.map > +++ b/lib/ethdev/version.map > @@ -337,6 +337,9 @@ EXPERIMENTAL { > rte_eth_timesync_adjust_freq; > rte_flow_async_create_by_index_with_pattern; > rte_tm_node_query; > + rte_eth_xstats_enable_counter; > + rte_eth_xstats_disable_counter; > + rte_eth_xstats_query_state; > }; > > INTERNAL {