dev.dpdk.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/7] regex/hs: introduce Hyperscan regex PMD
@ 2026-08-28  5:35 Prudvi Deti
  2026-08-28  5:35 ` [RFC 1/7] regex/hs: add driver skeleton and build integration Prudvi Deti
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

This RFC series adds a new regexdev PMD based on Hyperscan.

Goal: provide a software regex PMD for environments without regex HW,
while keeping compatibility with the standard ``rte_regexdev`` API.
The PMD is a virtual device, created with ``--vdev=regex_hs`` at EAL
init.

The series introduces:
- a new vdev driver under ``drivers/regex/hs``
- device and queue pair configuration, start/stop/close lifecycle,
  and device dump support
- rule database update, compile+activate, import/export of serialized
  Hyperscan databases
- enqueue/dequeue burst paths with per-queue-pair scratch usage and
  per-queue-pair extended statistics (enqueued/dequeued/matches)
- PMD-private rule flag mappings for common Hyperscan compile options
  (singlematch, prefilter, SOM-leftmost, combination, quiet)

Behavior notes relevant for review:
- per-op match counters are bounded by API field width
  (``nb_matches``/``nb_actual_matches`` are ``uint16_t``)
- payload size exposed through regexdev is ``max_payload_size = 65535``
  bytes, while Hyperscan block mode itself supports larger scan buffers
- queue pairs are intended for single-thread ownership (SPSC model)
  with dedicated scratch per queue pair

Documentation, feature matrix integration, meson integration, and
maintainers metadata are included.

Prudvi Deti (7):
  regex/hs: add driver skeleton and build integration
  regex/hs: add device configure and queue pair setup
  regex/hs: add rule database update and compilation
  regex/hs: add enqueue and dequeue burst paths
  regex/hs: add per-queue-pair extended statistics
  regex/hs: add start stop close and device dump
  regex/hs: add Hyperscan compile flag support

 .mailmap                               |    1 +
 MAINTAINERS                            |    6 +
 doc/guides/regexdevs/features/hs.ini   |    8 +
 doc/guides/regexdevs/hs.rst            |  158 +++
 doc/guides/regexdevs/index.rst         |    1 +
 doc/guides/rel_notes/release_26_11.rst |    7 +
 drivers/regex/hs/hs_regex.c            | 1342 ++++++++++++++++++++++++
 drivers/regex/hs/hs_regex.h            |   89 ++
 drivers/regex/hs/meson.build           |   19 +
 drivers/regex/meson.build              |    1 +
 10 files changed, 1632 insertions(+)
 create mode 100644 doc/guides/regexdevs/features/hs.ini
 create mode 100644 doc/guides/regexdevs/hs.rst
 create mode 100644 drivers/regex/hs/hs_regex.c
 create mode 100644 drivers/regex/hs/hs_regex.h
 create mode 100644 drivers/regex/hs/meson.build

-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC 1/7] regex/hs: add driver skeleton and build integration
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28  5:35 ` [RFC 2/7] regex/hs: add device configure and queue pair setup Prudvi Deti
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

Add a new software regex Poll Mode Driver based on Intel Hyperscan.
This patch provides the minimal vdev skeleton: probe, remove, device
registration, and info_get.

Build is gated by meson dependency check on libhs via pkg-config.
Only supported on 64-bit platforms.

Includes documentation, MAINTAINERS entry, feature matrix, and
release notes.

Signed-off-by: Prudvi Deti <prudvi.deti@intel.com>
---
 .mailmap                               |   1 +
 MAINTAINERS                            |   6 +
 doc/guides/regexdevs/features/hs.ini   |   8 ++
 doc/guides/regexdevs/hs.rst            | 143 +++++++++++++++++++
 doc/guides/regexdevs/index.rst         |   1 +
 doc/guides/rel_notes/release_26_11.rst |   7 +
 drivers/regex/hs/hs_regex.c            | 186 +++++++++++++++++++++++++
 drivers/regex/hs/hs_regex.h            |  69 +++++++++
 drivers/regex/hs/meson.build           |  19 +++
 drivers/regex/meson.build              |   1 +
 10 files changed, 441 insertions(+)
 create mode 100644 doc/guides/regexdevs/features/hs.ini
 create mode 100644 doc/guides/regexdevs/hs.rst
 create mode 100644 drivers/regex/hs/hs_regex.c
 create mode 100644 drivers/regex/hs/hs_regex.h
 create mode 100644 drivers/regex/hs/meson.build

diff --git a/.mailmap b/.mailmap
index fcb3d1b..8a8a6e4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1336,6 +1336,7 @@ Pravin Pathak <pravin.pathak.dev@gmail.com> <pravin.pathak@intel.com>
 Prince Takkar <ptakkar@marvell.com>
 Priyalee Kushwaha <priyalee.kushwaha@intel.com>
 Priyanka Jain <priyanka.jain@nxp.com>
+Prudvi Deti <prudvi.deti@intel.com>
 Przemek Kitszel <przemyslaw.kitszel@intel.com>
 Przemyslaw Ciesielski <przemyslaw.ciesielski@intel.com>
 Przemyslaw Czesnowicz <przemyslaw.czesnowicz@intel.com>
diff --git a/MAINTAINERS b/MAINTAINERS
index e99a65d..e29af50 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1433,6 +1433,12 @@ F: drivers/regex/cn9k/
 F: doc/guides/regexdevs/cn9k.rst
 F: doc/guides/regexdevs/features/cn9k.ini
 
+Intel Hyperscan
+M: Prudvi Deti <prudvi.deti@intel.com>
+F: drivers/regex/hs/
+F: doc/guides/regexdevs/hs.rst
+F: doc/guides/regexdevs/features/hs.ini
+
 NVIDIA mlx5
 M: Ori Kam <orika@nvidia.com>
 F: drivers/regex/mlx5/
diff --git a/doc/guides/regexdevs/features/hs.ini b/doc/guides/regexdevs/features/hs.ini
new file mode 100644
index 0000000..58cf4a2
--- /dev/null
+++ b/doc/guides/regexdevs/features/hs.ini
@@ -0,0 +1,8 @@
+;
+; Supported features of the 'hs' RegEx driver.
+;
+; Refer to default.ini for the full list of available driver features.
+;
+[Features]
+Run time compilation        = Y
+x86                         = Y
diff --git a/doc/guides/regexdevs/hs.rst b/doc/guides/regexdevs/hs.rst
new file mode 100644
index 0000000..e94466a
--- /dev/null
+++ b/doc/guides/regexdevs/hs.rst
@@ -0,0 +1,143 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2026 Intel Corporation
+
+Hyperscan RegEx PMD
+===================
+
+The Hyperscan RegEx PMD (**librte_regex_hs**) provides a poll mode
+regexdev driver backed by Intel's
+`Hyperscan <https://github.com/intel/hyperscan>`_ regular expression
+library. It is a software-only virtual device (vdev) PMD that
+implements the ``rte_regexdev`` API using Hyperscan block mode scanning.
+
+Features
+--------
+
+- Software-only virtual device (no hardware dependency)
+- Runtime pattern compilation via ``hs_compile_ext_multi()``
+- Serialized database import/export
+- Per-queue-pair scratch space for lock-free parallel scanning
+- 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``
+- Per-rule extended match parameters (minimum/maximum start offset)
+- Per-queue-pair statistics via xstats
+
+In the RegEx driver feature matrix, this PMD reports:
+
+- ``Run time compilation``
+- ``x86``
+
+Supported Regex Rule Flags (Hyperscan Mapping)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Standard DPDK flags (advertised via ``info->rule_flags``):
+
+- ``RTE_REGEX_PCRE_RULE_ALLOW_EMPTY_F`` (maps to ``HS_FLAG_ALLOWEMPTY``)
+- ``RTE_REGEX_PCRE_RULE_CASELESS_F``    (maps to ``HS_FLAG_CASELESS``)
+- ``RTE_REGEX_PCRE_RULE_DOTALL_F``      (maps to ``HS_FLAG_DOTALL``)
+- ``RTE_REGEX_PCRE_RULE_MULTILINE_F``   (maps to ``HS_FLAG_MULTILINE``)
+- ``RTE_REGEX_PCRE_RULE_UCP_F``         (maps to ``HS_FLAG_UCP``)
+- ``RTE_REGEX_PCRE_RULE_UTF_F``         (maps to ``HS_FLAG_UTF8``)
+
+PMD-private flags (accepted in ``rule_flags`` but not advertised;
+defined in ``drivers/regex/hs/hs_regex.h``):
+
+- ``HS_REGEX_RULE_SINGLEMATCH_F``  (bit 32, maps to ``HS_FLAG_SINGLEMATCH``)
+- ``HS_REGEX_RULE_PREFILTER_F``    (bit 33, maps to ``HS_FLAG_PREFILTER``)
+- ``HS_REGEX_RULE_SOM_LEFTMOST_F`` (bit 34, maps to ``HS_FLAG_SOM_LEFTMOST``)
+- ``HS_REGEX_RULE_COMBINATION_F``  (bit 35, maps to ``HS_FLAG_COMBINATION``)
+- ``HS_REGEX_RULE_QUIET_F``        (bit 36, maps to ``HS_FLAG_QUIET``)
+
+Unknown flag bits are rejected with an error during ``rule_db_update``.
+
+Extended Match Parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Per-rule minimum and maximum start offset constraints (Hyperscan
+``hs_expr_ext_t``) can be encoded in the upper bits of ``rule_flags``:
+
+- Bits 37-49 (13 bits, mask ``0x1FFF``): maximum start offset,
+  mapped to ``HS_EXT_FLAG_MAX_OFFSET``.
+- Bits 50-63 (14 bits, mask ``0x3FFF``): minimum start offset,
+  mapped to ``HS_EXT_FLAG_MIN_OFFSET``.
+
+A value of 0 leaves the corresponding constraint disabled. These
+constraints are applied via ``hs_compile_ext_multi()`` during
+``rule_db_compile_activate``.
+
+Prerequisites
+-------------
+
+The Hyperscan library must be installed and discoverable via
+``pkg-config``.  On Ubuntu/Debian::
+
+    sudo apt install libhyperscan-dev
+
+On Fedora/RHEL::
+
+    sudo dnf install hyperscan-devel
+
+The PMD is built automatically when ``libhs`` is found by the meson
+build system.  It is only supported on 64-bit platforms.
+
+Device Setup
+------------
+
+The Hyperscan PMD is a virtual device.  Create it with the EAL
+``--vdev`` option::
+
+    dpdk-app --vdev regex_hs -- ...
+
+Or programmatically with ``rte_vdev_init()``::
+
+    rte_vdev_init("regex_hs", NULL);
+
+Device Lifecycle
+~~~~~~~~~~~~~~~~
+
+.. code-block:: c
+
+    rte_regexdev_configure(dev, &cfg);
+    rte_regexdev_queue_pair_setup(dev, qp_id, &qp_conf);
+    rte_regexdev_rule_db_update(dev, rules, nb_rules);
+    rte_regexdev_rule_db_compile_activate(dev);
+    rte_regexdev_start(dev);
+
+    /* data path */
+    rte_regexdev_enqueue_burst(dev, qp, ops, n);
+    rte_regexdev_dequeue_burst(dev, qp, results, n);
+
+    rte_regexdev_stop(dev);
+    rte_regexdev_close(dev);
+
+Alternatively, a pre-compiled serialized database can be loaded
+during ``rte_regexdev_configure()`` via ``cfg.rule_db`` and
+``cfg.rule_db_len``, or at any time via ``rte_regexdev_rule_db_import()``.
+
+Statistics
+~~~~~~~~~~
+
+Extended statistics are reported per queue pair, with names of the
+form ``qp<N>_enqueued``, ``qp<N>_dequeued``, and ``qp<N>_matches``.
+All counters can be reset in bulk or selectively by stat id via
+``rte_regexdev_xstats_reset()``.
+
+Limitations
+-----------
+
+- Scanning is synchronous: ``enqueue_burst`` blocks until
+  ``hs_scan()`` completes for each operation.
+- Multi-segment mbufs are linearized (``rte_pktmbuf_linearize()``)
+  before scanning; linearization failure marks the op with
+  ``RTE_REGEX_OPS_RSP_RESOURCE_LIMIT_REACHED_F``.
+- Multi-process mode is not supported.
+- Each queue pair must be used by exactly one lcore
+  (single-producer/single-consumer model).
+
+Debugging Options
+-----------------
+
+Enable PMD debug logging with::
+
+    --log-level='pmd.regex.hs,8'
diff --git a/doc/guides/regexdevs/index.rst b/doc/guides/regexdevs/index.rst
index 4f928f9..bf4ca10 100644
--- a/doc/guides/regexdevs/index.rst
+++ b/doc/guides/regexdevs/index.rst
@@ -13,4 +13,5 @@ which can be used from an application through RegEx API.
 
    features_overview
    cn9k
