DPDK patches and discussions
 help / color / mirror / Atom feed
From: Stephen Hemminger <stephen@networkplumber.org>
To: Shani Peretz <shperetz@nvidia.com>
Cc: <dev@dpdk.org>, Aman Singh <aman.deep.singh@intel.com>,
	Dariusz Sosnowski <dsosnowski@nvidia.com>,
	Viacheslav Ovsiienko <viacheslavo@nvidia.com>,
	Bing Zhao <bingz@nvidia.com>, Ori Kam <orika@nvidia.com>,
	Suanming Mou <suanmingm@nvidia.com>,
	Matan Azrad <matan@nvidia.com>,
	Thomas Monjalon <thomas@monjalon.net>,
	Ferruh Yigit <ferruh.yigit@amd.com>,
	Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>,
	Anatoly Burakov <anatoly.burakov@intel.com>
Subject: Re: [RFC PATCH] ethdev: add new API for enable/disable xstat counters by ID
Date: Thu, 9 Jan 2025 20:36:30 -0800	[thread overview]
Message-ID: <20250109203630.13322c14@hermes.local> (raw)
In-Reply-To: <20241222153819.62519-1-shperetz@nvidia.com>

On Sun, 22 Dec 2024 17:38:19 +0200
Shani Peretz <shperetz@nvidia.com> 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 <counter_name>  
> 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 <shperetz@nvidia.com>
> ---
>  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 <counter_name>",
> +	.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 {


      parent reply	other threads:[~2025-01-10  4:36 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-22 15:38 Shani Peretz
2024-12-22 16:46 ` Stephen Hemminger
2024-12-23 10:46   ` Shani Peretz
2024-12-24 17:19     ` Stephen Hemminger
2025-01-01 19:05       ` Shani Peretz
2025-01-10  4:36 ` Stephen Hemminger [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250109203630.13322c14@hermes.local \
    --to=stephen@networkplumber.org \
    --cc=aman.deep.singh@intel.com \
    --cc=anatoly.burakov@intel.com \
    --cc=andrew.rybchenko@oktetlabs.ru \
    --cc=bingz@nvidia.com \
    --cc=dev@dpdk.org \
    --cc=dsosnowski@nvidia.com \
    --cc=ferruh.yigit@amd.com \
    --cc=matan@nvidia.com \
    --cc=orika@nvidia.com \
    --cc=shperetz@nvidia.com \
    --cc=suanmingm@nvidia.com \
    --cc=thomas@monjalon.net \
    --cc=viacheslavo@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).