numam-dpdk/drivers/net/hns3/hns3_fdir.c
Wei Hu (Xavier) fcba820d9b net/hns3: support flow director
This patch adds support for rte_flow_ops operation and flow
director of hns3 PMD driver.
Flow director feature is only supported in hns3 PF driver.
It supports the network L2\L3\L4 and tunnel packet creation,
deletion, flushing, and querying hit statistics.

Signed-off-by: Chunsong Feng <fengchunsong@huawei.com>
Signed-off-by: Wei Hu (Xavier) <xavier.huwei@huawei.com>
Signed-off-by: Hao Chen <chenhao164@huawei.com>
Signed-off-by: Min Hu (Connor) <humin29@huawei.com>
Signed-off-by: Huisong Li <lihuisong@huawei.com>
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
2019-10-07 15:00:56 +02:00

1060 lines
28 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2018-2019 Hisilicon Limited.
*/
#include <stdbool.h>
#include <rte_ethdev_driver.h>
#include <rte_hash.h>
#include <rte_hash_crc.h>
#include <rte_io.h>
#include <rte_malloc.h>
#include "hns3_ethdev.h"
#include "hns3_logs.h"
#define HNS3_VLAN_TAG_TYPE_NONE 0
#define HNS3_VLAN_TAG_TYPE_TAG2 1
#define HNS3_VLAN_TAG_TYPE_TAG1 2
#define HNS3_VLAN_TAG_TYPE_TAG1_2 3
#define HNS3_PF_ID_S 0
#define HNS3_PF_ID_M GENMASK(2, 0)
#define HNS3_VF_ID_S 3
#define HNS3_VF_ID_M GENMASK(10, 3)
#define HNS3_PORT_TYPE_B 11
#define HNS3_NETWORK_PORT_ID_S 0
#define HNS3_NETWORK_PORT_ID_M GENMASK(3, 0)
#define HNS3_FD_EPORT_SW_EN_B 0
#define HNS3_FD_AD_DATA_S 32
#define HNS3_FD_AD_DROP_B 0
#define HNS3_FD_AD_DIRECT_QID_B 1
#define HNS3_FD_AD_QID_S 2
#define HNS3_FD_AD_QID_M GENMASK(12, 2)
#define HNS3_FD_AD_USE_COUNTER_B 12
#define HNS3_FD_AD_COUNTER_NUM_S 13
#define HNS3_FD_AD_COUNTER_NUM_M GENMASK(20, 13)
#define HNS3_FD_AD_NXT_STEP_B 20
#define HNS3_FD_AD_NXT_KEY_S 21
#define HNS3_FD_AD_NXT_KEY_M GENMASK(26, 21)
#define HNS3_FD_AD_WR_RULE_ID_B 0
#define HNS3_FD_AD_RULE_ID_S 1
#define HNS3_FD_AD_RULE_ID_M GENMASK(13, 1)
enum HNS3_PORT_TYPE {
HOST_PORT,
NETWORK_PORT
};
enum HNS3_FD_MODE {
HNS3_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1,
HNS3_FD_MODE_DEPTH_1K_WIDTH_400B_STAGE_2,
HNS3_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1,
HNS3_FD_MODE_DEPTH_2K_WIDTH_200B_STAGE_2,
};
enum HNS3_FD_KEY_TYPE {
HNS3_FD_KEY_BASE_ON_PTYPE,
HNS3_FD_KEY_BASE_ON_TUPLE,
};
enum HNS3_FD_META_DATA {
PACKET_TYPE_ID,
IP_FRAGEMENT,
ROCE_TYPE,
NEXT_KEY,
VLAN_NUMBER,
SRC_VPORT,
DST_VPORT,
TUNNEL_PACKET,
MAX_META_DATA,
};
struct key_info {
uint8_t key_type;
uint8_t key_length;
};
static const struct key_info meta_data_key_info[] = {
{PACKET_TYPE_ID, 6},
{IP_FRAGEMENT, 1},
{ROCE_TYPE, 1},
{NEXT_KEY, 5},
{VLAN_NUMBER, 2},
{SRC_VPORT, 12},
{DST_VPORT, 12},
{TUNNEL_PACKET, 1},
};
static const struct key_info tuple_key_info[] = {
{OUTER_DST_MAC, 48},
{OUTER_SRC_MAC, 48},
{OUTER_VLAN_TAG_FST, 16},
{OUTER_VLAN_TAG_SEC, 16},
{OUTER_ETH_TYPE, 16},
{OUTER_L2_RSV, 16},
{OUTER_IP_TOS, 8},
{OUTER_IP_PROTO, 8},
{OUTER_SRC_IP, 32},
{OUTER_DST_IP, 32},
{OUTER_L3_RSV, 16},
{OUTER_SRC_PORT, 16},
{OUTER_DST_PORT, 16},
{OUTER_L4_RSV, 32},
{OUTER_TUN_VNI, 24},
{OUTER_TUN_FLOW_ID, 8},
{INNER_DST_MAC, 48},
{INNER_SRC_MAC, 48},
{INNER_VLAN_TAG1, 16},
{INNER_VLAN_TAG2, 16},
{INNER_ETH_TYPE, 16},
{INNER_L2_RSV, 16},
{INNER_IP_TOS, 8},
{INNER_IP_PROTO, 8},
{INNER_SRC_IP, 32},
{INNER_DST_IP, 32},
{INNER_L3_RSV, 16},
{INNER_SRC_PORT, 16},
{INNER_DST_PORT, 16},
{INNER_SCTP_TAG, 32},
};
#define HNS3_BITS_PER_BYTE 8
#define MAX_KEY_LENGTH 400
#define MAX_200B_KEY_LENGTH 200
#define MAX_META_DATA_LENGTH 16
#define MAX_KEY_DWORDS DIV_ROUND_UP(MAX_KEY_LENGTH / HNS3_BITS_PER_BYTE, 4)
#define MAX_KEY_BYTES (MAX_KEY_DWORDS * 4)
enum HNS3_FD_PACKET_TYPE {
NIC_PACKET,
ROCE_PACKET,
};
/* For each bit of TCAM entry, it uses a pair of 'x' and
* 'y' to indicate which value to match, like below:
* ----------------------------------
* | bit x | bit y | search value |
* ----------------------------------
* | 0 | 0 | always hit |
* ----------------------------------
* | 1 | 0 | match '0' |
* ----------------------------------
* | 0 | 1 | match '1' |
* ----------------------------------
* | 1 | 1 | invalid |
* ----------------------------------
* Then for input key(k) and mask(v), we can calculate the value by
* the formulae:
* x = (~k) & v
* y = k & v
*/
#define calc_x(x, k, v) ((x) = (~(k) & (v)))
#define calc_y(y, k, v) ((y) = ((k) & (v)))
struct hns3_fd_tcam_config_1_cmd {
uint8_t stage;
uint8_t xy_sel;
uint8_t port_info;
uint8_t rsv1[1];
rte_le32_t index;
uint8_t entry_vld;
uint8_t rsv2[7];
uint8_t tcam_data[8];
};
struct hns3_fd_tcam_config_2_cmd {
uint8_t tcam_data[24];
};
struct hns3_fd_tcam_config_3_cmd {
uint8_t tcam_data[20];
uint8_t rsv[4];
};
struct hns3_get_fd_mode_cmd {
uint8_t mode;
uint8_t enable;
uint8_t rsv[22];
};
struct hns3_get_fd_allocation_cmd {
rte_le32_t stage1_entry_num;
rte_le32_t stage2_entry_num;
rte_le16_t stage1_counter_num;
rte_le16_t stage2_counter_num;
uint8_t rsv[12];
};
struct hns3_set_fd_key_config_cmd {
uint8_t stage;
uint8_t key_select;
uint8_t inner_sipv6_word_en;
uint8_t inner_dipv6_word_en;
uint8_t outer_sipv6_word_en;
uint8_t outer_dipv6_word_en;
uint8_t rsv1[2];
rte_le32_t tuple_mask;
rte_le32_t meta_data_mask;
uint8_t rsv2[8];
};
struct hns3_fd_ad_config_cmd {
uint8_t stage;
uint8_t rsv1[3];
rte_le32_t index;
rte_le64_t ad_data;
uint8_t rsv2[8];
};
struct hns3_fd_get_cnt_cmd {
uint8_t stage;
uint8_t rsv1[3];
rte_le16_t index;
uint8_t rsv2[2];
rte_le64_t value;
uint8_t rsv3[8];
};
static int hns3_get_fd_mode(struct hns3_hw *hw, uint8_t *fd_mode)
{
struct hns3_get_fd_mode_cmd *req;
struct hns3_cmd_desc desc;
int ret;
hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_MODE_CTRL, true);
req = (struct hns3_get_fd_mode_cmd *)desc.data;
ret = hns3_cmd_send(hw, &desc, 1);
if (ret) {
hns3_err(hw, "Get fd mode fail, ret=%d", ret);
return ret;
}
*fd_mode = req->mode;
return ret;
}
static int hns3_get_fd_allocation(struct hns3_hw *hw,
uint32_t *stage1_entry_num,
uint32_t *stage2_entry_num,
uint16_t *stage1_counter_num,
uint16_t *stage2_counter_num)
{
struct hns3_get_fd_allocation_cmd *req;
struct hns3_cmd_desc desc;
int ret;
hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_GET_ALLOCATION, true);
req = (struct hns3_get_fd_allocation_cmd *)desc.data;
ret = hns3_cmd_send(hw, &desc, 1);
if (ret) {
hns3_err(hw, "Query fd allocation fail, ret=%d", ret);
return ret;
}
*stage1_entry_num = rte_le_to_cpu_32(req->stage1_entry_num);
*stage2_entry_num = rte_le_to_cpu_32(req->stage2_entry_num);
*stage1_counter_num = rte_le_to_cpu_16(req->stage1_counter_num);
*stage2_counter_num = rte_le_to_cpu_16(req->stage2_counter_num);
return ret;
}
static int hns3_set_fd_key_config(struct hns3_adapter *hns)
{
struct hns3_set_fd_key_config_cmd *req;
struct hns3_fd_key_cfg *key_cfg;
struct hns3_pf *pf = &hns->pf;
struct hns3_hw *hw = &hns->hw;
struct hns3_cmd_desc desc;
int ret;
hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_KEY_CONFIG, false);
req = (struct hns3_set_fd_key_config_cmd *)desc.data;
key_cfg = &pf->fdir.fd_cfg.key_cfg[HNS3_FD_STAGE_1];
req->stage = HNS3_FD_STAGE_1;
req->key_select = key_cfg->key_sel;
req->inner_sipv6_word_en = key_cfg->inner_sipv6_word_en;
req->inner_dipv6_word_en = key_cfg->inner_dipv6_word_en;
req->outer_sipv6_word_en = key_cfg->outer_sipv6_word_en;
req->outer_dipv6_word_en = key_cfg->outer_dipv6_word_en;
req->tuple_mask = rte_cpu_to_le_32(~key_cfg->tuple_active);
req->meta_data_mask = rte_cpu_to_le_32(~key_cfg->meta_data_active);
ret = hns3_cmd_send(hw, &desc, 1);
if (ret)
hns3_err(hw, "Set fd key fail, ret=%d", ret);
return ret;
}
int hns3_init_fd_config(struct hns3_adapter *hns)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_hw *hw = &hns->hw;
struct hns3_fd_key_cfg *key_cfg;
int ret;
ret = hns3_get_fd_mode(hw, &pf->fdir.fd_cfg.fd_mode);
if (ret)
return ret;
switch (pf->fdir.fd_cfg.fd_mode) {
case HNS3_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1:
pf->fdir.fd_cfg.max_key_length = MAX_KEY_LENGTH;
break;
case HNS3_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1:
pf->fdir.fd_cfg.max_key_length = MAX_200B_KEY_LENGTH;
hns3_warn(hw, "Unsupported tunnel filter in 4K*200Bit");
break;
default:
hns3_err(hw, "Unsupported flow director mode %d",
pf->fdir.fd_cfg.fd_mode);
return -EOPNOTSUPP;
}
key_cfg = &pf->fdir.fd_cfg.key_cfg[HNS3_FD_STAGE_1];
key_cfg->key_sel = HNS3_FD_KEY_BASE_ON_TUPLE;
key_cfg->inner_sipv6_word_en = IPV6_ADDR_WORD_MASK;
key_cfg->inner_dipv6_word_en = IPV6_ADDR_WORD_MASK;
key_cfg->outer_sipv6_word_en = 0;
key_cfg->outer_dipv6_word_en = 0;
key_cfg->tuple_active = BIT(INNER_VLAN_TAG1) | BIT(INNER_ETH_TYPE) |
BIT(INNER_IP_PROTO) | BIT(INNER_IP_TOS) |
BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
/* If use max 400bit key, we can support tuples for ether type */
if (pf->fdir.fd_cfg.max_key_length == MAX_KEY_LENGTH) {
key_cfg->tuple_active |=
BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC) |
BIT(OUTER_SRC_PORT) | BIT(INNER_SCTP_TAG) |
BIT(OUTER_DST_PORT) | BIT(INNER_VLAN_TAG2) |
BIT(OUTER_TUN_VNI) | BIT(OUTER_TUN_FLOW_ID) |
BIT(OUTER_ETH_TYPE) | BIT(OUTER_IP_PROTO);
}
/* roce_type is used to filter roce frames
* dst_vport is used to specify the rule
*/
key_cfg->meta_data_active = BIT(DST_VPORT) | BIT(TUNNEL_PACKET) |
BIT(VLAN_NUMBER);
ret = hns3_get_fd_allocation(hw,
&pf->fdir.fd_cfg.rule_num[HNS3_FD_STAGE_1],
&pf->fdir.fd_cfg.rule_num[HNS3_FD_STAGE_2],
&pf->fdir.fd_cfg.cnt_num[HNS3_FD_STAGE_1],
&pf->fdir.fd_cfg.cnt_num[HNS3_FD_STAGE_2]);
if (ret)
return ret;
return hns3_set_fd_key_config(hns);
}
static int hns3_fd_tcam_config(struct hns3_hw *hw, bool sel_x, int loc,
uint8_t *key, bool is_add)
{
#define FD_TCAM_CMD_NUM 3
struct hns3_fd_tcam_config_1_cmd *req1;
struct hns3_fd_tcam_config_2_cmd *req2;
struct hns3_fd_tcam_config_3_cmd *req3;
struct hns3_cmd_desc desc[FD_TCAM_CMD_NUM];
int len;
int ret;
hns3_cmd_setup_basic_desc(&desc[0], HNS3_OPC_FD_TCAM_OP, false);
desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
hns3_cmd_setup_basic_desc(&desc[1], HNS3_OPC_FD_TCAM_OP, false);
desc[1].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT);
hns3_cmd_setup_basic_desc(&desc[2], HNS3_OPC_FD_TCAM_OP, false);
req1 = (struct hns3_fd_tcam_config_1_cmd *)desc[0].data;
req2 = (struct hns3_fd_tcam_config_2_cmd *)desc[1].data;
req3 = (struct hns3_fd_tcam_config_3_cmd *)desc[2].data;
req1->stage = HNS3_FD_STAGE_1;
req1->xy_sel = sel_x ? 1 : 0;
hns3_set_bit(req1->port_info, HNS3_FD_EPORT_SW_EN_B, 0);
req1->index = rte_cpu_to_le_32(loc);
req1->entry_vld = sel_x ? is_add : 0;
if (key) {
len = sizeof(req1->tcam_data);
memcpy(req1->tcam_data, key, len);
key += len;
len = sizeof(req2->tcam_data);
memcpy(req2->tcam_data, key, len);
key += len;
len = sizeof(req3->tcam_data);
memcpy(req3->tcam_data, key, len);
}
ret = hns3_cmd_send(hw, desc, FD_TCAM_CMD_NUM);
if (ret)
hns3_err(hw, "Config tcam key fail, ret=%d loc=%d add=%d",
ret, loc, is_add);
return ret;
}
static int hns3_fd_ad_config(struct hns3_hw *hw, int loc,
struct hns3_fd_ad_data *action)
{
struct hns3_fd_ad_config_cmd *req;
struct hns3_cmd_desc desc;
uint64_t ad_data = 0;
int ret;
hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_AD_OP, false);
req = (struct hns3_fd_ad_config_cmd *)desc.data;
req->index = rte_cpu_to_le_32(loc);
req->stage = HNS3_FD_STAGE_1;
hns3_set_bit(ad_data, HNS3_FD_AD_WR_RULE_ID_B,
action->write_rule_id_to_bd);
hns3_set_field(ad_data, HNS3_FD_AD_RULE_ID_M, HNS3_FD_AD_RULE_ID_S,
action->rule_id);
ad_data <<= HNS3_FD_AD_DATA_S;
hns3_set_bit(ad_data, HNS3_FD_AD_DROP_B, action->drop_packet);
hns3_set_bit(ad_data, HNS3_FD_AD_DIRECT_QID_B,
action->forward_to_direct_queue);
hns3_set_field(ad_data, HNS3_FD_AD_QID_M, HNS3_FD_AD_QID_S,
action->queue_id);
hns3_set_bit(ad_data, HNS3_FD_AD_USE_COUNTER_B, action->use_counter);
hns3_set_field(ad_data, HNS3_FD_AD_COUNTER_NUM_M,
HNS3_FD_AD_COUNTER_NUM_S, action->counter_id);
hns3_set_bit(ad_data, HNS3_FD_AD_NXT_STEP_B, action->use_next_stage);
hns3_set_field(ad_data, HNS3_FD_AD_NXT_KEY_M, HNS3_FD_AD_NXT_KEY_S,
action->counter_id);
req->ad_data = rte_cpu_to_le_64(ad_data);
ret = hns3_cmd_send(hw, &desc, 1);
if (ret)
hns3_err(hw, "Config fd ad fail, ret=%d loc=%d", ret, loc);
return ret;
}
static inline void hns3_fd_convert_mac(uint8_t *key, uint8_t *mask,
uint8_t *mac_x, uint8_t *mac_y)
{
uint8_t tmp;
int i;
for (i = 0; i < RTE_ETHER_ADDR_LEN; i++) {
tmp = RTE_ETHER_ADDR_LEN - 1 - i;
calc_x(mac_x[tmp], key[i], mask[i]);
calc_y(mac_y[tmp], key[i], mask[i]);
}
}
static void hns3_fd_convert_int16(uint32_t tuple, struct hns3_fdir_rule *rule,
uint8_t *val_x, uint8_t *val_y)
{
uint16_t tmp_x_s;
uint16_t tmp_y_s;
uint16_t mask;
uint16_t key;
switch (tuple) {
case OUTER_SRC_PORT:
key = rule->key_conf.spec.outer_src_port;
mask = rule->key_conf.mask.outer_src_port;
break;
case OUTER_DST_PORT:
key = rule->key_conf.spec.tunnel_type;
mask = rule->key_conf.mask.tunnel_type;
break;
case OUTER_ETH_TYPE:
key = rule->key_conf.spec.outer_ether_type;
mask = rule->key_conf.mask.outer_ether_type;
break;
case INNER_SRC_PORT:
key = rule->key_conf.spec.src_port;
mask = rule->key_conf.mask.src_port;
break;
case INNER_DST_PORT:
key = rule->key_conf.spec.dst_port;
mask = rule->key_conf.mask.dst_port;
break;
case INNER_VLAN_TAG1:
key = rule->key_conf.spec.vlan_tag1;
mask = rule->key_conf.mask.vlan_tag1;
break;
case INNER_VLAN_TAG2:
key = rule->key_conf.spec.vlan_tag2;
mask = rule->key_conf.mask.vlan_tag2;
break;
default:
/* INNER_ETH_TYPE: */
key = rule->key_conf.spec.ether_type;
mask = rule->key_conf.mask.ether_type;
break;
}
calc_x(tmp_x_s, key, mask);
calc_y(tmp_y_s, key, mask);
val_x[0] = rte_cpu_to_le_16(tmp_x_s) & 0xFF;
val_x[1] = rte_cpu_to_le_16(tmp_x_s) >> HNS3_BITS_PER_BYTE;
val_y[0] = rte_cpu_to_le_16(tmp_y_s) & 0xFF;
val_y[1] = rte_cpu_to_le_16(tmp_y_s) >> HNS3_BITS_PER_BYTE;
}
static inline void hns3_fd_convert_int32(uint32_t key, uint32_t mask,
uint8_t *val_x, uint8_t *val_y)
{
uint32_t tmp_x_l;
uint32_t tmp_y_l;
calc_x(tmp_x_l, key, mask);
calc_y(tmp_y_l, key, mask);
memcpy(val_x, &tmp_x_l, sizeof(tmp_x_l));
memcpy(val_y, &tmp_y_l, sizeof(tmp_y_l));
}
static bool hns3_fd_convert_tuple(uint32_t tuple, uint8_t *key_x,
uint8_t *key_y, struct hns3_fdir_rule *rule)
{
struct hns3_fdir_key_conf *key_conf;
int tmp;
int i;
if ((rule->input_set & BIT(tuple)) == 0)
return true;
key_conf = &rule->key_conf;
switch (tuple) {
case INNER_DST_MAC:
hns3_fd_convert_mac(key_conf->spec.dst_mac,
key_conf->mask.dst_mac, key_x, key_y);
break;
case INNER_SRC_MAC:
hns3_fd_convert_mac(key_conf->spec.src_mac,
key_conf->mask.src_mac, key_x, key_y);
break;
case OUTER_SRC_PORT:
case OUTER_DST_PORT:
case OUTER_ETH_TYPE:
case INNER_SRC_PORT:
case INNER_DST_PORT:
case INNER_VLAN_TAG1:
case INNER_VLAN_TAG2:
case INNER_ETH_TYPE:
hns3_fd_convert_int16(tuple, rule, key_x, key_y);
break;
case INNER_SRC_IP:
hns3_fd_convert_int32(key_conf->spec.src_ip[IP_ADDR_KEY_ID],
key_conf->mask.src_ip[IP_ADDR_KEY_ID],
key_x, key_y);
break;
case INNER_DST_IP:
hns3_fd_convert_int32(key_conf->spec.dst_ip[IP_ADDR_KEY_ID],
key_conf->mask.dst_ip[IP_ADDR_KEY_ID],
key_x, key_y);
break;
case INNER_SCTP_TAG:
hns3_fd_convert_int32(key_conf->spec.sctp_tag,
key_conf->mask.sctp_tag, key_x, key_y);
break;
case OUTER_TUN_VNI:
for (i = 0; i < VNI_OR_TNI_LEN; i++) {
tmp = VNI_OR_TNI_LEN - 1 - i;
calc_x(key_x[tmp],
key_conf->spec.outer_tun_vni[i],
key_conf->mask.outer_tun_vni[i]);
calc_y(key_y[tmp],
key_conf->spec.outer_tun_vni[i],
key_conf->mask.outer_tun_vni[i]);
}
break;
case OUTER_TUN_FLOW_ID:
calc_x(*key_x, key_conf->spec.outer_tun_flow_id,
key_conf->mask.outer_tun_flow_id);
calc_y(*key_y, key_conf->spec.outer_tun_flow_id,
key_conf->mask.outer_tun_flow_id);
break;
case INNER_IP_TOS:
calc_x(*key_x, key_conf->spec.ip_tos, key_conf->mask.ip_tos);
calc_y(*key_y, key_conf->spec.ip_tos, key_conf->mask.ip_tos);
break;
case OUTER_IP_PROTO:
calc_x(*key_x, key_conf->spec.outer_proto,
key_conf->mask.outer_proto);
calc_y(*key_y, key_conf->spec.outer_proto,
key_conf->mask.outer_proto);
break;
case INNER_IP_PROTO:
calc_x(*key_x, key_conf->spec.ip_proto,
key_conf->mask.ip_proto);
calc_y(*key_y, key_conf->spec.ip_proto,
key_conf->mask.ip_proto);
break;
}
return true;
}
static uint32_t hns3_get_port_number(uint8_t pf_id, uint8_t vf_id)
{
uint32_t port_number = 0;
hns3_set_field(port_number, HNS3_PF_ID_M, HNS3_PF_ID_S, pf_id);
hns3_set_field(port_number, HNS3_VF_ID_M, HNS3_VF_ID_S, vf_id);
hns3_set_bit(port_number, HNS3_PORT_TYPE_B, HOST_PORT);
return port_number;
}
static void hns3_fd_convert_meta_data(struct hns3_fd_key_cfg *cfg,
uint8_t vf_id,
struct hns3_fdir_rule *rule,
uint8_t *key_x, uint8_t *key_y)
{
uint16_t meta_data = 0;
uint16_t port_number;
uint8_t cur_pos = 0;
uint8_t tuple_size;
uint8_t shift_bits;
uint32_t tmp_x;
uint32_t tmp_y;
uint8_t i;
for (i = 0; i < MAX_META_DATA; i++) {
if ((cfg->meta_data_active & BIT(i)) == 0)
continue;
tuple_size = meta_data_key_info[i].key_length;
if (i == TUNNEL_PACKET) {
hns3_set_bit(meta_data, cur_pos,
rule->key_conf.spec.tunnel_type ? 1 : 0);
cur_pos += tuple_size;
} else if (i == VLAN_NUMBER) {
uint8_t vlan_tag;
uint8_t vlan_num;
if (rule->key_conf.spec.tunnel_type == 0)
vlan_num = rule->key_conf.vlan_num;
else
vlan_num = rule->key_conf.outer_vlan_num;
if (vlan_num == 1)
vlan_tag = HNS3_VLAN_TAG_TYPE_TAG1;
else if (vlan_num == VLAN_TAG_NUM_MAX)
vlan_tag = HNS3_VLAN_TAG_TYPE_TAG1_2;
else
vlan_tag = HNS3_VLAN_TAG_TYPE_NONE;
hns3_set_field(meta_data,
GENMASK(cur_pos + tuple_size,
cur_pos), cur_pos, vlan_tag);
cur_pos += tuple_size;
} else if (i == DST_VPORT) {
port_number = hns3_get_port_number(0, vf_id);
hns3_set_field(meta_data,
GENMASK(cur_pos + tuple_size, cur_pos),
cur_pos, port_number);
cur_pos += tuple_size;
}
}
calc_x(tmp_x, meta_data, 0xFFFF);
calc_y(tmp_y, meta_data, 0xFFFF);
shift_bits = sizeof(meta_data) * HNS3_BITS_PER_BYTE - cur_pos;
tmp_x = rte_cpu_to_le_32(tmp_x << shift_bits);
tmp_y = rte_cpu_to_le_32(tmp_y << shift_bits);
key_x[0] = tmp_x & 0xFF;
key_x[1] = (tmp_x >> HNS3_BITS_PER_BYTE) & 0xFF;
key_y[0] = tmp_y & 0xFF;
key_y[1] = (tmp_y >> HNS3_BITS_PER_BYTE) & 0xFF;
}
/* A complete key is combined with meta data key and tuple key.
* Meta data key is stored at the MSB region, and tuple key is stored at
* the LSB region, unused bits will be filled 0.
*/
static int hns3_config_key(struct hns3_adapter *hns,
struct hns3_fdir_rule *rule)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_hw *hw = &hns->hw;
struct hns3_fd_key_cfg *key_cfg;
uint8_t *cur_key_x;
uint8_t *cur_key_y;
uint8_t key_x[MAX_KEY_BYTES] __attribute__((aligned(4)));
uint8_t key_y[MAX_KEY_BYTES] __attribute__((aligned(4)));
uint8_t vf_id = rule->vf_id;
uint8_t meta_data_region;
uint8_t tuple_size;
uint8_t i;
int ret;
memset(key_x, 0, sizeof(key_x));
memset(key_y, 0, sizeof(key_y));
cur_key_x = key_x;
cur_key_y = key_y;
key_cfg = &pf->fdir.fd_cfg.key_cfg[HNS3_FD_STAGE_1];
for (i = 0; i < MAX_TUPLE; i++) {
bool tuple_valid;
tuple_size = tuple_key_info[i].key_length / HNS3_BITS_PER_BYTE;
if (key_cfg->tuple_active & BIT(i)) {
tuple_valid = hns3_fd_convert_tuple(i, cur_key_x,
cur_key_y, rule);
if (tuple_valid) {
cur_key_x += tuple_size;
cur_key_y += tuple_size;
}
}
}
meta_data_region = pf->fdir.fd_cfg.max_key_length / HNS3_BITS_PER_BYTE -
MAX_META_DATA_LENGTH / HNS3_BITS_PER_BYTE;
hns3_fd_convert_meta_data(key_cfg, vf_id, rule,
key_x + meta_data_region,
key_y + meta_data_region);
ret = hns3_fd_tcam_config(hw, false, rule->location, key_y, true);
if (ret) {
hns3_err(hw, "Config fd key_y fail, loc=%d, ret=%d",
rule->queue_id, ret);
return ret;
}
ret = hns3_fd_tcam_config(hw, true, rule->location, key_x, true);
if (ret)
hns3_err(hw, "Config fd key_x fail, loc=%d, ret=%d",
rule->queue_id, ret);
return ret;
}
static int hns3_config_action(struct hns3_hw *hw, struct hns3_fdir_rule *rule)
{
struct hns3_fd_ad_data ad_data;
ad_data.ad_id = rule->location;
if (rule->action == HNS3_FD_ACTION_DROP_PACKET) {
ad_data.drop_packet = true;
ad_data.forward_to_direct_queue = false;
ad_data.queue_id = 0;
} else {
ad_data.drop_packet = false;
ad_data.forward_to_direct_queue = true;
ad_data.queue_id = rule->queue_id;
}
if (unlikely(rule->flags & HNS3_RULE_FLAG_COUNTER)) {
ad_data.use_counter = true;
ad_data.counter_id = rule->act_cnt.id;
} else {
ad_data.use_counter = false;
ad_data.counter_id = 0;
}
if (unlikely(rule->flags & HNS3_RULE_FLAG_FDID))
ad_data.rule_id = rule->fd_id;
else
ad_data.rule_id = rule->location;
ad_data.use_next_stage = false;
ad_data.next_input_key = 0;
ad_data.write_rule_id_to_bd = true;
return hns3_fd_ad_config(hw, ad_data.ad_id, &ad_data);
}
int hns3_fdir_filter_init(struct hns3_adapter *hns)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_fdir_info *fdir_info = &pf->fdir;
uint32_t rule_num = fdir_info->fd_cfg.rule_num[HNS3_FD_STAGE_1];
char fdir_hash_name[RTE_HASH_NAMESIZE];
struct rte_hash_parameters fdir_hash_params = {
.name = fdir_hash_name,
.entries = rule_num,
.key_len = sizeof(struct hns3_fdir_key_conf),
.hash_func = rte_hash_crc,
.hash_func_init_val = 0,
};
fdir_hash_params.socket_id = rte_socket_id();
TAILQ_INIT(&fdir_info->fdir_list);
rte_spinlock_init(&fdir_info->flows_lock);
snprintf(fdir_hash_name, RTE_HASH_NAMESIZE, "%s", hns->hw.data->name);
fdir_info->hash_handle = rte_hash_create(&fdir_hash_params);
if (fdir_info->hash_handle == NULL) {
PMD_INIT_LOG(ERR, "Create FDIR hash handle fail!");
return -EINVAL;
}
fdir_info->hash_map = rte_zmalloc("hns3 FDIR hash",
rule_num *
sizeof(struct hns3_fdir_rule_ele *),
0);
if (fdir_info->hash_map == NULL) {
PMD_INIT_LOG(ERR, "Allocate memory for FDIR hash map fail!");
rte_hash_free(fdir_info->hash_handle);
return -ENOMEM;
}
return 0;
}
void hns3_fdir_filter_uninit(struct hns3_adapter *hns)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_fdir_info *fdir_info = &pf->fdir;
struct hns3_fdir_rule_ele *fdir_filter;
rte_spinlock_lock(&fdir_info->flows_lock);
if (fdir_info->hash_map) {
rte_free(fdir_info->hash_map);
fdir_info->hash_map = NULL;
}
if (fdir_info->hash_handle) {
rte_hash_free(fdir_info->hash_handle);
fdir_info->hash_handle = NULL;
}
rte_spinlock_unlock(&fdir_info->flows_lock);
fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list);
while (fdir_filter) {
TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries);
hns3_fd_tcam_config(&hns->hw, true,
fdir_filter->fdir_conf.location, NULL,
false);
rte_free(fdir_filter);
fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list);
}
}
/*
* Find a key in the hash table.
* @return
* - Zero and positive values are key location.
* - -EINVAL if the parameters are invalid.
* - -ENOENT if the key is not found.
*/
static int hns3_fdir_filter_lookup(struct hns3_fdir_info *fdir_info,
struct hns3_fdir_key_conf *key)
{
hash_sig_t sig;
int ret;
rte_spinlock_lock(&fdir_info->flows_lock);
sig = rte_hash_crc(key, sizeof(*key), 0);
ret = rte_hash_lookup_with_hash(fdir_info->hash_handle, key, sig);
rte_spinlock_unlock(&fdir_info->flows_lock);
return ret;
}
static int hns3_insert_fdir_filter(struct hns3_hw *hw,
struct hns3_fdir_info *fdir_info,
struct hns3_fdir_rule_ele *fdir_filter)
{
struct hns3_fdir_key_conf *key;
hash_sig_t sig;
int ret;
key = &fdir_filter->fdir_conf.key_conf;
rte_spinlock_lock(&fdir_info->flows_lock);
sig = rte_hash_crc(key, sizeof(*key), 0);
ret = rte_hash_add_key_with_hash(fdir_info->hash_handle, key, sig);
if (ret < 0) {
rte_spinlock_unlock(&fdir_info->flows_lock);
hns3_err(hw, "Hash table full? err:%d(%s)!", ret,
strerror(ret));
return ret;
}
fdir_info->hash_map[ret] = fdir_filter;
TAILQ_INSERT_TAIL(&fdir_info->fdir_list, fdir_filter, entries);
rte_spinlock_unlock(&fdir_info->flows_lock);
return ret;
}
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_ele *fdir_filter;
hash_sig_t sig;
int ret;
rte_spinlock_lock(&fdir_info->flows_lock);
sig = rte_hash_crc(key, sizeof(*key), 0);
ret = rte_hash_del_key_with_hash(fdir_info->hash_handle, key, sig);
if (ret < 0) {
rte_spinlock_unlock(&fdir_info->flows_lock);
hns3_err(hw, "Delete hash key fail ret=%d", ret);
return ret;
}
fdir_filter = fdir_info->hash_map[ret];
fdir_info->hash_map[ret] = NULL;
TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries);
rte_spinlock_unlock(&fdir_info->flows_lock);
rte_free(fdir_filter);
return 0;
}
int hns3_fdir_filter_program(struct hns3_adapter *hns,
struct hns3_fdir_rule *rule, bool del)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_fdir_info *fdir_info = &pf->fdir;
struct hns3_fdir_rule_ele *node;
struct hns3_hw *hw = &hns->hw;
int ret;
if (del) {
ret = hns3_fd_tcam_config(hw, true, rule->location, NULL,
false);
if (ret)
hns3_err(hw, "Failed to delete fdir: %d src_ip:%x "
"dst_ip:%x src_port:%d dst_port:%d",
rule->location,
rule->key_conf.spec.src_ip[IP_ADDR_KEY_ID],
rule->key_conf.spec.dst_ip[IP_ADDR_KEY_ID],
rule->key_conf.spec.src_port,
rule->key_conf.spec.dst_port);
else
hns3_remove_fdir_filter(hw, fdir_info, &rule->key_conf);
return ret;
}
ret = hns3_fdir_filter_lookup(fdir_info, &rule->key_conf);
if (ret >= 0) {
hns3_err(hw, "Conflict with existing fdir loc: %d", ret);
return -EINVAL;
}
node = rte_zmalloc("hns3 fdir rule", sizeof(struct hns3_fdir_rule_ele),
0);
if (node == NULL) {
hns3_err(hw, "Failed to allocate fdir_rule memory");
return -ENOMEM;
}
rte_memcpy(&node->fdir_conf, rule, sizeof(struct hns3_fdir_rule));
ret = hns3_insert_fdir_filter(hw, fdir_info, node);
if (ret < 0) {
rte_free(node);
return ret;
}
rule->location = ret;
node->fdir_conf.location = ret;
rte_spinlock_lock(&fdir_info->flows_lock);
ret = hns3_config_action(hw, rule);
if (!ret)
ret = hns3_config_key(hns, rule);
rte_spinlock_unlock(&fdir_info->flows_lock);
if (ret) {
hns3_err(hw, "Failed to config fdir: %d src_ip:%x dst_ip:%x "
"src_port:%d dst_port:%d",
rule->location,
rule->key_conf.spec.src_ip[IP_ADDR_KEY_ID],
rule->key_conf.spec.dst_ip[IP_ADDR_KEY_ID],
rule->key_conf.spec.src_port,
rule->key_conf.spec.dst_port);
(void)hns3_remove_fdir_filter(hw, fdir_info, &rule->key_conf);
}
return ret;
}
/* remove all the flow director filters */
int hns3_clear_all_fdir_filter(struct hns3_adapter *hns)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_fdir_info *fdir_info = &pf->fdir;
struct hns3_fdir_rule_ele *fdir_filter;
struct hns3_hw *hw = &hns->hw;
int ret = 0;
/* flush flow director */
rte_spinlock_lock(&fdir_info->flows_lock);
rte_hash_reset(fdir_info->hash_handle);
rte_spinlock_unlock(&fdir_info->flows_lock);
fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list);
while (fdir_filter) {
TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries);
ret += hns3_fd_tcam_config(hw, true,
fdir_filter->fdir_conf.location,
NULL, false);
rte_free(fdir_filter);
fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list);
}
if (ret) {
hns3_err(hw, "Fail to delete FDIR filter!");
ret = -EIO;
}
return ret;
}
int hns3_restore_all_fdir_filter(struct hns3_adapter *hns)
{
struct hns3_pf *pf = &hns->pf;
struct hns3_fdir_info *fdir_info = &pf->fdir;
struct hns3_fdir_rule_ele *fdir_filter;
struct hns3_hw *hw = &hns->hw;
bool err = false;
int ret;
TAILQ_FOREACH(fdir_filter, &fdir_info->fdir_list, entries) {
ret = hns3_config_action(hw, &fdir_filter->fdir_conf);
if (!ret)
ret = hns3_config_key(hns, &fdir_filter->fdir_conf);
if (ret) {
err = true;
if (ret == -EBUSY)
break;
}
}
if (err) {
hns3_err(hw, "Fail to restore FDIR filter!");
return -EIO;
}
return 0;
}
int hns3_get_count(struct hns3_hw *hw, uint32_t id, uint64_t *value)
{
struct hns3_fd_get_cnt_cmd *req;
struct hns3_cmd_desc desc;
int ret;
hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_COUNTER_OP, true);
req = (struct hns3_fd_get_cnt_cmd *)desc.data;
req->stage = HNS3_FD_STAGE_1;
req->index = rte_cpu_to_le_32(id);
ret = hns3_cmd_send(hw, &desc, 1);
if (ret) {
hns3_err(hw, "Read counter fail, ret=%d", ret);
return ret;
}
*value = req->value;
return ret;
}