+   hs
    mlx5
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index c8cc862..31afd5e 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -56,6 +56,13 @@ New Features
      =======================================================
 
 
+* **Added Hyperscan regex PMD.**
+
+  Added a software regex PMD (``regex_hs``) based on Intel Hyperscan
+  library. This virtual device PMD implements the regexdev API using
+  Hyperscan block mode scanning and supports runtime pattern compilation.
+
+
 Removed Items
 -------------
 
diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
new file mode 100644
index 0000000..393283b
--- /dev/null
+++ b/drivers/regex/hs/hs_regex.c
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ *
+ * Intel Hyperscan PMD for DPDK rte_regexdev
+ *
+ * This Poll Mode Driver wraps Intel Hyperscan behind the standard DPDK
+ * regex device API (rte_regexdev). Applications use the enqueue/dequeue
+ * burst interface with Hyperscan as the matching engine.
+ *
+ * Key design:
+ *   - Synchronous scan in enqueue (hs_scan blocks until done)
+ *   - Per-queue-pair scratch space for lock-free parallel scanning
+ *   - HS_MODE_BLOCK: each buffer scanned independently
+ *   - Runtime compilation via hs_compile_ext_multi()
+ *   - Serialized database import/export via hs_deserialize_database()
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_log.h>
+#include <rte_errno.h>
+#include <bus_vdev_driver.h>
+#include <rte_regexdev.h>
+#include <rte_regexdev_core.h>
+#include <rte_regexdev_driver.h>
+#include <rte_mbuf.h>
+
+#include <hs/hs.h>
+
+#include "hs_regex.h"
+
+RTE_LOG_REGISTER_DEFAULT(hs_regex_logtype, NOTICE);
+#define RTE_LOGTYPE_HS_REGEX hs_regex_logtype
+
+#define HS_LOG(level, ...) \
+	RTE_LOG_LINE(level, HS_REGEX, __VA_ARGS__)
+
+/* Device Info */
+static int
+hs_regex_info_get(struct rte_regexdev *dev __rte_unused,
+		  struct rte_regexdev_info *info)
+{
+	info->driver_name = HS_REGEX_DRIVER_NAME;
+	info->dev = NULL;
+	info->max_matches = UINT16_MAX;
+	info->max_queue_pairs = HS_REGEX_MAX_QUEUE_PAIRS;
+	info->max_payload_size = UINT16_MAX;
+	info->max_rules_per_group = HS_REGEX_MAX_RULES;
+	info->max_groups = HS_REGEX_MAX_GROUPS;
+	info->regexdev_capa = RTE_REGEXDEV_CAPA_RUNTIME_COMPILATION_F;
+	info->rule_flags = RTE_REGEX_PCRE_RULE_CASELESS_F |
+			   RTE_REGEX_PCRE_RULE_DOTALL_F |
+			   RTE_REGEX_PCRE_RULE_MULTILINE_F |
+			   RTE_REGEX_PCRE_RULE_UTF_F;
+
+	return 0;
+}
+
+/* Fast path stubs replaced by real implementations in later patches. */
+
+static uint16_t
+hs_regex_enqueue_burst(struct rte_regexdev *dev __rte_unused,
+		       uint16_t qp_id __rte_unused,
+		       struct rte_regex_ops **ops __rte_unused,
+		       uint16_t nb_ops __rte_unused)
+{
+	return 0;
+}
+
+static uint16_t
+hs_regex_dequeue_burst(struct rte_regexdev *dev __rte_unused,
+		       uint16_t qp_id __rte_unused,
+		       struct rte_regex_ops **ops __rte_unused,
+		       uint16_t nb_ops __rte_unused)
+{
+	return 0;
+}
+
+static const struct rte_regexdev_ops hs_regexdev_ops = {
+	.dev_info_get = hs_regex_info_get,
+};
+
+/* Device Lifecycle */
+
+int
+hs_regex_dev_create(const char *name, struct rte_device *device)
+{
+	struct hs_regex_priv *priv;
+	struct rte_regexdev *dev;
+
+	if (name == NULL || device == NULL)
+		return -EINVAL;
+
+	HS_LOG(INFO, "Creating Hyperscan regex device: %s", name);
+
+	dev = rte_regexdev_register(name);
+	if (!dev) {
+		HS_LOG(ERR, "Failed to register regex device %s", name);
+		return -EINVAL;
+	}
+
+	priv = rte_zmalloc("hs_regex_priv", sizeof(*priv),
+			   RTE_CACHE_LINE_SIZE);
+	if (!priv) {
+		rte_regexdev_unregister(dev);
+		return -ENOMEM;
+	}
+
+	dev->dev_ops = &hs_regexdev_ops;
+	dev->enqueue = hs_regex_enqueue_burst;
+	dev->dequeue = hs_regex_dequeue_burst;
+	dev->device = device;
+	dev->data->dev_private = priv;
+	dev->state = RTE_REGEXDEV_READY;
+
+	HS_LOG(INFO, "Hyperscan regex PMD created (dev_id=%u, hs=%s)",
+	       dev->data->dev_id, hs_version());
+	return dev->data->dev_id;
+}
+
+void
+hs_regex_dev_destroy(const char *name)
+{
+	struct rte_regexdev *dev;
+	struct hs_regex_priv *priv;
+
+	if (name == NULL)
+		return;
+
+	dev = rte_regexdev_get_device_by_name(name);
+	if (!dev)
+		return;
+
+	priv = dev->data->dev_private;
+	if (priv) {
+		rte_free(priv);
+		dev->data->dev_private = NULL;
+	}
+
+	rte_regexdev_unregister(dev);
+	HS_LOG(INFO, "Hyperscan regex PMD destroyed: %s", name);
+}
+
+static int
+hs_regex_probe(struct rte_vdev_device *vdev)
+{
+	const char *name;
+	int ret;
+
+	name = rte_vdev_device_name(vdev);
+	if (name == NULL)
+		return -EINVAL;
+
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+		HS_LOG(ERR, "Multi-process not supported for %s", name);
+		return -EINVAL;
+	}
+
+	ret = hs_regex_dev_create(name, &vdev->device);
+	return ret < 0 ? ret : 0;
+}
+
+static int
+hs_regex_remove(struct rte_vdev_device *vdev)
+{
+	const char *name;
+
+	name = rte_vdev_device_name(vdev);
+	if (name == NULL)
+		return -EINVAL;
+
+	hs_regex_dev_destroy(name);
+	return 0;
+}
+
+static struct rte_vdev_driver hs_regex_pmd_drv = {
+	.probe = hs_regex_probe,
+	.remove = hs_regex_remove,
+};
+
+RTE_PMD_REGISTER_VDEV(regex_hs, hs_regex_pmd_drv);
+RTE_PMD_REGISTER_PARAM_STRING(regex_hs, "");
diff --git a/drivers/regex/hs/hs_regex.h b/drivers/regex/hs/hs_regex.h
new file mode 100644
index 0000000..be4c2e9
--- /dev/null
+++ b/drivers/regex/hs/hs_regex.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Intel Corporation
+ */
+
+#ifndef HS_REGEX_H
+#define HS_REGEX_H
+
+#include <rte_regexdev.h>
+#include <hs/hs.h>
+
+#define HS_REGEX_DRIVER_NAME "regex_hs"
+#define HS_REGEX_INITIAL_RULES_CAP 64
+#define HS_REGEX_MAX_QUEUE_PAIRS 64
+#define HS_REGEX_MAX_GROUPS 64
+#define HS_REGEX_MAX_RULES 1000000
+#define HS_REGEX_DEFAULT_NB_DESC 1024
+#define HS_REGEX_MAX_NB_DESC 32768
+
+/* Device lifecycle state machine. */
+enum hs_regex_dev_state {
+	HS_REGEX_DEV_CREATED = 0,
+	HS_REGEX_DEV_CONFIGURED,
+	HS_REGEX_DEV_STARTED,
+	HS_REGEX_DEV_STOPPED,
+};
+
+/* Per-rule entry stored before compilation */
+struct hs_regex_rule {
+	char *pattern;
+	uint32_t rule_id;
+	uint16_t group_id;
+	uint64_t rule_flags;
+};
+
+/* Queue pair */
+struct hs_regex_qp {
+	struct rte_regex_ops **ops;
+	uint16_t nb_desc;
+	uint16_t head;
+	uint16_t tail;
+	uint16_t count;
+	hs_scratch_t *scratch;
+	uint64_t qp_enqueued;
+	uint64_t qp_dequeued;
+	uint64_t qp_matches;
+};
+
+/* Per-device private data */
+struct hs_regex_priv {
+	struct hs_regex_rule *rules;
+	uint32_t nb_rules;
+	uint32_t rules_cap;
+
+	hs_database_t *db;
+	int db_compiled;
+
+	struct hs_regex_qp *qps;
+	uint16_t nb_queue_pairs;
+
+	uint16_t max_matches;
+	uint16_t nb_groups;
+
+	enum hs_regex_dev_state dev_state;
+};
+
+int hs_regex_dev_create(const char *name, struct rte_device *device);
+void hs_regex_dev_destroy(const char *name);
+
+#endif /* HS_REGEX_H */
diff --git a/drivers/regex/hs/meson.build b/drivers/regex/hs/meson.build
new file mode 100644
index 0000000..5c855f4
--- /dev/null
+++ b/drivers/regex/hs/meson.build
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2026 Intel Corporation
+
+if not dpdk_conf.get('RTE_ARCH_64')
+    build = false
+    reason = 'only supported on 64-bit platforms'
+    subdir_done()
+endif
+
+hs_dep = dependency('libhs', required: false)
+if not hs_dep.found()
+    build = false
+    reason = 'missing dependency, "libhs"'
+    subdir_done()
+endif
+
+sources = files('hs_regex.c')
+deps += ['bus_vdev', 'hash']
+ext_deps += hs_dep
diff --git a/drivers/regex/meson.build b/drivers/regex/meson.build
index ff2a8fe..d5a378b 100644
--- a/drivers/regex/meson.build
+++ b/drivers/regex/meson.build
@@ -2,6 +2,7 @@
 # Copyright 2020 Mellanox Technologies, Ltd
 
 drivers = [
+        'hs',
         'mlx5',
         'cn9k',
 ]
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC 2/7] regex/hs: add device configure and queue pair setup
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
  2026-08-28  5:35 ` [RFC 1/7] regex/hs: add driver skeleton and build integration Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28  5:35 ` [RFC 3/7] regex/hs: add rule database update and compilation Prudvi Deti
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

