From a93a030ea0c817a229ce92df315a3cd72dcee6d5 Mon Sep 17 00:00:00 2001
From: Dengdui Huang <huangdengdui@huawei.com>
Date: Wed, 30 Oct 2024 17:29:19 +0800
Subject: [PATCH] net/hns3: support flow rule priority

[ upstream commit ac72aae60f71b8716f65d1e2daf531a5ca38c998 ]

The hardware determines the priority of the flow rule based on the position
of the rule in the hardware flow director table. Lower index denotes higher
priority (it means when a packet matches multiple indexes, the smaller
index wins). This patch implements flow priority based on this feature.

To avoid affecting the current use, use runtime config 'fdir_index_config'
to select flow director index strategy. The options are as follows:
1. hash: Default config, the rule priority level cannot be set.
   The driver generates a flow index based on the hash of the rte_flow key.
2. priority: The flow rule priority feature is supported.
   The driver uses the rte_flow priority field as the flow director index.

Signed-off-by: Dengdui Huang <huangdengdui@huawei.com>
Signed-off-by: Jie Hai <haijie1@huawei.com>
---
 doc/guides/nics/hns3.rst       | 12 +++++++
 drivers/net/hns3/hns3_common.c | 25 ++++++++++++++
 drivers/net/hns3/hns3_common.h |  1 +
 drivers/net/hns3/hns3_dump.c   |  2 ++
 drivers/net/hns3/hns3_ethdev.c |  3 +-
 drivers/net/hns3/hns3_fdir.c   | 62 ++++++++++++++++++++++++----------
 drivers/net/hns3/hns3_fdir.h   | 10 ++++++
 drivers/net/hns3/hns3_flow.c   | 45 +++++++++++++++++++++---
 8 files changed, 136 insertions(+), 24 deletions(-)

diff --git a/doc/guides/nics/hns3.rst b/doc/guides/nics/hns3.rst
index bdc10da..b8e79c1 100644
--- a/doc/guides/nics/hns3.rst
+++ b/doc/guides/nics/hns3.rst
@@ -193,6 +193,15 @@ Runtime Configuration
   ``+outvlan-sctptag``: means disable sctp tag tuple, and enable outer vlan tuple.
   ``+outvlan-tunvni``: means disable tunnel vni tuple, and enable outer vlan tuple.
 
+- ``fdir_index_config`` (default ``hash``)
+
+  Used to select flow director index strategy, the flow director index is the index
+  position in the hardware flow director table. Lower index denotes higher priority
+  (it means when a packet matches multiple indexes, the smaller index wins).
+  Current supported options are as follows:
+  ``hash``: The driver generates a flow index based on the hash of the rte_flow key.
+  ``priority``: the driver uses the rte_flow priority field as the flow director index.
+
 Driver compilation and testing
 ------------------------------
 
@@ -322,6 +331,9 @@ Generic flow API
   configuration for hardware which will affect other rules.
   The rule just setting input tuple is completely independent.
 
+  In addition, if the rule priority level is set, no error is reported,
+  but the rule priority level does not take effect.
+
   Run ``testpmd``:
 
   .. code-block:: console
diff --git a/drivers/net/hns3/hns3_common.c b/drivers/net/hns3/hns3_common.c
index 99a1d59..25a4521 100644
--- a/drivers/net/hns3/hns3_common.c
+++ b/drivers/net/hns3/hns3_common.c
@@ -290,6 +290,27 @@ hns3_parse_fdir_tuple_config(const char *key, const char *value, void *args)
 	return 0;
 }
 
+static int
+hns3_parse_fdir_index_config(const char *key, const char *value, void *args)
+{
+	enum hns3_fdir_index_config cfg;
+
+	if (strcmp(value, "hash") == 0) {
+		cfg  = HNS3_FDIR_INDEX_CONFIG_HASH;
+	} else if (strcmp(value, "priority") == 0) {
+		cfg  = HNS3_FDIR_INDEX_CONFIG_PRIORITY;
+	} else {
+		PMD_INIT_LOG(WARNING, "invalid value:\"%s\" for key:\"%s\", "
+			"value must be 'hash' or 'priority'",
+			value, key);
+		return -1;
+	}
+
+	*(enum hns3_fdir_index_config *)args = cfg;
+
+	return 0;
+}
+
 void
 hns3_parse_devargs(struct rte_eth_dev *dev)
 {
@@ -333,6 +354,10 @@ hns3_parse_devargs(struct rte_eth_dev *dev)
 					 HNS3_DEVARG_FDIR_TUPLE_CONFIG,
 					 &hns3_parse_fdir_tuple_config,
 					 &hns->pf.fdir.tuple_cfg);
