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 <[email protected]>
---
 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

Reply via email to