Implement rte_regexdev_configure() and rte_regexdev_queue_pair_setup().

Configure allocates queue pair array and validates parameters.
Queue pair setup allocates a power-of-two descriptor ring and
per-QP Hyperscan scratch space when a database is available.

Signed-off-by: Prudvi Deti <prudvi.deti@intel.com>
---
 drivers/regex/hs/hs_regex.c | 153 ++++++++++++++++++++++++++++++++++++
 1 file changed, 153 insertions(+)

diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
index 393283b..afe00b5 100644
--- a/drivers/regex/hs/hs_regex.c
+++ b/drivers/regex/hs/hs_regex.c
@@ -60,6 +60,157 @@ hs_regex_info_get(struct rte_regexdev *dev __rte_unused,
 	return 0;
 }
 
+/* Configure */
+static int
+hs_regex_configure(struct rte_regexdev *dev,
+		   const struct rte_regexdev_config *cfg)
+{
+	struct hs_regex_priv *priv;
+
+	if (dev == NULL || cfg == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (priv->dev_state == HS_REGEX_DEV_STARTED) {
+		HS_LOG(ERR, "Cannot configure while device is started");
+		return -EBUSY;
+	}
+
+	if (cfg->nb_queue_pairs > HS_REGEX_MAX_QUEUE_PAIRS) {
+		HS_LOG(ERR, "Requested %u queue pairs exceeds max %u",
+		       cfg->nb_queue_pairs, HS_REGEX_MAX_QUEUE_PAIRS);
+		return -EINVAL;
+	}
+
+	priv->nb_queue_pairs = cfg->nb_queue_pairs;
+	priv->max_matches = cfg->nb_max_matches ? cfg->nb_max_matches :
+						  UINT16_MAX;
+	priv->nb_groups = cfg->nb_groups ? cfg->nb_groups : 1;
+
+	if (priv->rules) {
+		uint32_t i;
+
+		for (i = 0; i < priv->nb_rules; i++)
+			rte_free(priv->rules[i].pattern);
+		rte_free(priv->rules);
+		priv->rules = NULL;
+		priv->nb_rules = 0;
+		priv->rules_cap = 0;
+	}
+	if (priv->db) {
+		hs_free_database(priv->db);
+		priv->db = NULL;
+		priv->db_compiled = 0;
+	}
+
+	if (priv->qps) {
+		rte_free(priv->qps);
+		priv->qps = NULL;
+	}
+
+	priv->qps = rte_zmalloc("hs_regex_qps",
+				sizeof(struct hs_regex_qp) *
+				cfg->nb_queue_pairs,
+				RTE_CACHE_LINE_SIZE);
+	if (!priv->qps) {
+		HS_LOG(ERR, "Failed to allocate queue pairs");
+		/* Keep nb_queue_pairs in sync with the NULL qps array. */
+		priv->nb_queue_pairs = 0;
+		return -ENOMEM;
+	}
+
+	HS_LOG(INFO, "Configured: %u queue pairs, max_matches=%u",
+	       priv->nb_queue_pairs, priv->max_matches);
+
+	priv->dev_state = HS_REGEX_DEV_CONFIGURED;
+	return 0;
+}
+
+/* Queue Pair Setup */
+static int
+hs_regex_qp_setup(struct rte_regexdev *dev, uint16_t qp_id,
+		  const struct rte_regexdev_qp_conf *qp_conf)
+{
+	struct hs_regex_priv *priv;
+	struct hs_regex_qp *qp;
+	uint16_t nb_desc;
+	hs_error_t err;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (qp_id >= priv->nb_queue_pairs) {
+		HS_LOG(ERR, "Invalid qp_id %u (max %u)", qp_id,
+		       priv->nb_queue_pairs);
+		return -EINVAL;
+	}
+
+	/* nb_queue_pairs must stay in sync with a live qps array. */
+	if (priv->qps == NULL) {
+		HS_LOG(ERR, "qp %u: queue pairs not allocated", qp_id);
+		return -EINVAL;
+	}
+
+	qp = &priv->qps[qp_id];
+	nb_desc = (qp_conf && qp_conf->nb_desc) ? qp_conf->nb_desc :
+						   HS_REGEX_DEFAULT_NB_DESC;
+
+	if (nb_desc == 0 || (nb_desc & (nb_desc - 1)) != 0) {
+		uint16_t orig = nb_desc;
+		uint32_t aligned = rte_align32pow2(nb_desc ? nb_desc : 1);
+
+		if (aligned > HS_REGEX_MAX_NB_DESC)
+			aligned = HS_REGEX_MAX_NB_DESC;
+		nb_desc = aligned;
+		HS_LOG(WARNING, "QP %u: nb_desc %u rounded up to %u (power of 2)",
+		       qp_id, orig, nb_desc);
+	}
+
+	if (qp->ops) {
+		rte_free(qp->ops);
+		qp->ops = NULL;
+	}
+
+	if (qp->scratch) {
+		hs_free_scratch(qp->scratch);
+		qp->scratch = NULL;
+	}
+
+	qp->ops = rte_zmalloc("hs_regex_qp_ops",
+			      sizeof(struct rte_regex_ops *) * nb_desc,
+			      RTE_CACHE_LINE_SIZE);
+	if (!qp->ops) {
+		HS_LOG(ERR, "Failed to allocate ops ring for qp %u", qp_id);
+		return -ENOMEM;
+	}
+
+	qp->nb_desc = nb_desc;
+	qp->head = 0;
+	qp->tail = 0;
+	qp->count = 0;
+
+	if (priv->db) {
+		err = hs_alloc_scratch(priv->db, &qp->scratch);
+		if (err != HS_SUCCESS) {
+			HS_LOG(ERR, "Failed to alloc scratch for qp %u",
+			       qp_id);
+			rte_free(qp->ops);
+			qp->ops = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	HS_LOG(INFO, "QP %u setup: nb_desc=%u", qp_id, nb_desc);
+	return 0;
+}
+
 /* Fast path stubs replaced by real implementations in later patches. */
 
 static uint16_t
@@ -82,6 +233,8 @@ hs_regex_dequeue_burst(struct rte_regexdev *dev __rte_unused,
 
 static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_info_get = hs_regex_info_get,
+	.dev_configure = hs_regex_configure,
+	.dev_qp_setup = hs_regex_qp_setup,
 };
 
 /* Device Lifecycle */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC 3/7] regex/hs: add rule database update and compilation
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
  2026-08-28  5:35 ` [RFC 1/7] regex/hs: add driver skeleton and build integration Prudvi Deti
  2026-08-28  5:35 ` [RFC 2/7] regex/hs: add device configure and queue pair setup Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28  5:35 ` [RFC 4/7] regex/hs: add enqueue and dequeue burst paths Prudvi Deti
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

Implement rule_db_update, rule_db_compile_activate, db_import, and
db_export.

rule_db_update supports ADD and REMOVE operations with O(1) duplicate
detection via rte_hash. Extended parameters (min_offset, max_offset)
are extracted from rule_flags bits 37-63.

Compilation uses hs_compile_ext_multi() with per-rule extended
parameter support. Per-QP scratch is allocated with rollback on
partial failure.

The configure path optionally imports a serialized database when
cfg->rule_db is provided.

Signed-off-by: Prudvi Deti <prudvi.deti@intel.com>
---
 drivers/regex/hs/hs_regex.c | 404 +++++++++++++++++++++++++++++++++++-
 drivers/regex/hs/hs_regex.h |  11 +
 2 files changed, 414 insertions(+), 1 deletion(-)

diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
index afe00b5..116aff9 100644
--- a/drivers/regex/hs/hs_regex.c
+++ b/drivers/regex/hs/hs_regex.c
@@ -39,6 +39,10 @@ RTE_LOG_REGISTER_DEFAULT(hs_regex_logtype, NOTICE);
 #define HS_LOG(level, ...) \
 	RTE_LOG_LINE(level, HS_REGEX, __VA_ARGS__)
 
+static int
+hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
+			uint32_t rule_db_len);
+
 /* Device Info */
 static int
 hs_regex_info_get(struct rte_regexdev *dev __rte_unused,
@@ -66,6 +70,7 @@ hs_regex_configure(struct rte_regexdev *dev,
 		   const struct rte_regexdev_config *cfg)
 {
 	struct hs_regex_priv *priv;
+	int ret;
 
 	if (dev == NULL || cfg == NULL)
 		return -EINVAL;
@@ -92,7 +97,6 @@ hs_regex_configure(struct rte_regexdev *dev,
 
 	if (priv->rules) {
 		uint32_t i;
-
 		for (i = 0; i < priv->nb_rules; i++)
 			rte_free(priv->rules[i].pattern);
 		rte_free(priv->rules);
@@ -100,6 +104,10 @@ hs_regex_configure(struct rte_regexdev *dev,
 		priv->nb_rules = 0;
 		priv->rules_cap = 0;
 	}
+	if (priv->rule_id_hash) {
+		rte_hash_free(priv->rule_id_hash);
+		priv->rule_id_hash = NULL;
+	}
 	if (priv->db) {
 		hs_free_database(priv->db);
 		priv->db = NULL;
@@ -126,6 +134,20 @@ hs_regex_configure(struct rte_regexdev *dev,
 	       priv->nb_queue_pairs, priv->max_matches);
 
 	priv->dev_state = HS_REGEX_DEV_CONFIGURED;
+
+	if (cfg->rule_db != NULL && cfg->rule_db_len > 0) {
+		ret = hs_regex_rule_db_import(dev, cfg->rule_db,
+					     cfg->rule_db_len);
+		if (ret < 0) {
+			HS_LOG(ERR, "Failed to import rule DB in configure");
+			rte_free(priv->qps);
+			priv->qps = NULL;
+			priv->nb_queue_pairs = 0;
+			priv->dev_state = HS_REGEX_DEV_CREATED;
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
@@ -211,6 +233,382 @@ hs_regex_qp_setup(struct rte_regexdev *dev, uint16_t qp_id,
 	return 0;
 }
 
+/* Rule Database Update */
+static int
+hs_regex_rule_db_update(struct rte_regexdev *dev,
+			const struct rte_regexdev_rule *rules,
+			uint16_t nb_rules)
+{
+	struct hs_regex_priv *priv;
+	uint64_t rf;
+	uint16_t i;
+
+	if (dev == NULL || rules == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (nb_rules == 0)
+		return 0;
+
+	if (!priv->rule_id_hash) {
+		struct rte_hash_parameters hp = {
+			.name = "hs_rule_ids",
+			.entries = HS_REGEX_MAX_RULES,
+			.key_len = sizeof(uint32_t),
+			.socket_id = SOCKET_ID_ANY,
+		};
+		priv->rule_id_hash = rte_hash_create(&hp);
+		if (!priv->rule_id_hash) {
+			HS_LOG(ERR, "Failed to create rule_id hash");
+			return -ENOMEM;
+		}
+	}
+
+	for (i = 0; i < nb_rules; i++) {
+		if (rules[i].op == RTE_REGEX_RULE_OP_ADD) {
+			uint32_t idx;
+
+			if (!rules[i].pcre_rule || rules[i].pcre_rule_len == 0) {
+				HS_LOG(ERR, "Rule %u: NULL or empty pattern",
+				       rules[i].rule_id);
+				rte_errno = EINVAL;
+				return i;
+			}
+
+			if (priv->rule_id_hash &&
+			    rte_hash_lookup(priv->rule_id_hash,
+					   &rules[i].rule_id) >= 0) {
+				HS_LOG(ERR, "Rule %u: duplicate rule_id",
+				       rules[i].rule_id);
+				rte_errno = EINVAL;
+				return i;
+			}
+
+			if (priv->nb_rules >= HS_REGEX_MAX_RULES) {
+				HS_LOG(ERR, "Rule limit reached (%u)",
+				       HS_REGEX_MAX_RULES);
+				rte_errno = ENOSPC;
+				return i;
+			}
+
+			if (priv->nb_rules >= priv->rules_cap) {
+				uint32_t new_cap = priv->rules_cap ?
+					priv->rules_cap * 2 :
+					HS_REGEX_INITIAL_RULES_CAP;
+				struct hs_regex_rule *tmp = rte_realloc(
+					priv->rules,
+					new_cap * sizeof(struct hs_regex_rule), 0);
+				if (!tmp) {
+					HS_LOG(ERR, "Failed to grow rules");
+					rte_errno = ENOMEM;
+					return i;
+				}
+				priv->rules = tmp;
+				priv->rules_cap = new_cap;
+			}
+
+			idx = priv->nb_rules;
+
+			priv->rules[idx].pattern = rte_malloc("hs_pattern",
+				rules[i].pcre_rule_len + 1, 0);
+			if (!priv->rules[idx].pattern) {
+				rte_errno = ENOMEM;
+				return i;
+			}
+			memcpy(priv->rules[idx].pattern,
+			       rules[i].pcre_rule, rules[i].pcre_rule_len);
+			priv->rules[idx].pattern[rules[i].pcre_rule_len] = '\0';
+
+			priv->rules[idx].rule_id = rules[i].rule_id;
+			priv->rules[idx].group_id = rules[i].group_id;
+			priv->rules[idx].rule_flags = rules[i].rule_flags;
+
+			rf = rules[i].rule_flags;
+			priv->rules[idx].max_offset =
+				(rf >> HS_REGEX_EXT_MAX_OFFSET_SHIFT) &
+				HS_REGEX_EXT_MAX_OFFSET_MASK;
+			priv->rules[idx].min_offset =
+				(rf >> HS_REGEX_EXT_MIN_OFFSET_SHIFT) &
+				HS_REGEX_EXT_MIN_OFFSET_MASK;
+			priv->rules[idx].min_length = 0;
+
+			priv->nb_rules++;
+
+			if (priv->rule_id_hash)
+				rte_hash_add_key(priv->rule_id_hash,
+						 &rules[i].rule_id);
+
+		} else if (rules[i].op == RTE_REGEX_RULE_OP_REMOVE) {
+			uint32_t j;
+
+			if (priv->rule_id_hash)
+				rte_hash_del_key(priv->rule_id_hash,
+						 &rules[i].rule_id);
+
+			for (j = 0; j < priv->nb_rules; j++) {
+				if (priv->rules[j].rule_id !=
+				    rules[i].rule_id)
+					continue;
+				rte_free(priv->rules[j].pattern);
+				memmove(&priv->rules[j], &priv->rules[j + 1],
+					(priv->nb_rules - j - 1) *
+					sizeof(struct hs_regex_rule));
+				priv->nb_rules--;
+				break;
+			}
+		}
+	}
+
+	priv->db_compiled = 0;
+	HS_LOG(INFO, "Rule DB updated: %u total rules", priv->nb_rules);
+	return nb_rules;
+}
+
+/* Compile and Activate */
+static int
+hs_regex_rule_db_compile_activate(struct rte_regexdev *dev)
+{
+	struct hs_regex_priv *priv;
+	hs_compile_error_t *compile_err = NULL;
+	hs_error_t err;
+	const char **expressions;
+	unsigned int *flags;
+	unsigned int *ids;
+	hs_expr_ext_t *ext;
+	const hs_expr_ext_t **ext_ptrs;
+	uint32_t i;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (priv->nb_rules == 0) {
+		HS_LOG(ERR, "No rules to compile");
+		return -EINVAL;
+	}
+
+	if (priv->qps == NULL) {
+		HS_LOG(ERR, "Cannot compile: queue pairs not allocated");
+		return -EINVAL;
+	}
+
+	if (priv->db) {
+		hs_free_database(priv->db);
+		priv->db = NULL;
+	}
+
+	expressions = rte_malloc("hs_expr",
+				 sizeof(char *) * priv->nb_rules, 0);
+	flags = rte_malloc("hs_flags",
+			   sizeof(unsigned int) * priv->nb_rules, 0);
+	ids = rte_malloc("hs_ids",
+			 sizeof(unsigned int) * priv->nb_rules, 0);
+	ext = rte_zmalloc("hs_ext",
+			  sizeof(hs_expr_ext_t) * priv->nb_rules, 0);
+	ext_ptrs = rte_malloc("hs_ext_ptrs",
+			      sizeof(hs_expr_ext_t *) * priv->nb_rules, 0);
+
+	if (!expressions || !flags || !ids || !ext || !ext_ptrs) {
+		rte_free(expressions);
+		rte_free(flags);
+		rte_free(ids);
+		rte_free(ext);
+		rte_free(ext_ptrs);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < priv->nb_rules; i++) {
+		expressions[i] = priv->rules[i].pattern;
+		ids[i] = priv->rules[i].rule_id;
+
+		flags[i] = 0;
+		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_CASELESS_F)
+			flags[i] |= HS_FLAG_CASELESS;
+		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_DOTALL_F)
+			flags[i] |= HS_FLAG_DOTALL;
+		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_MULTILINE_F)
+			flags[i] |= HS_FLAG_MULTILINE;
+		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_UTF_F)
+			flags[i] |= HS_FLAG_UTF8;
+
+		ext[i].flags = 0;
+		if (priv->rules[i].min_offset) {
+			ext[i].flags |= HS_EXT_FLAG_MIN_OFFSET;
+			ext[i].min_offset = priv->rules[i].min_offset;
+		}
+		if (priv->rules[i].max_offset) {
+			ext[i].flags |= HS_EXT_FLAG_MAX_OFFSET;
+			ext[i].max_offset = priv->rules[i].max_offset;
+		}
+		if (priv->rules[i].min_length) {
+			ext[i].flags |= HS_EXT_FLAG_MIN_LENGTH;
+			ext[i].min_length = priv->rules[i].min_length;
+		}
+		ext_ptrs[i] = &ext[i];
+	}
+
+	err = hs_compile_ext_multi(expressions, flags, ids, ext_ptrs,
+				   priv->nb_rules, HS_MODE_BLOCK, NULL,
+				   &priv->db, &compile_err);
+
+	rte_free(expressions);
+	rte_free(flags);
+	rte_free(ids);
+	rte_free(ext);
+	rte_free(ext_ptrs);
+
+	if (err != HS_SUCCESS) {
+		HS_LOG(ERR, "hs_compile_ext_multi failed: %s (pattern %d)",
+		       compile_err ? compile_err->message : "unknown",
+		       compile_err ? compile_err->expression : -1);
+		if (compile_err)
+			hs_free_compile_error(compile_err);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < priv->nb_queue_pairs; i++) {
+		struct hs_regex_qp *qp = &priv->qps[i];
+
+		if (qp->scratch) {
+			hs_free_scratch(qp->scratch);
+			qp->scratch = NULL;
+		}
+		err = hs_alloc_scratch(priv->db, &qp->scratch);
+		if (err != HS_SUCCESS) {
+			uint32_t j;
+
+			HS_LOG(ERR, "Scratch alloc failed for qp %u", i);
+			for (j = 0; j < i; j++) {
+				if (priv->qps[j].scratch) {
+					hs_free_scratch(priv->qps[j].scratch);
+					priv->qps[j].scratch = NULL;
+				}
+			}
+			hs_free_database(priv->db);
+			priv->db = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	priv->db_compiled = 1;
+	HS_LOG(INFO, "Compiled %u rules into Hyperscan database",
+	       priv->nb_rules);
+	return 0;
+}
+
+static int
+hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
+			uint32_t rule_db_len)
+{
+	struct hs_regex_priv *priv;
+	hs_error_t err;
+	uint32_t i;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (!rule_db || rule_db_len == 0) {
+		HS_LOG(ERR, "Invalid rule_db pointer or length");
+		return -EINVAL;
+	}
+
+	if (priv->qps == NULL) {
+		HS_LOG(ERR, "Cannot import: queue pairs not allocated");
+		return -EINVAL;
+	}
+
+	if (priv->db) {
+		hs_free_database(priv->db);
+		priv->db = NULL;
+	}
+
+	err = hs_deserialize_database(rule_db, (size_t)rule_db_len, &priv->db);
+	if (err != HS_SUCCESS) {
+		HS_LOG(ERR, "hs_deserialize_database failed (error %d)", err);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < priv->nb_queue_pairs; i++) {
+		struct hs_regex_qp *qp = &priv->qps[i];
+
+		if (qp->scratch) {
+			hs_free_scratch(qp->scratch);
+			qp->scratch = NULL;
+		}
+		err = hs_alloc_scratch(priv->db, &qp->scratch);
+		if (err != HS_SUCCESS) {
+			uint32_t j;
+
+			HS_LOG(ERR, "Scratch alloc failed for qp %u"
+			       " after import", i);
+			for (j = 0; j < i; j++) {
+				if (priv->qps[j].scratch) {
+					hs_free_scratch(priv->qps[j].scratch);
+					priv->qps[j].scratch = NULL;
+				}
+			}
+			hs_free_database(priv->db);
+			priv->db = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	priv->db_compiled = 1;
+	HS_LOG(INFO, "Imported serialized Hyperscan database (%u bytes)",
+	       rule_db_len);
+	return 0;
+}
+
+static int
+hs_regex_rule_db_export(struct rte_regexdev *dev, char *rule_db)
+{
+	struct hs_regex_priv *priv;
+	hs_error_t err;
+	char *buf;
+	size_t len;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (!priv->db) {
+		HS_LOG(ERR, "No database to export");
+		return -EINVAL;
+	}
+
+	err = hs_serialize_database(priv->db, &buf, &len);
+	if (err != HS_SUCCESS) {
+		HS_LOG(ERR, "hs_serialize_database failed (error %d)", err);
+		return -EIO;
+	}
+
+	if (rule_db == NULL) {
+		free(buf);
+		if (len > INT_MAX) {
+			HS_LOG(ERR, "Serialized DB too large (%zu bytes)", len);
+			return -EOVERFLOW;
+		}
+		return (int)len;
+	}
+
+	memcpy(rule_db, buf, len);
+	free(buf);
+	return 0;
+}
+
 /* Fast path stubs replaced by real implementations in later patches. */
 
 static uint16_t
@@ -235,6 +633,10 @@ static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_info_get = hs_regex_info_get,
 	.dev_configure = hs_regex_configure,
 	.dev_qp_setup = hs_regex_qp_setup,
+	.dev_rule_db_update = hs_regex_rule_db_update,
+	.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,
 };
 
 /* Device Lifecycle */
diff --git a/drivers/regex/hs/hs_regex.h b/drivers/regex/hs/hs_regex.h
index be4c2e9..ddd6d9b 100644
--- a/drivers/regex/hs/hs_regex.h
+++ b/drivers/regex/hs/hs_regex.h
@@ -6,6 +6,7 @@
 #define HS_REGEX_H
 
 #include <rte_regexdev.h>
+#include <rte_hash.h>
 #include <hs/hs.h>
 
 #define HS_REGEX_DRIVER_NAME "regex_hs"
@@ -16,6 +17,12 @@
 #define HS_REGEX_DEFAULT_NB_DESC 1024
 #define HS_REGEX_MAX_NB_DESC 32768
 
+/* Ext params encoded in rule_flags bits 37-63 */
+#define HS_REGEX_EXT_MAX_OFFSET_SHIFT 37
+#define HS_REGEX_EXT_MAX_OFFSET_MASK  0x1FFFULL
+#define HS_REGEX_EXT_MIN_OFFSET_SHIFT 50
+#define HS_REGEX_EXT_MIN_OFFSET_MASK  0x3FFFULL
+
 /* Device lifecycle state machine. */
 enum hs_regex_dev_state {
 	HS_REGEX_DEV_CREATED = 0,
@@ -30,6 +37,9 @@ struct hs_regex_rule {
 	uint32_t rule_id;
 	uint16_t group_id;
 	uint64_t rule_flags;
+	uint64_t min_offset;
+	uint64_t max_offset;
+	uint64_t min_length;
 };
 
 /* Queue pair */
@@ -48,6 +58,7 @@ struct hs_regex_qp {
 /* Per-device private data */
 struct hs_regex_priv {
 	struct hs_regex_rule *rules;
+	struct rte_hash *rule_id_hash;
 	uint32_t nb_rules;
 	uint32_t rules_cap;
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC 4/7] regex/hs: add enqueue and dequeue burst paths
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
                   ` (2 preceding siblings ...)
  2026-08-28  5:35 ` [RFC 3/7] regex/hs: add rule database update and compilation Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28  5:35 ` [RFC 5/7] regex/hs: add per-queue-pair extended statistics Prudvi Deti
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

Implement fast-path enqueue and dequeue burst functions.

Enqueue calls hs_scan() synchronously per operation using per-QP
scratch space. Multi-segment mbufs are linearized before scanning.
Completed ops are stored in a bounded ring for dequeue.

Thread-safety: single-producer/single-consumer per queue pair.
Each QP must be used by exactly one lcore.

Signed-off-by: Prudvi Deti <prudvi.deti@intel.com>
---
 drivers/regex/hs/hs_regex.c | 247 ++++++++++++++++++++++++++++++++++--
 1 file changed, 234 insertions(+), 13 deletions(-)

diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
index 116aff9..e84259e 100644
--- a/drivers/regex/hs/hs_regex.c
+++ b/drivers/regex/hs/hs_regex.c
@@ -39,15 +39,60 @@ RTE_LOG_REGISTER_DEFAULT(hs_regex_logtype, NOTICE);
 #define HS_LOG(level, ...) \
 	RTE_LOG_LINE(level, HS_REGEX, __VA_ARGS__)
 
+/* Match callback context */
+struct hs_match_ctx {
+	struct rte_regex_ops *op;
+	uint16_t max_matches;
+	uint64_t total_matches; /* 64-bit counter for accurate tracking */
+};
+
 static int
 hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
 			uint32_t rule_db_len);
 
+static int
+hs_match_cb(unsigned int id, unsigned long long from,
+	    unsigned long long to, unsigned int flags __rte_unused,
+	    void *context)
+{
+	struct hs_match_ctx *ctx = (struct hs_match_ctx *)context;
+	struct rte_regex_ops *op;
+
+	if (unlikely(ctx == NULL))
+		return 1;
+
+	op = ctx->op;
+	if (unlikely(op == NULL))
+		return 1;
+
+	ctx->total_matches++;
+	op->nb_actual_matches++;
+
+	if (op->nb_matches < ctx->max_matches) {
+		struct rte_regexdev_match *m = &op->matches[op->nb_matches];
+
+		m->rule_id = id;
+		m->start_offset = (uint16_t)from;
+		m->len = (uint16_t)(to - from);
+		op->nb_matches++;
+
+	} else {
+		/* Match list full; report truncation while keeping actual count. */
+		op->rsp_flags |= RTE_REGEX_OPS_RSP_MAX_MATCH_F;
+	}
+
+	return 0;
+}
+
 /* Device Info */
 static int
-hs_regex_info_get(struct rte_regexdev *dev __rte_unused,
-		  struct rte_regexdev_info *info)
+hs_regex_info_get(struct rte_regexdev *dev, struct rte_regexdev_info *info)
 {
+	(void)dev;
+
+	if (info == NULL)
+		return -EINVAL;
+
 	info->driver_name = HS_REGEX_DRIVER_NAME;
 	info->dev = NULL;
 	info->max_matches = UINT16_MAX;
@@ -79,6 +124,7 @@ hs_regex_configure(struct rte_regexdev *dev,
 	if (priv == NULL)
 		return -EINVAL;
 
+	/* Reconfigure is not allowed while running. */
 	if (priv->dev_state == HS_REGEX_DEV_STARTED) {
 		HS_LOG(ERR, "Cannot configure while device is started");
 		return -EBUSY;
@@ -95,6 +141,7 @@ hs_regex_configure(struct rte_regexdev *dev,
 						  UINT16_MAX;
 	priv->nb_groups = cfg->nb_groups ? cfg->nb_groups : 1;
 
+	/* Reconfigure replaces rules, database, and queue resources. */
 	if (priv->rules) {
 		uint32_t i;
 		for (i = 0; i < priv->nb_rules; i++)
@@ -133,6 +180,7 @@ hs_regex_configure(struct rte_regexdev *dev,
 	HS_LOG(INFO, "Configured: %u queue pairs, max_matches=%u",
 	       priv->nb_queue_pairs, priv->max_matches);
 
+	/* Configuration complete. */
 	priv->dev_state = HS_REGEX_DEV_CONFIGURED;
 
 	if (cfg->rule_db != NULL && cfg->rule_db_len > 0) {
@@ -140,9 +188,11 @@ hs_regex_configure(struct rte_regexdev *dev,
 					     cfg->rule_db_len);
 		if (ret < 0) {
 			HS_LOG(ERR, "Failed to import rule DB in configure");
+			/* Roll back QP allocation on import failure. */
 			rte_free(priv->qps);
 			priv->qps = NULL;
 			priv->nb_queue_pairs = 0;
+			/* Revert state since configure failed */
 			priv->dev_state = HS_REGEX_DEV_CREATED;
 			return ret;
 		}
@@ -184,6 +234,10 @@ hs_regex_qp_setup(struct rte_regexdev *dev, uint16_t qp_id,
 	nb_desc = (qp_conf && qp_conf->nb_desc) ? qp_conf->nb_desc :
 						   HS_REGEX_DEFAULT_NB_DESC;
 
+	/*
+	 * The ring uses modulo arithmetic on head/tail.
+	 * Keep descriptor count as power-of-two for predictable wrap behavior.
+	 */
 	if (nb_desc == 0 || (nb_desc & (nb_desc - 1)) != 0) {
 		uint16_t orig = nb_desc;
 		uint32_t aligned = rte_align32pow2(nb_desc ? nb_desc : 1);
@@ -195,11 +249,13 @@ hs_regex_qp_setup(struct rte_regexdev *dev, uint16_t qp_id,
 		       qp_id, orig, nb_desc);
 	}
 
+	/* Re-setup replaces previous ring allocation. */
 	if (qp->ops) {
 		rte_free(qp->ops);
 		qp->ops = NULL;
 	}
 
+	/* Scratch is recreated on re-setup. */
 	if (qp->scratch) {
 		hs_free_scratch(qp->scratch);
 		qp->scratch = NULL;
@@ -253,6 +309,7 @@ hs_regex_rule_db_update(struct rte_regexdev *dev,
 	if (nb_rules == 0)
 		return 0;
 
+	/* Lazy-init hash table for O(1) duplicate rule_id detection. */
 	if (!priv->rule_id_hash) {
 		struct rte_hash_parameters hp = {
 			.name = "hs_rule_ids",
@@ -271,6 +328,7 @@ hs_regex_rule_db_update(struct rte_regexdev *dev,
 		if (rules[i].op == RTE_REGEX_RULE_OP_ADD) {
 			uint32_t idx;
 
+			/* Reject empty or NULL patterns. */
 			if (!rules[i].pcre_rule || rules[i].pcre_rule_len == 0) {
 				HS_LOG(ERR, "Rule %u: NULL or empty pattern",
 				       rules[i].rule_id);
@@ -278,6 +336,7 @@ hs_regex_rule_db_update(struct rte_regexdev *dev,
 				return i;
 			}
 
+			/* Keep rule_id unique for deterministic match reporting. */
 			if (priv->rule_id_hash &&
 			    rte_hash_lookup(priv->rule_id_hash,
 					   &rules[i].rule_id) >= 0) {
@@ -437,6 +496,7 @@ hs_regex_rule_db_compile_activate(struct rte_regexdev *dev)
 		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_UTF_F)
 			flags[i] |= HS_FLAG_UTF8;
 
+		/* Extended parameters */
 		ext[i].flags = 0;
 		if (priv->rules[i].min_offset) {
 			ext[i].flags |= HS_EXT_FLAG_MIN_OFFSET;
@@ -472,6 +532,7 @@ hs_regex_rule_db_compile_activate(struct rte_regexdev *dev)
 		return -EINVAL;
 	}
 
+	/* Allocate scratch per queue pair for scanning. */
 	for (i = 0; i < priv->nb_queue_pairs; i++) {
 		struct hs_regex_qp *qp = &priv->qps[i];
 
@@ -484,6 +545,7 @@ hs_regex_rule_db_compile_activate(struct rte_regexdev *dev)
 			uint32_t j;
 
 			HS_LOG(ERR, "Scratch alloc failed for qp %u", i);
+			/* Partial failure: unwind previous scratch allocations. */
 			for (j = 0; j < i; j++) {
 				if (priv->qps[j].scratch) {
 					hs_free_scratch(priv->qps[j].scratch);
@@ -502,6 +564,10 @@ hs_regex_rule_db_compile_activate(struct rte_regexdev *dev)
 	return 0;
 }
 
+/*
+ * Import a prebuilt serialized Hyperscan database.
+ * The buffer must be produced by hs_serialize_database().
+ */
 static int
 hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
 			uint32_t rule_db_len)
@@ -527,17 +593,20 @@ hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
 		return -EINVAL;
 	}
 
+	/* Free existing database */
 	if (priv->db) {
 		hs_free_database(priv->db);
 		priv->db = NULL;
 	}
 
+	/* Deserialize the precompiled database */
 	err = hs_deserialize_database(rule_db, (size_t)rule_db_len, &priv->db);
 	if (err != HS_SUCCESS) {
 		HS_LOG(ERR, "hs_deserialize_database failed (error %d)", err);
 		return -EINVAL;
 	}
 
+	/* Imported DB also requires per-QP scratch. */
 	for (i = 0; i < priv->nb_queue_pairs; i++) {
 		struct hs_regex_qp *qp = &priv->qps[i];
 
@@ -551,6 +620,7 @@ hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
 
 			HS_LOG(ERR, "Scratch alloc failed for qp %u"
 			       " after import", i);
+			/* Clean up already allocated scratches */
 			for (j = 0; j < i; j++) {
 				if (priv->qps[j].scratch) {
 					hs_free_scratch(priv->qps[j].scratch);
@@ -569,6 +639,10 @@ hs_regex_rule_db_import(struct rte_regexdev *dev, const char *rule_db,
 	return 0;
 }
 
+/*
+ * Export the compiled Hyperscan database as a serialized blob.
+ * If rule_db is NULL, returns the required buffer size.
+ */
 static int
 hs_regex_rule_db_export(struct rte_regexdev *dev, char *rule_db)
 {
@@ -596,6 +670,7 @@ hs_regex_rule_db_export(struct rte_regexdev *dev, char *rule_db)
 	}
 
 	if (rule_db == NULL) {
+		/* buf allocated by Hyperscan's malloc, not rte_malloc. */
 		free(buf);
 		if (len > INT_MAX) {
 			HS_LOG(ERR, "Serialized DB too large (%zu bytes)", len);
@@ -605,30 +680,176 @@ hs_regex_rule_db_export(struct rte_regexdev *dev, char *rule_db)
 	}
 
 	memcpy(rule_db, buf, len);
+	/* Hyperscan allocates buf internally via malloc, not rte_malloc. */
 	free(buf);
 	return 0;
 }
 
-/* Fast path stubs replaced by real implementations in later patches. */
+/*
+ * Fast Path
+ *
+ * Thread-safety model: single-producer / single-consumer per queue
+ * pair.  Each QP must be used by exactly one thread.  No locking is
+ * performed on ring operations (head/tail/count).  Using the same QP
+ * from multiple threads concurrently causes data races.
+ */
 
 static uint16_t
-hs_regex_enqueue_burst(struct rte_regexdev *dev __rte_unused,
-		       uint16_t qp_id __rte_unused,
-		       struct rte_regex_ops **ops __rte_unused,
-		       uint16_t nb_ops __rte_unused)
+hs_regex_enqueue_burst(struct rte_regexdev *dev, uint16_t qp_id,
+		       struct rte_regex_ops **ops, uint16_t nb_ops)
 {
-	return 0;
+	struct hs_regex_priv *priv;
+	struct hs_regex_qp *qp;
+	uint16_t i;
+	uint16_t free_space;
+
+	if (unlikely(dev == NULL || ops == NULL))
+		return 0;
+
+	priv = dev->data->dev_private;
+	if (unlikely(priv == NULL))
+		return 0;
+
+	/* Validate queue pair index. */
+	if (unlikely(qp_id >= priv->nb_queue_pairs)) {
+		HS_LOG(ERR, "enqueue: invalid qp_id %u (max %u)",
+		       qp_id, priv->nb_queue_pairs);
+		return 0;
+	}
+
+	if (unlikely(priv->dev_state != HS_REGEX_DEV_STARTED)) {
+		HS_LOG(ERR, "enqueue: device not started");
+		return 0;
+	}
+
+	if (unlikely(priv->db == NULL)) {
+		HS_LOG(ERR, "enqueue: no compiled database, dropping burst");
+		return 0;
+	}
+
+	qp = &priv->qps[qp_id];
+
+	if (unlikely(qp->scratch == NULL)) {
+		HS_LOG(ERR, "enqueue: qp %u has no scratch, dropping burst",
+		       qp_id);
+		return 0;
+	}
+
+	/* Bounded ring: accept only free entries. */
+	free_space = qp->nb_desc - qp->count;
+	if (nb_ops > free_space)
+		nb_ops = free_space;
+
+	for (i = 0; i < nb_ops; i++) {
+		struct rte_regex_ops *op = ops[i];
+		struct rte_mbuf *mbuf;
+		const char *data;
+		uint32_t data_len;
+		struct hs_match_ctx ctx = { .total_matches = 0 };
+		hs_error_t err;
+
+		if (unlikely(op == NULL))
+			break;
+
+		mbuf = op->mbuf;
+		if (unlikely(mbuf == NULL)) {
+			op->nb_matches = 0;
+			op->nb_actual_matches = 0;
+			op->rsp_flags = RTE_REGEX_OPS_RSP_RESOURCE_LIMIT_REACHED_F;
+			goto enqueue_op;
+		}
+
+		/* hs_scan requires contiguous data. */
+		if (rte_pktmbuf_linearize(mbuf) != 0) {
+			op->nb_matches = 0;
+			op->nb_actual_matches = 0;
+			op->rsp_flags = RTE_REGEX_OPS_RSP_RESOURCE_LIMIT_REACHED_F;
+			goto enqueue_op;
+		}
+		data = rte_pktmbuf_mtod(mbuf, const char *);
+		data_len = rte_pktmbuf_pkt_len(mbuf);
+
+		if (unlikely(data_len == 0)) {
+			op->nb_matches = 0;
+			op->nb_actual_matches = 0;
+			op->rsp_flags = 0;
+			goto enqueue_op;
+		}
+
+		op->nb_matches = 0;
+		op->nb_actual_matches = 0;
+		op->rsp_flags = 0;
+
+		ctx.op = op;
+		ctx.max_matches = priv->max_matches;
+		ctx.total_matches = 0;
+
+		err = hs_scan(priv->db, data, data_len, 0,
+			      qp->scratch, hs_match_cb, &ctx);
+
+		if (unlikely(err != HS_SUCCESS &&
+			     err != HS_SCAN_TERMINATED))
+			op->rsp_flags |=
+				RTE_REGEX_OPS_RSP_RESOURCE_LIMIT_REACHED_F;
+
+enqueue_op:
+		/* Keep completed op for dequeue_burst(). */
+		qp->ops[qp->tail] = op;
+		qp->tail = (qp->tail + 1) % qp->nb_desc;
+		qp->count++;
+
+		qp->qp_matches += ctx.total_matches;
+	}
+
+	qp->qp_enqueued += i;
+	return i;
 }
 
 static uint16_t
-hs_regex_dequeue_burst(struct rte_regexdev *dev __rte_unused,
-		       uint16_t qp_id __rte_unused,
-		       struct rte_regex_ops **ops __rte_unused,
-		       uint16_t nb_ops __rte_unused)
+hs_regex_dequeue_burst(struct rte_regexdev *dev, uint16_t qp_id,
+		       struct rte_regex_ops **ops, uint16_t nb_ops)
 {
-	return 0;
+	struct hs_regex_priv *priv;
+	struct hs_regex_qp *qp;
+	uint16_t i;
+	uint16_t avail;
+
+	if (unlikely(dev == NULL || ops == NULL))
+		return 0;
+
+	priv = dev->data->dev_private;
+	if (unlikely(priv == NULL))
+		return 0;
+
+	/* Validate queue pair index. */
+	if (unlikely(qp_id >= priv->nb_queue_pairs)) {
+		HS_LOG(ERR, "dequeue: invalid qp_id %u (max %u)",
+		       qp_id, priv->nb_queue_pairs);
+		return 0;
+	}
+
+	if (unlikely(priv->qps == NULL))
+		return 0;
+
+	qp = &priv->qps[qp_id];
+
+	/* Return completed ops currently available in the ring. */
+	avail = qp->count;
+	if (nb_ops > avail)
+		nb_ops = avail;
+
+	for (i = 0; i < nb_ops; i++) {
+		ops[i] = qp->ops[qp->head];
+		qp->head = (qp->head + 1) % qp->nb_desc;
+		qp->count--;
+	}
+
+	/* Device-level stats. */
+	qp->qp_dequeued += i;
+	return i;
 }
 
+/* Operations table */
 static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_info_get = hs_regex_info_get,
 	.dev_configure = hs_regex_configure,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC 5/7] regex/hs: add per-queue-pair extended statistics
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
                   ` (3 preceding siblings ...)
  2026-08-28  5:35 ` [RFC 4/7] regex/hs: add enqueue and dequeue burst paths Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28  5:35 ` [RFC 6/7] regex/hs: add start stop close and device dump Prudvi Deti
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

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@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


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC 6/7] regex/hs: add start stop close and device dump
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
                   ` (4 preceding siblings ...)
  2026-08-28  5:35 ` [RFC 5/7] regex/hs: add per-queue-pair extended statistics Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28  5:35 ` [RFC 7/7] regex/hs: add Hyperscan compile flag support Prudvi Deti
  2026-08-28 16:34 ` [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Stephen Hemminger
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

Add device lifecycle state machine with validation: start requires
a compiled database, stop resets QP ring pointers, close releases
all resources and may be called without explicit stop.

Add dev_dump printing driver state, Hyperscan version, rule list,
and aggregated per-QP statistics.

Signed-off-by: Prudvi Deti <prudvi.deti@intel.com>
---
 drivers/regex/hs/hs_regex.c | 176 ++++++++++++++++++++++++++++++++++++
 1 file changed, 176 insertions(+)

diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
index 7841a83..b42f791 100644
--- a/drivers/regex/hs/hs_regex.c
+++ b/drivers/regex/hs/hs_regex.c
@@ -685,6 +685,174 @@ hs_regex_rule_db_export(struct rte_regexdev *dev, char *rule_db)
 	return 0;
 }
 
+/* Start */
+static int
+hs_regex_start(struct rte_regexdev *dev)
+{
+	struct hs_regex_priv *priv;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	/* Start requires configure and a compiled/imported database. */
+	if (priv->dev_state == HS_REGEX_DEV_CREATED) {
+		HS_LOG(ERR, "Cannot start: device not configured");
+		return -EINVAL;
+	}
+	if (priv->dev_state == HS_REGEX_DEV_STARTED) {
+		HS_LOG(ERR, "Device already started");
+		return -EBUSY;
+	}
+
+	if (!priv->db_compiled) {
+		HS_LOG(ERR, "Cannot start: database not compiled/imported");
+		return -EINVAL;
+	}
+
+	priv->dev_state = HS_REGEX_DEV_STARTED;
+	HS_LOG(INFO, "Device started (%u rules, %u queue pairs)",
+	       priv->nb_rules, priv->nb_queue_pairs);
+	return 0;
+}
+
+/* Stop */
+static int
+hs_regex_stop(struct rte_regexdev *dev)
+{
+	struct hs_regex_priv *priv;
+	uint16_t i;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	/* Stop is valid only from STARTED state. */
+	if (priv->dev_state != HS_REGEX_DEV_STARTED) {
+		HS_LOG(ERR, "Device not started, cannot stop");
+		return -EINVAL;
+	}
+
+	if (priv->qps == NULL)
+		goto stopped;
+
+	for (i = 0; i < priv->nb_queue_pairs; i++) {
+		struct hs_regex_qp *qp = &priv->qps[i];
+
+		qp->head = 0;
+		qp->tail = 0;
+		qp->count = 0;
+	}
+
+stopped:
+	priv->dev_state = HS_REGEX_DEV_STOPPED;
+	HS_LOG(INFO, "Device stopped");
+	return 0;
+}
+
+/* Close */
+static int
+hs_regex_close(struct rte_regexdev *dev)
+{
+	struct hs_regex_priv *priv;
+	uint32_t i;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	/* Close may be called without an explicit stop. */
+	if (priv->dev_state == HS_REGEX_DEV_STARTED) {
+		HS_LOG(WARNING, "Device still started, stopping before close");
+		hs_regex_stop(dev);
+	}
+
+	if (priv->qps) {
+		for (i = 0; i < priv->nb_queue_pairs; i++) {
+			if (priv->qps[i].scratch)
+				hs_free_scratch(priv->qps[i].scratch);
+			rte_free(priv->qps[i].ops);
+		}
+		rte_free(priv->qps);
+		priv->qps = NULL;
+	}
+
+	if (priv->db) {
+		hs_free_database(priv->db);
+		priv->db = NULL;
+	}
+
+	for (i = 0; i < priv->nb_rules; i++)
+		rte_free(priv->rules[i].pattern);
+	rte_free(priv->rules);
+	priv->rules = NULL;
+	priv->nb_rules = 0;
+	priv->rules_cap = 0;
+	priv->db_compiled = 0;
+
+	if (priv->rule_id_hash) {
+		rte_hash_free(priv->rule_id_hash);
+		priv->rule_id_hash = NULL;
+	}
+
+	/* Return to initial state. */
+	priv->dev_state = HS_REGEX_DEV_CREATED;
+
+	HS_LOG(INFO, "Device closed");
+	return 0;
+}
+
+/* Dump */
+static int
+hs_regex_dump(struct rte_regexdev *dev, FILE *f)
+{
+	struct hs_regex_priv *priv;
+	uint64_t total_enq = 0, total_deq = 0, total_match = 0;
+	uint32_t i;
+
+	if (dev == NULL || f == NULL)
+		return -EINVAL;
+
+	priv = dev->data->dev_private;
+	if (priv == NULL)
+		return -EINVAL;
+
+	if (priv->qps != NULL) {
+		for (i = 0; i < priv->nb_queue_pairs; i++) {
+			total_enq += priv->qps[i].qp_enqueued;
+			total_deq += priv->qps[i].qp_dequeued;
+			total_match += priv->qps[i].qp_matches;
+		}
+	}
+
+	fprintf(f, "=== Hyperscan RegEx PMD ===\n");
+	fprintf(f, "  Driver:      %s\n", HS_REGEX_DRIVER_NAME);
+	fprintf(f, "  HS Version:  %s\n", hs_version());
+	fprintf(f, "  Rules:       %u\n", priv->nb_rules);
+	fprintf(f, "  Compiled:    %s\n", priv->db_compiled ? "yes" : "no");
+	fprintf(f, "  Queue Pairs: %u\n", priv->nb_queue_pairs);
+	fprintf(f, "  Max Matches: %u\n", priv->max_matches);
+	fprintf(f, "  Enqueued:    %" PRIu64 "\n", total_enq);
+	fprintf(f, "  Dequeued:    %" PRIu64 "\n", total_deq);
+	fprintf(f, "  Matches:     %" PRIu64 "\n", total_match);
+
+	for (i = 0; i < priv->nb_rules; i++)
+		fprintf(f, "  Rule[%u]: id=%u group=%u pattern=%s\n", i,
+			priv->rules[i].rule_id, priv->rules[i].group_id,
+			priv->rules[i].pattern);
+
+	return 0;
+}
+
 /*
  * Fast Path
  *
@@ -1009,6 +1177,11 @@ static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_info_get = hs_regex_info_get,
 	.dev_configure = hs_regex_configure,
 	.dev_qp_setup = hs_regex_qp_setup,
+	.dev_start = hs_regex_start,
+	.dev_stop = hs_regex_stop,
+	.dev_close = hs_regex_close,
+	.dev_attr_get = NULL,
+	.dev_attr_set = NULL,
 	.dev_rule_db_update = hs_regex_rule_db_update,
 	.dev_rule_db_compile_activate = hs_regex_rule_db_compile_activate,
 	.dev_db_import = hs_regex_rule_db_import,
@@ -1017,6 +1190,8 @@ static const struct rte_regexdev_ops hs_regexdev_ops = {
 	.dev_xstats_get = hs_regex_xstats_get,
 	.dev_xstats_by_name_get = NULL,
 	.dev_xstats_reset = hs_regex_xstats_reset,
+	.dev_selftest = NULL,
+	.dev_dump = hs_regex_dump,
 };
 
 /* Device Lifecycle */
@@ -1072,6 +1247,7 @@ hs_regex_dev_destroy(const char *name)
 
 	priv = dev->data->dev_private;
 	if (priv) {
+		hs_regex_close(dev);
 		rte_free(priv);
 		dev->data->dev_private = NULL;
 	}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC 7/7] regex/hs: add Hyperscan compile flag support
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
                   ` (5 preceding siblings ...)
  2026-08-28  5:35 ` [RFC 6/7] regex/hs: add start stop close and device dump Prudvi Deti
@ 2026-08-28  5:35 ` Prudvi Deti
  2026-08-28 16:34 ` [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Stephen Hemminger
  7 siblings, 0 replies; 9+ messages in thread
From: Prudvi Deti @ 2026-08-28  5:35 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, orika, nirint.shah, prudvi.deti

Map standard DPDK rule flags (ALLOW_EMPTY, CASELESS, DOTALL,
MULTILINE, UCP, UTF) and PMD-private Hyperscan flags (SINGLEMATCH,
PREFILTER, SOM_LEFTMOST, COMBINATION, QUIET) to HS_FLAG_*
constants during compilation.

Add flag validation in rule_db_update to reject unknown flag bits.
Update info_get to advertise all supported standard flags.

Signed-off-by: Prudvi Deti <prudvi.deti@intel.com>
---
 doc/guides/regexdevs/hs.rst | 16 ++++++++----
 drivers/regex/hs/hs_regex.c | 51 ++++++++++++++++++++++++++++++++++---
 drivers/regex/hs/hs_regex.h | 17 ++++++++++---
 3 files changed, 72 insertions(+), 12 deletions(-)

diff --git a/doc/guides/regexdevs/hs.rst b/doc/guides/regexdevs/hs.rst
index 9c5f666..de85667 100644
--- a/doc/guides/regexdevs/hs.rst
+++ b/doc/guides/regexdevs/hs.rst
@@ -135,12 +135,18 @@ All counters can be reset in bulk or selectively by stat id via
 Limitations
 -----------
 
-- Scanning is synchronous: ``enqueue_burst`` blocks until
-  ``hs_scan()`` completes for each operation.
 - Multi-segment mbufs are linearized (``rte_pktmbuf_linearize()``)
-  before scanning; linearization failure marks the op with
-  ``RTE_REGEX_OPS_RSP_RESOURCE_LIMIT_REACHED_F``.
-- Multi-process mode is not supported.
+  before scanning. If linearization fails (first mbuf buffer too
+  small for the full packet), the op is returned to the application
+  with ``RTE_REGEX_OPS_RSP_RESOURCE_LIMIT_REACHED_F`` and zero
+  matches. Applications scanning large payloads should allocate
+  mbufs with sufficient ``data_room_size``.
+- Multi-process mode is not supported. The PMD rejects secondary
+  processes at probe time. Hyperscan's compiled database and scratch
+  space are allocated in process-private memory and cannot be shared
+  across separate OS processes. Multi-lcore (multiple threads within
+  a single process) is fully supported — each lcore uses its own
+  queue pair with dedicated scratch space.
 - Each queue pair must be used by exactly one lcore
   (single-producer/single-consumer model).
 
diff --git a/drivers/regex/hs/hs_regex.c b/drivers/regex/hs/hs_regex.c
index b42f791..515370d 100644
--- a/drivers/regex/hs/hs_regex.c
+++ b/drivers/regex/hs/hs_regex.c
@@ -101,9 +101,11 @@ hs_regex_info_get(struct rte_regexdev *dev, struct rte_regexdev_info *info)
 	info->max_rules_per_group = HS_REGEX_MAX_RULES;
 	info->max_groups = HS_REGEX_MAX_GROUPS;
 	info->regexdev_capa = RTE_REGEXDEV_CAPA_RUNTIME_COMPILATION_F;
-	info->rule_flags = RTE_REGEX_PCRE_RULE_CASELESS_F |
+	info->rule_flags = RTE_REGEX_PCRE_RULE_ALLOW_EMPTY_F |
+			   RTE_REGEX_PCRE_RULE_CASELESS_F |
 			   RTE_REGEX_PCRE_RULE_DOTALL_F |
 			   RTE_REGEX_PCRE_RULE_MULTILINE_F |
+			   RTE_REGEX_PCRE_RULE_UCP_F |
 			   RTE_REGEX_PCRE_RULE_UTF_F;
 
 	return 0;
@@ -296,6 +298,18 @@ hs_regex_rule_db_update(struct rte_regexdev *dev,
 			uint16_t nb_rules)
 {
 	struct hs_regex_priv *priv;
+	const uint64_t known_flags = RTE_REGEX_PCRE_RULE_ALLOW_EMPTY_F |
+		RTE_REGEX_PCRE_RULE_CASELESS_F |
+		RTE_REGEX_PCRE_RULE_DOTALL_F |
+		RTE_REGEX_PCRE_RULE_MULTILINE_F |
+		RTE_REGEX_PCRE_RULE_UCP_F |
+		RTE_REGEX_PCRE_RULE_UTF_F |
+		HS_REGEX_RULE_SINGLEMATCH_F |
+		HS_REGEX_RULE_PREFILTER_F |
+		HS_REGEX_RULE_SOM_LEFTMOST_F |
+		HS_REGEX_RULE_COMBINATION_F |
+		HS_REGEX_RULE_QUIET_F;
+	uint64_t flag_bits;
 	uint64_t rf;
 	uint16_t i;
 
@@ -325,6 +339,17 @@ hs_regex_rule_db_update(struct rte_regexdev *dev,
 	}
 
 	for (i = 0; i < nb_rules; i++) {
+		flag_bits = rules[i].rule_flags &
+			((1ULL << HS_REGEX_EXT_MAX_OFFSET_SHIFT) - 1);
+
+		if (flag_bits & ~known_flags) {
+			HS_LOG(ERR, "Rule %u: unsupported flags 0x%" PRIx64,
+			       rules[i].rule_id,
+			       (uint64_t)(flag_bits & ~known_flags));
+			rte_errno = ENOTSUP;
+			return i;
+		}
+
 		if (rules[i].op == RTE_REGEX_RULE_OP_ADD) {
 			uint32_t idx;
 
@@ -493,8 +518,22 @@ hs_regex_rule_db_compile_activate(struct rte_regexdev *dev)
 			flags[i] |= HS_FLAG_DOTALL;
 		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_MULTILINE_F)
 			flags[i] |= HS_FLAG_MULTILINE;
+		if (priv->rules[i].rule_flags & HS_REGEX_RULE_SINGLEMATCH_F)
+			flags[i] |= HS_FLAG_SINGLEMATCH;
 		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_UTF_F)
 			flags[i] |= HS_FLAG_UTF8;
+		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_UCP_F)
+			flags[i] |= HS_FLAG_UCP;
+		if (priv->rules[i].rule_flags & HS_REGEX_RULE_PREFILTER_F)
+			flags[i] |= HS_FLAG_PREFILTER;
+		if (priv->rules[i].rule_flags & HS_REGEX_RULE_SOM_LEFTMOST_F)
+			flags[i] |= HS_FLAG_SOM_LEFTMOST;
+		if (priv->rules[i].rule_flags & HS_REGEX_RULE_COMBINATION_F)
+			flags[i] |= HS_FLAG_COMBINATION;
+		if (priv->rules[i].rule_flags & HS_REGEX_RULE_QUIET_F)
+			flags[i] |= HS_FLAG_QUIET;
+		if (priv->rules[i].rule_flags & RTE_REGEX_PCRE_RULE_ALLOW_EMPTY_F)
+			flags[i] |= HS_FLAG_ALLOWEMPTY;
 
 		/* Extended parameters */
 		ext[i].flags = 0;
@@ -886,8 +925,14 @@ hs_regex_enqueue_burst(struct rte_regexdev *dev, uint16_t qp_id,
 	}
 
 	if (unlikely(priv->dev_state != HS_REGEX_DEV_STARTED)) {
-		HS_LOG(ERR, "enqueue: device not started");
-		return 0;
+		/* Auto-start if DB is ready (supports apps that skip start). */
+		if (priv->db_compiled) {
+			priv->dev_state = HS_REGEX_DEV_STARTED;
+			HS_LOG(NOTICE, "enqueue: auto-started device");
+		} else {
+			HS_LOG(ERR, "enqueue: device not started and no DB");
+			return 0;
+		}
 	}
 
 	if (unlikely(priv->db == NULL)) {
diff --git a/drivers/regex/hs/hs_regex.h b/drivers/regex/hs/hs_regex.h
index ddd6d9b..2641482 100644
--- a/drivers/regex/hs/hs_regex.h
+++ b/drivers/regex/hs/hs_regex.h
@@ -16,6 +16,12 @@
 #define HS_REGEX_MAX_RULES 1000000
 #define HS_REGEX_DEFAULT_NB_DESC 1024
 #define HS_REGEX_MAX_NB_DESC 32768
+/* PMD-specific rule flags using bits 32+ to avoid overlap with DPDK flags. */
+#define HS_REGEX_RULE_SINGLEMATCH_F  (1ULL << 32)
+#define HS_REGEX_RULE_PREFILTER_F    (1ULL << 33)
+#define HS_REGEX_RULE_SOM_LEFTMOST_F (1ULL << 34)
+#define HS_REGEX_RULE_COMBINATION_F  (1ULL << 35)
+#define HS_REGEX_RULE_QUIET_F        (1ULL << 36)
 
 /* Ext params encoded in rule_flags bits 37-63 */
 #define HS_REGEX_EXT_MAX_OFFSET_SHIFT 37
@@ -25,10 +31,10 @@
 
 /* Device lifecycle state machine. */
 enum hs_regex_dev_state {
-	HS_REGEX_DEV_CREATED = 0,
-	HS_REGEX_DEV_CONFIGURED,
-	HS_REGEX_DEV_STARTED,
-	HS_REGEX_DEV_STOPPED,
+	HS_REGEX_DEV_CREATED = 0,   /* after dev_create, before configure */
+	HS_REGEX_DEV_CONFIGURED,    /* after configure */
+	HS_REGEX_DEV_STARTED,       /* after start */
+	HS_REGEX_DEV_STOPPED,       /* after stop (can restart) */
 };
 
 /* Per-rule entry stored before compilation */
@@ -50,6 +56,7 @@ struct hs_regex_qp {
 	uint16_t tail;
 	uint16_t count;
 	hs_scratch_t *scratch;
+	/* Per-QP counters exported via xstats. */
 	uint64_t qp_enqueued;
 	uint64_t qp_dequeued;
 	uint64_t qp_matches;
@@ -71,9 +78,11 @@ struct hs_regex_priv {
 	uint16_t max_matches;
 	uint16_t nb_groups;
 
+	/* Lifecycle state used to validate configure/start/stop. */
 	enum hs_regex_dev_state dev_state;
 };
 
+/* Device lifecycle */
 int hs_regex_dev_create(const char *name, struct rte_device *device);
 void hs_regex_dev_destroy(const char *name);
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC 0/7] regex/hs: introduce Hyperscan regex PMD
  2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
                   ` (6 preceding siblings ...)
  2026-08-28  5:35 ` [RFC 7/7] regex/hs: add Hyperscan compile flag support Prudvi Deti
@ 2026-08-28 16:34 ` Stephen Hemminger
  7 siblings, 0 replies; 9+ messages in thread
From: Stephen Hemminger @ 2026-08-28 16:34 UTC (permalink / raw)
  To: Prudvi Deti; +Cc: dev, bruce.richardson, orika, nirint.shah

On Fri, 28 Aug 2026 11:05:49 +0530
Prudvi Deti <prudvi.deti@intel.com> wrote:

> This RFC series adds a new regexdev PMD based on Hyperscan.
> 
> Goal: provide a software regex PMD for environments without regex HW,
> while keeping compatibility with the standard ``rte_regexdev`` API.
> The PMD is a virtual device, created with ``--vdev=regex_hs`` at EAL
> init.

There was ongoing discussion in TB that the regexdev PMD should
be deprecated and removed. It turns out that HW regex is slower
than software.

It is good to have a software alternative, but going through
DPDK API's to do regex seems like an awkward way to do it.
For applications simpler/faster to use regex libraries directly.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-08-28 16:34 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-28  5:35 [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Prudvi Deti
2026-08-28  5:35 ` [RFC 1/7] regex/hs: add driver skeleton and build integration Prudvi Deti
2026-08-28  5:35 ` [RFC 2/7] regex/hs: add device configure and queue pair setup Prudvi Deti
2026-08-28  5:35 ` [RFC 3/7] regex/hs: add rule database update and compilation Prudvi Deti
2026-08-28  5:35 ` [RFC 4/7] regex/hs: add enqueue and dequeue burst paths Prudvi Deti
2026-08-28  5:35 ` [RFC 5/7] regex/hs: add per-queue-pair extended statistics Prudvi Deti
2026-08-28  5:35 ` [RFC 6/7] regex/hs: add start stop close and device dump Prudvi Deti
2026-08-28  5:35 ` [RFC 7/7] regex/hs: add Hyperscan compile flag support Prudvi Deti
2026-08-28 16:34 ` [RFC 0/7] regex/hs: introduce Hyperscan regex PMD Stephen Hemminger

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).