+		(void)rte_kvargs_process(kvlist,
+					 HNS3_DEVARG_FDIR_INDEX_CONFIG,
+					 &hns3_parse_fdir_index_config,
+					 &hns->pf.fdir.index_cfg);
 	}
 
 	rte_kvargs_free(kvlist);
diff --git a/drivers/net/hns3/hns3_common.h b/drivers/net/hns3/hns3_common.h
index ca90936..7b3f96b 100644
--- a/drivers/net/hns3/hns3_common.h
+++ b/drivers/net/hns3/hns3_common.h
@@ -29,6 +29,7 @@ enum {
 
 #define HNS3_DEVARG_FDIR_VLAN_MATCH_MODE	"fdir_vlan_match_mode"
 #define HNS3_DEVARG_FDIR_TUPLE_CONFIG	"fdir_tuple_config"
+#define HNS3_DEVARG_FDIR_INDEX_CONFIG	"fdir_index_config"
 
 #define MSEC_PER_SEC              1000L
 #define USEC_PER_MSEC             1000L
diff --git a/drivers/net/hns3/hns3_dump.c b/drivers/net/hns3/hns3_dump.c
index fbe3716..dcdfcf4 100644
--- a/drivers/net/hns3/hns3_dump.c
+++ b/drivers/net/hns3/hns3_dump.c
@@ -169,6 +169,7 @@ hns3_get_fdir_basic_info(FILE *file, struct hns3_pf *pf)
 		"\t  -- mode=%u max_key_len=%u rule_num:%u cnt_num:%u\n"
 		"\t  -- key_sel=%u tuple_active=0x%x meta_data_active=0x%x\n"
 		"\t  -- ipv6_word_en: in_s=%u in_d=%u out_s=%u out_d=%u\n"
+		"\t  -- index_cfg: %s\n"
 		"\t  -- tuple_config: %s\n"
 		"\t  -- active_tuples:\n",
 		fdcfg->fd_mode, fdcfg->max_key_length,
@@ -181,6 +182,7 @@ hns3_get_fdir_basic_info(FILE *file, struct hns3_pf *pf)
 		fdcfg->key_cfg[HNS3_FD_STAGE_1].inner_dipv6_word_en,
 		fdcfg->key_cfg[HNS3_FD_STAGE_1].outer_sipv6_word_en,
 		fdcfg->key_cfg[HNS3_FD_STAGE_1].outer_dipv6_word_en,
+		hns3_fdir_index_config_name(pf->fdir.index_cfg),
 		hns3_tuple_config_name(pf->fdir.tuple_cfg));
 
 	for (i = 0; i < MAX_TUPLE; i++) {
diff --git a/drivers/net/hns3/hns3_ethdev.c b/drivers/net/hns3/hns3_ethdev.c
index 30b7aaa..12bb834 100644
--- a/drivers/net/hns3/hns3_ethdev.c
+++ b/drivers/net/hns3/hns3_ethdev.c
@@ -6672,7 +6672,8 @@ RTE_PMD_REGISTER_PARAM_STRING(net_hns3,
 		HNS3_DEVARG_FDIR_VLAN_MATCH_MODE "=strict|nostrict "
 		HNS3_DEVARG_FDIR_TUPLE_CONFIG "=+outvlan-insmac|+outvlan-indmac|"
 					      "+outvlan-insip|+outvlan-indip"
-					      "+outvlan-sctptag|+outvlan-tunvni ");
+					      "+outvlan-sctptag|+outvlan-tunvni "
+		HNS3_DEVARG_FDIR_INDEX_CONFIG "=hash|priority ");
 RTE_LOG_REGISTER_SUFFIX(hns3_logtype_init, init, NOTICE);
 RTE_LOG_REGISTER_SUFFIX(hns3_logtype_driver, driver, NOTICE);
 #ifdef RTE_ETHDEV_DEBUG_RX
