[RFC 5/7] regex/hs: add per-queue-pair extended statistics

Prudvi Deti prudvi.deti at intel.com
Fri Aug 28 07:35:54 CEST 2026


Add xstats support with three per-QP counters: enqueued, dequeued,
and matches. Implement xstats_names_get, xstats_get, and
xstats_reset with support for bulk and selective reset by stat id.

Signed-off-by: Prudvi Deti <prudvi.deti at intel.com>
---
 doc/guides/regexdevs/hs.rst |   9 ++
 drivers/regex/hs/hs_regex.c | 159 ++++++++++++++++++++++++++++++++++++
 2 files changed, 168 insertions(+)

diff --git a/doc/guides/regexdevs/hs.rst b/doc/guides/regexdevs/hs.rst
index e94466a..9c5f666 100644
--- a/doc/guides/regexdevs/hs.rst
+++ b/doc/guides/regexdevs/hs.rst
@@ -20,7 +20,16 @@ Features
 - Up to 64 queue pairs, each with up to 32768 descriptors
 - Up to 1,000,000 rules per device, with O(1) duplicate rule_id
   detection backed by ``rte_hash``
+- Up to 65,535 matches per scan operation (API field width limit);
+  cumulative totals are tracked via per-queue-pair xstats
 - Per-rule extended match parameters (minimum/maximum start offset)
+- Hyperscan block-mode engine supports scan buffers up to 4 GB
+  (library capability)
+- Through the current ``rte_regexdev`` API, this PMD advertises
+  ``max_payload_size = 65,535`` bytes (``uint16_t`` field width), so
+  ``dpdk-test-regex`` validation is limited to about 64 KB per op
+- Hyperscan uses x86 vectorized instructions (SSSE3/AVX2/AVX-512)
+  for high throughput
 - Per-queue-pair statistics via xstats
 
 In the RegEx driver feature matrix, this PMD reports:
diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
index e84259e..7841a83 100644
--- a/drivers/regex/hs/hs_regex.c
+++ b/drivers/regex/hs/hs_regex.c
@@ -849,6 +849,161 @@ hs_regex_dequeue_burst(struct rte_regexdev *dev, uint16_t qp_id,
 	return i;
 }
 
+/* xstats: per-QP statistics */
+
+/* 3 stats per QP: enqueued, dequeued, matches */
+#define HS_XSTATS_PER_QP 3
+
+static const char * const hs_xstat_suffixes[HS_XSTATS_PER_QP] = {
+	"enqueued", "dequeued", "matches"
+};
+
+static int
+hs_regex_xstats_names_get(struct rte_regexdev *dev,
+			  struct rte_regexdev_xstats_map *xstats_map)
+{
+	struct hs_regex_priv *priv;
+	uint16_t nqp;
+	int total;
+	int idx = 0;
+	uint16_t q;
+	int s;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	nqp = priv->nb_queue_pairs;
+	total = nqp * HS_XSTATS_PER_QP;
+
+	if (!xstats_map)
+		return total;
+
+	for (q = 0; q < nqp; q++) {
+		for (s = 0; s < HS_XSTATS_PER_QP; s++) {
+			snprintf(xstats_map[idx].name,
+				 sizeof(xstats_map[idx].name),
+				 "qp%u_%s", q, hs_xstat_suffixes[s]);
+			xstats_map[idx].id = idx;
+			idx++;
+		}
+	}
+	return total;
+}
+
+static int
+hs_regex_xstats_get(struct rte_regexdev *dev,
+		    const uint16_t *ids, uint64_t *values,
+		    uint16_t nb_values)
+{
+	struct hs_regex_priv *priv;
+	uint16_t nqp;
+	int total;
+	uint16_t id, qp_idx, stat_idx;
+	uint16_t i;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	nqp = priv->nb_queue_pairs;
+	total = nqp * HS_XSTATS_PER_QP;
+
+	if (!ids || !values)
+		return total;
+
+	if (priv->qps == NULL)
+		total = 0;
+
+	for (i = 0; i < nb_values; i++) {
+		id = ids[i];
+
+		if (id >= (uint16_t)total) {
+			values[i] = 0;
+			continue;
+		}
+
+		qp_idx = id / HS_XSTATS_PER_QP;
+		stat_idx = id % HS_XSTATS_PER_QP;
+
+		switch (stat_idx) {
+		case 0:
+			values[i] = priv->qps[qp_idx].qp_enqueued;
+			break;
+		case 1:
+			values[i] = priv->qps[qp_idx].qp_dequeued;
+			break;
+		case 2:
+			values[i] = priv->qps[qp_idx].qp_matches;
+			break;
+		}
+	}
+	return nb_values;
+}
+
+static int
+hs_regex_xstats_reset(struct rte_regexdev *dev,
+		      const uint16_t *ids, uint16_t nb_ids)
+{
+	struct hs_regex_priv *priv;
+	uint16_t nqp;
+	int total;
+	uint16_t q, i;
+	uint16_t id, qp_idx, stat_idx;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	nqp = priv->nb_queue_pairs;
+	total = nqp * HS_XSTATS_PER_QP;
+
+	if (priv->qps == NULL)
+		return 0;
+
+	if (!ids || nb_ids == 0) {
+		/* Reset all stats */
+		for (q = 0; q < nqp; q++) {
+			priv->qps[q].qp_enqueued = 0;
+			priv->qps[q].qp_dequeued = 0;
+			priv->qps[q].qp_matches = 0;
+		}
+	} else {
+		/* Reset specific stats by id */
+		for (i = 0; i < nb_ids; i++) {
+			id = ids[i];
+
+			if (id >= (uint16_t)total)
+				continue;
+
+			qp_idx = id / HS_XSTATS_PER_QP;
+			stat_idx = id % HS_XSTATS_PER_QP;
+
+			switch (stat_idx) {
+			case 0:
+				priv->qps[qp_idx].qp_enqueued = 0;
+				break;
+			case 1:
+				priv->qps[qp_idx].qp_dequeued = 0;
+				break;
+			case 2:
+				priv->qps[qp_idx].qp_matches = 0;
+				break;
+			}
+		}
+	}
+	return 0;
+}
+
 /* Operations table */
 static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_info_get = hs_regex_info_get,
@@ -858,6 +1013,10 @@ static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_rule_db_compile_activate = hs_regex_rule_db_compile_activate,
 	.dev_db_import = hs_regex_rule_db_import,
 	.dev_db_export = hs_regex_rule_db_export,
+	.dev_xstats_names_get = hs_regex_xstats_names_get,
+	.dev_xstats_get = hs_regex_xstats_get,
+	.dev_xstats_by_name_get = NULL,
+	.dev_xstats_reset = hs_regex_xstats_reset,
 };
 
 /* Device Lifecycle */
-- 
2.43.0



More information about the dev mailing list