+static bool
+hns3_action_rss_same(const struct rte_flow_action_rss *comp,
+ const struct rte_flow_action_rss *with)
+{
+ bool func_is_same;
+
+ /*
+ * When user flush all RSS rule, RSS func is set invalid with
+ * RTE_ETH_HASH_FUNCTION_MAX. Then the user create a flow after
+ * flushed, any validate RSS func is different with it before
+ * flushed. Others, when user create an action RSS with RSS func
+ * specified RTE_ETH_HASH_FUNCTION_DEFAULT, the func is the same
+ * between continuous RSS flow.
+ */
+ if (comp->func == RTE_ETH_HASH_FUNCTION_MAX)
+ func_is_same = false;
+ else
+ func_is_same = (with->func ? (comp->func == with->func) : true);
+
+ return (func_is_same &&
+ comp->types == (with->types & HNS3_ETH_RSS_SUPPORT) &&
+ comp->level == with->level && comp->key_len == with->key_len &&
+ comp->queue_num == with->queue_num &&
+ !memcmp(comp->key, with->key, with->key_len) &&
+ !memcmp(comp->queue, with->queue,
+ sizeof(*with->queue) * with->queue_num));
+}
+
+static int
+hns3_rss_conf_copy(struct hns3_rss_conf *out,
+ const struct rte_flow_action_rss *in)
+{
+ if (in->key_len > RTE_DIM(out->key) ||
+ in->queue_num > RTE_DIM(out->queue))
+ return -EINVAL;
+ if (in->key == NULL && in->key_len)
+ return -EINVAL;
+ out->conf = (struct rte_flow_action_rss) {
+ .func = in->func,
+ .level = in->level,
+ .types = in->types,
+ .key_len = in->key_len,
+ .queue_num = in->queue_num,
+ };
+ out->conf.queue = memcpy(out->queue, in->queue,
+ sizeof(*in->queue) * in->queue_num);
+ if (in->key)
+ out->conf.key = memcpy(out->key, in->key, in->key_len);
+
+ return 0;
+}
+
+/*
+ * This function is used to parse rss action validatation.
+ */
+static int
+hns3_parse_rss_filter(struct rte_eth_dev *dev,
+ const struct rte_flow_action *actions,
+ struct rte_flow_error *error)
+{
+ struct hns3_adapter *hns = dev->data->dev_private;
+ struct hns3_hw *hw = &hns->hw;
+ struct hns3_rss_conf *rss_conf = &hw->rss_info;
+ const struct rte_flow_action_rss *rss;
+ const struct rte_flow_action *act;
+ uint32_t act_index = 0;
+ uint16_t n;
+
+ NEXT_ITEM_OF_ACTION(act, actions, act_index);
+ rss = act->conf;
+
+ if (rss == NULL) {
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+ act, "no valid queues");
+ }
+
+ if (rss->queue_num > RTE_DIM(rss_conf->queue))
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+ "queue number configured exceeds "
+ "queue buffer size driver supported");
+
+ for (n = 0; n < rss->queue_num; n++) {
+ if (rss->queue[n] < hw->alloc_rss_size)
+ continue;
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+ "queue id must be less than queue number allocated to a TC");
+ }
+
+ if (!(rss->types & HNS3_ETH_RSS_SUPPORT) && rss->types)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+ act,
+ "Flow types is unsupported by "
+ "hns3's RSS");
+ if (rss->func >= RTE_ETH_HASH_FUNCTION_MAX)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+ "RSS hash func are not supported");
+ if (rss->level)
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+ "a nonzero RSS encapsulation level is not supported");
+ if (rss->key_len && rss->key_len != RTE_DIM(rss_conf->key))
+ return rte_flow_error_set(error, ENOTSUP,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF, act,
+ "RSS hash key must be exactly 40 bytes");
+
+ /*
+ * For Kunpeng920 and Kunpeng930 NIC hardware, it is not supported to
+ * use dst port/src port fields to RSS hash for the following packet
+ * types.
+ * - IPV4 FRAG | IPV4 NONFRAG | IPV6 FRAG | IPV6 NONFRAG
+ * Besides, for Kunpeng920, The NIC hardware is not supported to use
+ * src/dst port fields to RSS hash for IPV6 SCTP packet type.
+ */
+ if (rss->types & (ETH_RSS_L4_DST_ONLY | ETH_RSS_L4_SRC_ONLY) &&
+ (rss->types & ETH_RSS_IP ||
+ (!hw->rss_info.ipv6_sctp_offload_supported &&
+ rss->types & ETH_RSS_NONFRAG_IPV6_SCTP)))
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION_CONF,
+ &rss->types,
+ "input RSS types are not supported");
+
+ act_index++;
+
+ /* Check if the next not void action is END */
+ NEXT_ITEM_OF_ACTION(act, actions, act_index);
+ if (act->type != RTE_FLOW_ACTION_TYPE_END) {
+ memset(rss_conf, 0, sizeof(struct hns3_rss_conf));
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION,
+ act, "Not supported action.");
+ }
+
+ return 0;
+}
+
+static int
+hns3_disable_rss(struct hns3_hw *hw)
+{
+ int ret;
+
+ /* Redirected the redirection table to queue 0 */
+ ret = hns3_rss_reset_indir_table(hw);
+ if (ret)
+ return ret;
+
+ /* Disable RSS */
+ hw->rss_info.conf.types = 0;
+ hw->rss_dis_flag = true;
+
+ return 0;
+}
+
+static void
+hns3_parse_rss_key(struct hns3_hw *hw, struct rte_flow_action_rss *rss_conf)
+{
+ if (rss_conf->key == NULL || rss_conf->key_len < HNS3_RSS_KEY_SIZE) {
+ hns3_warn(hw, "Default RSS hash key to be set");
+ rss_conf->key = hns3_hash_key;
+ rss_conf->key_len = HNS3_RSS_KEY_SIZE;
+ }
+}
+
+static int
+hns3_parse_rss_algorithm(struct hns3_hw *hw, enum rte_eth_hash_function *func,
+ uint8_t *hash_algo)
+{
+ enum rte_eth_hash_function algo_func = *func;
+ switch (algo_func) {
+ case RTE_ETH_HASH_FUNCTION_DEFAULT:
+ /* Keep *hash_algo as what it used to be */
+ algo_func = hw->rss_info.conf.func;
+ break;
+ case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
+ *hash_algo = HNS3_RSS_HASH_ALGO_TOEPLITZ;
+ break;
+ case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR:
+ *hash_algo = HNS3_RSS_HASH_ALGO_SIMPLE;
+ break;
+ case RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ:
+ *hash_algo = HNS3_RSS_HASH_ALGO_SYMMETRIC_TOEP;
+ break;
+ default:
+ hns3_err(hw, "Invalid RSS algorithm configuration(%u)",
+ algo_func);
+ return -EINVAL;
+ }
+ *func = algo_func;
+
+ return 0;
+}
+
+static int
+hns3_hw_rss_hash_set(struct hns3_hw *hw, struct rte_flow_action_rss *rss_config)
+{
+ struct hns3_rss_tuple_cfg *tuple;
+ int ret;
+
+ hns3_parse_rss_key(hw, rss_config);
+
+ ret = hns3_parse_rss_algorithm(hw, &rss_config->func,
+ &hw->rss_info.hash_algo);
+ if (ret)
+ return ret;
+
+ ret = hns3_set_rss_algo_key(hw, rss_config->key);
+ if (ret)
+ return ret;
+
+ /* Update algorithm of hw */
+ hw->rss_info.conf.func = rss_config->func;
+
+ /* Set flow type supported */
+ tuple = &hw->rss_info.rss_tuple_sets;
+ ret = hns3_set_rss_tuple_by_rss_hf(hw, tuple, rss_config->types);
+ if (ret)
+ hns3_err(hw, "Update RSS tuples by rss hf failed %d", ret);
+
+ return ret;
+}
+
+static int
+hns3_update_indir_table(struct rte_eth_dev *dev,
+ const struct rte_flow_action_rss *conf, uint16_t num)
+{
+ struct hns3_adapter *hns = dev->data->dev_private;
+ struct hns3_hw *hw = &hns->hw;
+ uint16_t indir_tbl[HNS3_RSS_IND_TBL_SIZE];
+ uint16_t j;
+ uint32_t i;
+
+ /* Fill in redirection table */
+ memcpy(indir_tbl, hw->rss_info.rss_indirection_tbl,
+ sizeof(hw->rss_info.rss_indirection_tbl));
+ for (i = 0, j = 0; i < HNS3_RSS_IND_TBL_SIZE; i++, j++) {
+ j %= num;
+ if (conf->queue[j] >= hw->alloc_rss_size) {
+ hns3_err(hw, "queue id(%u) set to redirection table "
+ "exceeds queue number(%u) allocated to a TC.",
+ conf->queue[j], hw->alloc_rss_size);
+ return -EINVAL;
+ }
+ indir_tbl[i] = conf->queue[j];
+ }
+
+ return hns3_set_rss_indir_table(hw, indir_tbl, HNS3_RSS_IND_TBL_SIZE);
+}
+
+static int
+hns3_config_rss_filter(struct rte_eth_dev *dev,
+ const struct hns3_rss_conf *conf, bool add)
+{
+ struct hns3_process_private *process_list = dev->process_private;
+ struct hns3_adapter *hns = dev->data->dev_private;
+ struct hns3_rss_conf_ele *rss_filter_ptr;
+ struct hns3_hw *hw = &hns->hw;
+ struct hns3_rss_conf *rss_info;
+ uint64_t flow_types;
+ uint16_t num;
+ int ret;
+
+ struct rte_flow_action_rss rss_flow_conf = {
+ .func = conf->conf.func,
+ .level = conf->conf.level,
+ .types = conf->conf.types,
+ .key_len = conf->conf.key_len,
+ .queue_num = conf->conf.queue_num,
+ .key = conf->conf.key_len ?
+ (void *)(uintptr_t)conf->conf.key : NULL,
+ .queue = conf->conf.queue,
+ };
+
+ /* Filter the unsupported flow types */
+ flow_types = conf->conf.types ?
+ rss_flow_conf.types & HNS3_ETH_RSS_SUPPORT :
+ hw->rss_info.conf.types;
+ if (flow_types != rss_flow_conf.types)
+ hns3_warn(hw, "modified RSS types based on hardware support, "
+ "requested:%" PRIx64 " configured:%" PRIx64,
+ rss_flow_conf.types, flow_types);
+ /* Update the useful flow types */
+ rss_flow_conf.types = flow_types;
+
+ rss_info = &hw->rss_info;
+ if (!add) {
+ if (!conf->valid)
+ return 0;
+
+ ret = hns3_disable_rss(hw);
+ if (ret) {
+ hns3_err(hw, "RSS disable failed(%d)", ret);
+ return ret;
+ }
+
+ if (rss_flow_conf.queue_num) {
+ /*
+ * Due the content of queue pointer have been reset to
+ * 0, the rss_info->conf.queue should be set NULL
+ */
+ rss_info->conf.queue = NULL;
+ rss_info->conf.queue_num = 0;
+ }
+
+ /* set RSS func invalid after flushed */
+ rss_info->conf.func = RTE_ETH_HASH_FUNCTION_MAX;
+ return 0;
+ }
+
+ /* Set rx queues to use */
+ num = RTE_MIN(dev->data->nb_rx_queues, rss_flow_conf.queue_num);
+ if (rss_flow_conf.queue_num > num)
+ hns3_warn(hw, "Config queue numbers %u are beyond the scope of truncated",
+ rss_flow_conf.queue_num);
+ hns3_info(hw, "Max of contiguous %u PF queues are configured", num);
+
+ rte_spinlock_lock(&hw->lock);
+ if (num) {
+ ret = hns3_update_indir_table(dev, &rss_flow_conf, num);
+ if (ret)
+ goto rss_config_err;
+ }
+
+ /* Set hash algorithm and flow types by the user's config */
+ ret = hns3_hw_rss_hash_set(hw, &rss_flow_conf);
+ if (ret)
+ goto rss_config_err;
+
+ ret = hns3_rss_conf_copy(rss_info, &rss_flow_conf);
+ if (ret) {
+ hns3_err(hw, "RSS config init fail(%d)", ret);
+ goto rss_config_err;
+ }
+
+ /*
+ * When create a new RSS rule, the old rule will be overlaid and set
+ * invalid.
+ */
+ TAILQ_FOREACH(rss_filter_ptr, &process_list->filter_rss_list, entries)
+ rss_filter_ptr->filter_info.valid = false;
+
+rss_config_err:
+ rte_spinlock_unlock(&hw->lock);
+
+ return ret;
+}
+
+static int
+hns3_clear_rss_filter(struct rte_eth_dev *dev)
+{
+ struct hns3_process_private *process_list = dev->process_private;
+ struct hns3_adapter *hns = dev->data->dev_private;
+ struct hns3_rss_conf_ele *rss_filter_ptr;
+ struct hns3_hw *hw = &hns->hw;
+ int rss_rule_succ_cnt = 0; /* count for success of clearing RSS rules */
+ int rss_rule_fail_cnt = 0; /* count for failure of clearing RSS rules */
+ int ret = 0;
+
+ rss_filter_ptr = TAILQ_FIRST(&process_list->filter_rss_list);
+ while (rss_filter_ptr) {
+ TAILQ_REMOVE(&process_list->filter_rss_list, rss_filter_ptr,
+ entries);
+ ret = hns3_config_rss_filter(dev, &rss_filter_ptr->filter_info,
+ false);
+ if (ret)
+ rss_rule_fail_cnt++;
+ else
+ rss_rule_succ_cnt++;
+ rte_free(rss_filter_ptr);
+ rss_filter_ptr = TAILQ_FIRST(&process_list->filter_rss_list);
+ }
+
+ if (rss_rule_fail_cnt) {
+ hns3_err(hw, "fail to delete all RSS filters, success num = %d "
+ "fail num = %d", rss_rule_succ_cnt,
+ rss_rule_fail_cnt);
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+int
+hns3_restore_rss_filter(struct rte_eth_dev *dev)
+{
+ struct hns3_adapter *hns = dev->data->dev_private;
+ struct hns3_hw *hw = &hns->hw;
+
+ /* When user flush all rules, it doesn't need to restore RSS rule */
+ if (hw->rss_info.conf.func == RTE_ETH_HASH_FUNCTION_MAX)
+ return 0;
+
+ return hns3_config_rss_filter(dev, &hw->rss_info, true);
+}
+
+static int
+hns3_flow_parse_rss(struct rte_eth_dev *dev,
+ const struct hns3_rss_conf *conf, bool add)
+{
+ struct hns3_adapter *hns = dev->data->dev_private;
+ struct hns3_hw *hw = &hns->hw;
+ bool ret;
+
+ ret = hns3_action_rss_same(&hw->rss_info.conf, &conf->conf);
+ if (ret) {
+ hns3_err(hw, "Enter duplicate RSS configuration : %d", ret);
+ return -EINVAL;
+ }
+
+ return hns3_config_rss_filter(dev, conf, add);
+}
+