diff --git a/drivers/net/hns3/hns3_fdir.c b/drivers/net/hns3/hns3_fdir.c
index 389dec2..1e9932b 100644
--- a/drivers/net/hns3/hns3_fdir.c
+++ b/drivers/net/hns3/hns3_fdir.c
@@ -981,39 +981,44 @@ static int hns3_insert_fdir_filter(struct hns3_hw *hw,
 {
 	struct hns3_fdir_key_conf *key;
 	hash_sig_t sig;
-	int ret;
+	int index;
 
 	key = &fdir_filter->fdir_conf.key_conf;
 	sig = rte_hash_crc(key, sizeof(*key), 0);
-	ret = rte_hash_add_key_with_hash(fdir_info->hash_handle, key, sig);
-	if (ret < 0) {
-		hns3_err(hw, "Hash table full? err:%d!", ret);
-		return ret;
+	index = rte_hash_add_key_with_hash(fdir_info->hash_handle, key, sig);
+	if (index < 0) {
+		hns3_err(hw, "Hash table full? err:%d!", index);
+		return index;
 	}
 
-	fdir_info->hash_map[ret] = fdir_filter;
+	if (fdir_info->index_cfg == HNS3_FDIR_INDEX_CONFIG_PRIORITY)
+		index = fdir_filter->fdir_conf.location;
+
+	fdir_info->hash_map[index] = fdir_filter;
 	TAILQ_INSERT_TAIL(&fdir_info->fdir_list, fdir_filter, entries);
 
-	return ret;
+	return index;
 }
 
 static int hns3_remove_fdir_filter(struct hns3_hw *hw,
 				   struct hns3_fdir_info *fdir_info,
-				   struct hns3_fdir_key_conf *key)
+				   struct hns3_fdir_rule *rule)
 {
 	struct hns3_fdir_rule_ele *fdir_filter;
 	hash_sig_t sig;
-	int ret;
+	int index;
 
-	sig = rte_hash_crc(key, sizeof(*key), 0);
-	ret = rte_hash_del_key_with_hash(fdir_info->hash_handle, key, sig);
-	if (ret < 0) {
-		hns3_err(hw, "Delete hash key fail ret=%d", ret);
-		return ret;
+	sig = rte_hash_crc(&rule->key_conf, sizeof(rule->key_conf), 0);
+	index = rte_hash_del_key_with_hash(fdir_info->hash_handle, &rule->key_conf, sig);
+	if (index < 0) {
+		hns3_err(hw, "Delete hash key fail ret=%d", index);
+		return index;
 	}
 
-	fdir_filter = fdir_info->hash_map[ret];
-	fdir_info->hash_map[ret] = NULL;
+	if (fdir_info->index_cfg == HNS3_FDIR_INDEX_CONFIG_PRIORITY)
+		index = rule->location;
+	fdir_filter = fdir_info->hash_map[index];
+	fdir_info->hash_map[index] = NULL;
 	TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries);
 
 	rte_free(fdir_filter);
@@ -1042,7 +1047,7 @@ int hns3_fdir_filter_program(struct hns3_adapter *hns,
 				 rule->key_conf.spec.src_port,
 				 rule->key_conf.spec.dst_port, ret);
 		else
-			ret = hns3_remove_fdir_filter(hw, fdir_info, &rule->key_conf);
+			ret = hns3_remove_fdir_filter(hw, fdir_info, rule);
 
 		return ret;
 	}
@@ -1080,7 +1085,7 @@ int hns3_fdir_filter_program(struct hns3_adapter *hns,
 			 rule->key_conf.spec.dst_ip[IP_ADDR_KEY_ID],
 			 rule->key_conf.spec.src_port,
 			 rule->key_conf.spec.dst_port, ret);
-		(void)hns3_remove_fdir_filter(hw, fdir_info, &rule->key_conf);
+		(void)hns3_remove_fdir_filter(hw, fdir_info, rule);
 	}
 
 	return ret;
@@ -1231,3 +1236,24 @@ hns3_tuple_config_name(enum hns3_fdir_tuple_config tuple_cfg)
 
 	return "unknown";
 }
+
+static struct {
+	enum hns3_fdir_index_config cfg;
+	const char *name;
+} index_cfg_map[] = {
+	{ HNS3_FDIR_INDEX_CONFIG_HASH, "hash"},
+	{ HNS3_FDIR_INDEX_CONFIG_PRIORITY, "priority"},
+};
+
+const char *
+hns3_fdir_index_config_name(enum hns3_fdir_index_config cfg)
+{
+	uint32_t i;
+
+	for (i = 0; i < RTE_DIM(index_cfg_map); i++) {
+		if (cfg == index_cfg_map[i].cfg)
+			return index_cfg_map[i].name;
+	}
+
+	return "unknown";
+}
diff --git a/drivers/net/hns3/hns3_fdir.h b/drivers/net/hns3/hns3_fdir.h
index 2d0c9bf..5ba7b5b 100644
--- a/drivers/net/hns3/hns3_fdir.h
+++ b/drivers/net/hns3/hns3_fdir.h
@@ -228,6 +228,14 @@ enum hns3_fdir_tuple_config {
 	HNS3_FDIR_TUPLE_CONFIG_BUTT
 };
 
+enum hns3_fdir_index_config {
+	/* Generate the hardware flow director index based on rte_hash (Default) */
+	HNS3_FDIR_INDEX_CONFIG_HASH,
+
+	/* Use the rte_flow priority field as the hardware flow director index. */
+	HNS3_FDIR_INDEX_CONFIG_PRIORITY
+};
+
 /*
  *  A structure used to define fields of a FDIR related info.
  */
@@ -238,6 +246,7 @@ struct hns3_fdir_info {
 	struct hns3_fd_cfg fd_cfg;
 	uint8_t vlan_match_mode;
 	enum hns3_fdir_tuple_config tuple_cfg;
+	enum hns3_fdir_index_config index_cfg;
 };
 
 struct hns3_adapter;
@@ -254,5 +263,6 @@ int hns3_restore_all_fdir_filter(struct hns3_adapter *hns);
 
 enum hns3_fdir_tuple_config hns3_parse_tuple_config(const char *name);
 const char *hns3_tuple_config_name(enum hns3_fdir_tuple_config tuple_cfg);
+const char *hns3_fdir_index_config_name(enum hns3_fdir_index_config cfg);
 
 #endif /* HNS3_FDIR_H */
diff --git a/drivers/net/hns3/hns3_flow.c b/drivers/net/hns3/hns3_flow.c
index 4674f74..40e2409 100644
--- a/drivers/net/hns3/hns3_flow.c
+++ b/drivers/net/hns3/hns3_flow.c
@@ -597,10 +597,6 @@ hns3_check_attr(const struct rte_flow_attr *attr, struct rte_flow_error *error)
 		return rte_flow_error_set(error, ENOTSUP,
 					  RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER,
 					  attr, "No support for transfer");
-	if (attr->priority)
-		return rte_flow_error_set(error, ENOTSUP,
-					  RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
-					  attr, "Not support priority");
 	if (attr->group)
 		return rte_flow_error_set(error, ENOTSUP,
 					  RTE_FLOW_ERROR_TYPE_ATTR_GROUP,
@@ -1441,6 +1437,40 @@ is_tunnel_packet(enum rte_flow_item_type type)
 	return false;
 }
 
+static int
+hns3_handle_attributes(struct rte_eth_dev *dev,
+		       const struct rte_flow_attr *attr,
+		       struct hns3_fdir_rule *rule,
+		       struct rte_flow_error *error)
+{
+	struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct hns3_fdir_info fdir = pf->fdir;
+	uint32_t rule_num;
+
+	if (fdir.index_cfg != HNS3_FDIR_INDEX_CONFIG_PRIORITY) {
+		if (attr->priority == 0)
+			return 0;
+		return rte_flow_error_set(error, ENOTSUP,
+					  RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					  attr, "Not support priority");
+	}
+
+	rule_num = fdir.fd_cfg.rule_num[HNS3_FD_STAGE_1];
+	if (attr->priority >= rule_num)
+		return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					  attr, "Priority out of range");
+
+	if (fdir.hash_map[attr->priority] != NULL)
+		return rte_flow_error_set(error, EINVAL,
+					  RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY,
+					  attr, "Priority already exists");
+
+	rule->location = attr->priority;
+
+	return 0;
+}
+
 /*
  * Parse the flow director rule.
  * The supported PATTERN:
@@ -1468,6 +1498,7 @@ is_tunnel_packet(enum rte_flow_item_type type)
  */
 static int
 hns3_parse_fdir_filter(struct rte_eth_dev *dev,
+		       const struct rte_flow_attr *attr,
 		       const struct rte_flow_item pattern[],
 		       const struct rte_flow_action actions[],
 		       struct hns3_fdir_rule *rule,
@@ -1484,6 +1515,10 @@ hns3_parse_fdir_filter(struct rte_eth_dev *dev,
 					  RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
 					  "Fdir not supported in VF");
 
+	ret = hns3_handle_attributes(dev, attr, rule, error);
+	if (ret)
+		return ret;
+
 	step_mngr.items = first_items;
 	step_mngr.count = RTE_DIM(first_items);
 	for (item = pattern; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
@@ -2248,7 +2283,7 @@ hns3_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
 		return hns3_parse_rss_filter(dev, pattern, actions,
 					     &conf->rss_conf, error);
 
-	return hns3_parse_fdir_filter(dev, pattern, actions,
+	return hns3_parse_fdir_filter(dev, attr, pattern, actions,
 				      &conf->fdir_conf, error);
 }
 
-- 
2.33.0

