* [PATCH net-next 0/6] net: hns3: enhance tc flow offload support
@ 2026-05-18 9:35 Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 1/6] net: hns3: adjust add_cls_flower() for support more action Jijie Shao
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
This patchset enhances the tc flow offload support for hns3 driver:
- Patch 1: Refactor hclge_add_cls_flower() to support more actions
- Patch 2: Improve unused_tuple parameter setting for separate src/dst configuration
- Patch 3: Add support for HCLGE_FD_ACTION_SELECT_QUEUE and HCLGE_FD_ACTION_DROP_PACKET actions
- Patch 4: Add support for FLOW_DISSECTOR_KEY_IP and FLOW_DISSECTOR_KEY_ENC_KEYID dissectors
- Patch 5: Add debugfs support for dumping FD rules
- Patch 6: Move FD code to a separate file (hclge_fd.c) for better code organization
Jijie Shao (6):
net: hns3: adjust add_cls_flower() for support more action
net: hns3: improve the unused_tuple parameter setting
net: hns3: support two more actions for tc flow
net: hns3: support IP and tunnel VNI dissectors for tc flow
net: hns3: debugfs support for dumping fd rules
net: hns3: move fd code to a separate file
drivers/net/ethernet/hisilicon/hns3/Makefile | 1 +
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 3 +-
.../ethernet/hisilicon/hns3/hns3_debugfs.c | 6 +
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 3 +-
.../hisilicon/hns3/hns3pf/hclge_debugfs.c | 156 +
.../ethernet/hisilicon/hns3/hns3pf/hclge_fd.c | 2574 +++++++++++++++++
.../ethernet/hisilicon/hns3/hns3pf/hclge_fd.h | 33 +
.../hisilicon/hns3/hns3pf/hclge_main.c | 2514 +---------------
.../hisilicon/hns3/hns3pf/hclge_main.h | 6 +
9 files changed, 2822 insertions(+), 2474 deletions(-)
create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
--
2.33.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH net-next 1/6] net: hns3: adjust add_cls_flower() for support more action
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
@ 2026-05-18 9:35 ` Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 2/6] net: hns3: improve the unused_tuple parameter setting Jijie Shao
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
Currently, the driver supports only HCLGE_FD_ACTION_SELECT_TC configuration
in hclge_add_cls_flower(). Therefore, the tc parameter is included
in the function input parameters.
The driver plans to extend this function to support
HCLGE_FD_ACTION_SELECT_QUEUE and HCLGE_FD_ACTION_DROP_PACKET.
Therefore, the tc parameter needs to be deleted from the input parameters
of the function to ensure the universality of the function definition.
In addition, this patch moves the check of dissector->used_keys from
hclge_parse_cls_flower() to hclge_check_cls_flower().
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 2 +-
.../net/ethernet/hisilicon/hns3/hns3_enet.c | 3 +-
.../hisilicon/hns3/hns3pf/hclge_main.c | 68 ++++++++++++-------
3 files changed, 45 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index d7c3df1958f3..a724935b655a 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -778,7 +778,7 @@ struct hnae3_ae_ops {
u32 len, u8 *data);
bool (*get_cmdq_stat)(struct hnae3_handle *handle);
int (*add_cls_flower)(struct hnae3_handle *handle,
- struct flow_cls_offload *cls_flower, int tc);
+ struct flow_cls_offload *cls_flower);
int (*del_cls_flower)(struct hnae3_handle *handle,
struct flow_cls_offload *cls_flower);
bool (*cls_flower_active)(struct hnae3_handle *handle);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 4c34a144d21c..6ecb32e28e79 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -2678,13 +2678,12 @@ static int hns3_setup_tc(struct net_device *netdev, void *type_data)
static int hns3_setup_tc_cls_flower(struct hns3_nic_priv *priv,
struct flow_cls_offload *flow)
{
- int tc = tc_classid_to_hwtc(priv->netdev, flow->classid);
struct hnae3_handle *h = hns3_get_handle(priv->netdev);
switch (flow->command) {
case FLOW_CLS_REPLACE:
if (h->ae_algo->ops->add_cls_flower)
- return h->ae_algo->ops->add_cls_flower(h, flow, tc);
+ return h->ae_algo->ops->add_cls_flower(h, flow);
break;
case FLOW_CLS_DESTROY:
if (h->ae_algo->ops->del_cls_flower)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index e17b92a411a2..89fff510befd 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -7328,28 +7328,32 @@ static void hclge_get_cls_key_port(const struct flow_rule *flow,
}
}
+static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
+ struct flow_cls_offload *cls_flower,
+ struct hclge_fd_rule *rule)
+{
+ struct hnae3_handle *handle = &hdev->vport[0].nic;
+ int tc;
+
+ tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid);
+ if (tc < 0 || tc > hdev->tc_max) {
+ dev_err(&hdev->pdev->dev, "invalid traffic class: %d\n", tc);
+ return -EINVAL;
+ }
+
+ rule->action = HCLGE_FD_ACTION_SELECT_TC;
+ rule->cls_flower.tc = tc;
+ return 0;
+}
+
static int hclge_parse_cls_flower(struct hclge_dev *hdev,
struct flow_cls_offload *cls_flower,
struct hclge_fd_rule *rule)
{
struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
struct netlink_ext_ack *extack = cls_flower->common.extack;
- struct flow_dissector *dissector = flow->match.dissector;
int ret;
- if (dissector->used_keys &
- ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
- BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_PORTS))) {
- dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n",
- dissector->used_keys);
- return -EOPNOTSUPP;
- }
-
hclge_get_cls_key_basic(flow, rule);
hclge_get_cls_key_mac(flow, rule);
hclge_get_cls_key_vlan(flow, rule);
@@ -7364,15 +7368,12 @@ static int hclge_parse_cls_flower(struct hclge_dev *hdev,
}
static int hclge_check_cls_flower(struct hclge_dev *hdev,
- struct flow_cls_offload *cls_flower, int tc)
+ struct flow_cls_offload *cls_flower)
{
+ struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
+ struct flow_dissector *dissector = flow->match.dissector;
u32 prio = cls_flower->common.prio;
- if (tc < 0 || tc > hdev->tc_max) {
- dev_err(&hdev->pdev->dev, "invalid traffic class\n");
- return -EINVAL;
- }
-
if (prio == 0 ||
prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
dev_err(&hdev->pdev->dev,
@@ -7385,12 +7386,25 @@ static int hclge_check_cls_flower(struct hclge_dev *hdev,
dev_err(&hdev->pdev->dev, "prio %u is already used\n", prio);
return -EINVAL;
}
+
+ if (dissector->used_keys &
+ ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_PORTS))) {
+ dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n",
+ dissector->used_keys);
+ return -EOPNOTSUPP;
+ }
+
return 0;
}
static int hclge_add_cls_flower(struct hnae3_handle *handle,
- struct flow_cls_offload *cls_flower,
- int tc)
+ struct flow_cls_offload *cls_flower)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
@@ -7403,7 +7417,7 @@ static int hclge_add_cls_flower(struct hnae3_handle *handle,
return -EOPNOTSUPP;
}
- ret = hclge_check_cls_flower(hdev, cls_flower, tc);
+ ret = hclge_check_cls_flower(hdev, cls_flower);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to check cls flower params, ret = %d\n", ret);
@@ -7420,8 +7434,12 @@ static int hclge_add_cls_flower(struct hnae3_handle *handle,
return ret;
}
- rule->action = HCLGE_FD_ACTION_SELECT_TC;
- rule->cls_flower.tc = tc;
+ ret = hclge_get_tc_flower_action(hdev, cls_flower, rule);
+ if (ret) {
+ kfree(rule);
+ return ret;
+ }
+
rule->location = cls_flower->common.prio - 1;
rule->vf_id = 0;
rule->cls_flower.cookie = cls_flower->cookie;
--
2.33.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH net-next 2/6] net: hns3: improve the unused_tuple parameter setting
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 1/6] net: hns3: adjust add_cls_flower() for support more action Jijie Shao
@ 2026-05-18 9:35 ` Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 3/6] net: hns3: support two more actions for tc flow Jijie Shao
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
Currently, when the tc tool is used to set flow table rules, the IP address
and MAC address can be configured separately, for example, src_xx or dst_xx
can be configured separately.
Therefore, the driver needs to check whether the address is all zero in
keys, such as FLOW_DISSECTOR_KEY_IPV4_ADDRS, FLOW_DISSECTOR_KEY_IPV6_ADDRS,
and FLOW_DISSECTOR_KEY_ETH_ADDRS.
If the address is all zero, the tuple is not configured.
In this case, the driver adds the tuple to unused_tuple.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 89fff510befd..6eb4c6e4df4e 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -7242,6 +7242,10 @@ static void hclge_get_cls_key_mac(const struct flow_rule *flow,
ether_addr_copy(rule->tuples_mask.dst_mac, match.mask->dst);
ether_addr_copy(rule->tuples.src_mac, match.key->src);
ether_addr_copy(rule->tuples_mask.src_mac, match.mask->src);
+ if (is_zero_ether_addr(match.key->dst))
+ rule->unused_tuple |= BIT(INNER_DST_MAC);
+ if (is_zero_ether_addr(match.key->src))
+ rule->unused_tuple |= BIT(INNER_SRC_MAC);
} else {
rule->unused_tuple |= BIT(INNER_DST_MAC);
rule->unused_tuple |= BIT(INNER_SRC_MAC);
@@ -7290,6 +7294,10 @@ static int hclge_get_cls_key_ip(const struct flow_rule *flow,
rule->tuples.dst_ip[IPV4_INDEX] = be32_to_cpu(match.key->dst);
rule->tuples_mask.dst_ip[IPV4_INDEX] =
be32_to_cpu(match.mask->dst);
+ if (!match.key->src)
+ rule->unused_tuple |= BIT(INNER_SRC_IP);
+ if (!match.key->dst)
+ rule->unused_tuple |= BIT(INNER_DST_IP);
} else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
struct flow_match_ipv6_addrs match;
@@ -7302,6 +7310,10 @@ static int hclge_get_cls_key_ip(const struct flow_rule *flow,
match.key->dst.s6_addr32);
ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
match.mask->dst.s6_addr32);
+ if (ipv6_addr_any((struct in6_addr *)match.key->src.s6_addr32))
+ rule->unused_tuple |= BIT(INNER_SRC_IP);
+ if (ipv6_addr_any((struct in6_addr *)match.key->dst.s6_addr32))
+ rule->unused_tuple |= BIT(INNER_DST_IP);
} else {
rule->unused_tuple |= BIT(INNER_SRC_IP);
rule->unused_tuple |= BIT(INNER_DST_IP);
--
2.33.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH net-next 3/6] net: hns3: support two more actions for tc flow
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 1/6] net: hns3: adjust add_cls_flower() for support more action Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 2/6] net: hns3: improve the unused_tuple parameter setting Jijie Shao
@ 2026-05-18 9:35 ` Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 4/6] net: hns3: support IP and tunnel VNI dissectors " Jijie Shao
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
Currently, the driver supports only one action:HCLGE_FD_ACTION_SELECT_TC.
This patch adds support for HCLGE_FD_ACTION_SELECT_QUEUE and
HCLGE_FD_ACTION_DROP_PACKET.
A rule can have only one action. Therefore, the driver intercepts rules
that have multiple actions or no action.
Note: The driver considers cls_flower->classid as an action:
HCLGE_FD_ACTION_SELECT_TC.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../hisilicon/hns3/hns3pf/hclge_main.c | 59 ++++++++++++++++---
1 file changed, 52 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 6eb4c6e4df4e..bbbc017a0fb7 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -7344,18 +7344,45 @@ static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
struct flow_cls_offload *cls_flower,
struct hclge_fd_rule *rule)
{
+ struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
struct hnae3_handle *handle = &hdev->vport[0].nic;
+ struct flow_action *action = &flow->action;
+ struct flow_action_entry *act;
int tc;
- tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid);
- if (tc < 0 || tc > hdev->tc_max) {
- dev_err(&hdev->pdev->dev, "invalid traffic class: %d\n", tc);
- return -EINVAL;
+ if (!flow_action_has_entries(&flow->action)) {
+ tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid);
+ if (tc < 0 || tc > hdev->tc_max) {
+ dev_err(&hdev->pdev->dev,
+ "invalid traffic class: %d\n", tc);
+ return -EINVAL;
+ }
+
+ rule->action = HCLGE_FD_ACTION_SELECT_TC;
+ rule->cls_flower.tc = tc;
+ return 0;
}
- rule->action = HCLGE_FD_ACTION_SELECT_TC;
- rule->cls_flower.tc = tc;
- return 0;
+ act = &action->entries[0];
+ switch (act->id) {
+ case FLOW_ACTION_RX_QUEUE_MAPPING:
+ if (act->rx_queue >= handle->kinfo.num_tqps) {
+ dev_err(&hdev->pdev->dev,
+ "queue id (%u) should be less than %u\n",
+ act->rx_queue, handle->kinfo.num_tqps - 1);
+ return -EINVAL;
+ }
+
+ rule->queue_id = act->rx_queue;
+ rule->action = HCLGE_FD_ACTION_SELECT_QUEUE;
+ return 0;
+ case FLOW_ACTION_DROP:
+ rule->action = HCLGE_FD_ACTION_DROP_PACKET;
+ return 0;
+ default:
+ dev_err(&hdev->pdev->dev, "unsupported action(%d)\n", act->id);
+ return -EOPNOTSUPP;
+ }
}
static int hclge_parse_cls_flower(struct hclge_dev *hdev,
@@ -7412,6 +7439,24 @@ static int hclge_check_cls_flower(struct hclge_dev *hdev,
return -EOPNOTSUPP;
}
+ /* driver will parses classid into an action */
+ if (cls_flower->classid && flow_action_has_entries(&flow->action)) {
+ dev_err(&hdev->pdev->dev,
+ "please not set classid and action together\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) {
+ dev_err(&hdev->pdev->dev, "please set action or classid\n");
+ return -EINVAL;
+ }
+
+ if (flow_action_has_entries(&flow->action) &&
+ !flow_offload_has_one_action(&flow->action)) {
+ dev_err(&hdev->pdev->dev, "unsupported multiple actions\n");
+ return -EOPNOTSUPP;
+ }
+
return 0;
}
--
2.33.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH net-next 4/6] net: hns3: support IP and tunnel VNI dissectors for tc flow
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
` (2 preceding siblings ...)
2026-05-18 9:35 ` [PATCH net-next 3/6] net: hns3: support two more actions for tc flow Jijie Shao
@ 2026-05-18 9:35 ` Jijie Shao
2026-05-20 1:48 ` Jakub Kicinski
2026-05-18 9:35 ` [PATCH net-next 5/6] net: hns3: debugfs support for dumping fd rules Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 6/6] net: hns3: move fd code to a separate file Jijie Shao
5 siblings, 1 reply; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
Currently, the driver does not support FLOW_DISSECTOR_KEY_IP and
FLOW_DISSECTOR_KEY_ENC_KEYID. But the hardware supports
ip_tos (FLOW_DISSECTOR_KEY_IP) and
outer_tun_vni (FLOW_DISSECTOR_KEY_ENC_KEYID).
This patch adds support for FLOW_DISSECTOR_KEY_IP and
FLOW_DISSECTOR_KEY_ENC_KEYID.
Additionally, since tc flow cannot effectively support
l2_user_def, l3_user_def, and l4_user_def,
this patch explicitly sets them to not be used.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
.../hisilicon/hns3/hns3pf/hclge_main.c | 78 +++++++++++++++++--
.../hisilicon/hns3/hns3pf/hclge_main.h | 4 +
2 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index bbbc017a0fb7..35380dbd1ecf 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -16,7 +16,7 @@
#include <linux/crash_dump.h>
#include <net/rtnetlink.h>
-
+#include <net/vxlan.h>
#include "hclge_cmd.h"
#include "hclge_dcb.h"
#include "hclge_main.h"
@@ -337,7 +337,9 @@ static const struct key_info tuple_key_info[] = {
{ OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 },
- { OUTER_TUN_VNI, 24, KEY_OPT_VNI, -1, -1 },
+ { OUTER_TUN_VNI, 24, KEY_OPT_VNI,
+ offsetof(struct hclge_fd_rule, tuples.outer_tun_vni),
+ offsetof(struct hclge_fd_rule, tuples_mask.outer_tun_vni) },
{ OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 },
{ INNER_DST_MAC, 48, KEY_OPT_MAC,
offsetof(struct hclge_fd_rule, tuples.dst_mac),
@@ -5590,8 +5592,9 @@ static int hclge_init_fd_config(struct hclge_dev *hdev)
/* If use max 400bit key, we can support tuples for ether type */
if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
- key_cfg->tuple_active |=
- BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC);
+ key_cfg->tuple_active |= BIT(INNER_DST_MAC) |
+ BIT(INNER_SRC_MAC) |
+ BIT(OUTER_TUN_VNI);
if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3)
key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
}
@@ -5760,6 +5763,17 @@ static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y,
*(__le32 *)key_x = cpu_to_le32(tmp_x_l);
*(__le32 *)key_y = cpu_to_le32(tmp_y_l);
+ return true;
+ case KEY_OPT_VNI:
+ calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ for (i = 0; i < HCLGE_VNI_LENGTH; i++) {
+ key_x[i] = (cpu_to_le32(tmp_x_l) >>
+ (i * BITS_PER_BYTE)) & 0xFF;
+ key_y[i] = (cpu_to_le32(tmp_y_l) >>
+ (i * BITS_PER_BYTE)) & 0xFF;
+ }
+
return true;
default:
return false;
@@ -7340,6 +7354,52 @@ static void hclge_get_cls_key_port(const struct flow_rule *flow,
}
}
+static int hclge_get_cls_enc_keyid(struct hclge_dev *hdev,
+ const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+ struct flow_match_enc_keyid match;
+
+ flow_rule_match_enc_keyid(flow, &match);
+
+ /* vni is only 24 bits and must be greater than 0,
+ * and it can not be masked.
+ */
+ if (match.mask->keyid != HCLGE_FD_VXLAN_VNI_UNMASK ||
+ be32_to_cpu(match.key->keyid) >= VXLAN_N_VID ||
+ !match.key->keyid) {
+ dev_err(&hdev->pdev->dev, "invalid enc_keyid\n");
+ return -EINVAL;
+ }
+
+ rule->tuples.outer_tun_vni = be32_to_cpu(match.key->keyid);
+ rule->tuples_mask.outer_tun_vni =
+ be32_to_cpu(match.mask->keyid);
+ } else {
+ rule->unused_tuple |= BIT(OUTER_TUN_VNI);
+ }
+
+ return 0;
+}
+
+static void hclge_get_cls_key_ip_tos(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) {
+ struct flow_match_ip match;
+
+ flow_rule_match_ip(flow, &match);
+
+ rule->tuples.ip_tos = match.key->tos;
+ rule->tuples_mask.ip_tos = match.mask->tos;
+ if (!rule->tuples.ip_tos)
+ rule->unused_tuple |= BIT(INNER_IP_TOS);
+ } else {
+ rule->unused_tuple |= BIT(INNER_IP_TOS);
+ }
+}
+
static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
struct flow_cls_offload *cls_flower,
struct hclge_fd_rule *rule)
@@ -7393,6 +7453,9 @@ static int hclge_parse_cls_flower(struct hclge_dev *hdev,
struct netlink_ext_ack *extack = cls_flower->common.extack;
int ret;
+ /* not support any user def tuples */
+ rule->unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
+
hclge_get_cls_key_basic(flow, rule);
hclge_get_cls_key_mac(flow, rule);
hclge_get_cls_key_vlan(flow, rule);
@@ -7402,8 +7465,9 @@ static int hclge_parse_cls_flower(struct hclge_dev *hdev,
return ret;
hclge_get_cls_key_port(flow, rule);
+ hclge_get_cls_key_ip_tos(flow, rule);
- return 0;
+ return hclge_get_cls_enc_keyid(hdev, flow, rule);
}
static int hclge_check_cls_flower(struct hclge_dev *hdev,
@@ -7433,7 +7497,9 @@ static int hclge_check_cls_flower(struct hclge_dev *hdev,
BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_PORTS))) {
+ BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n",
dissector->used_keys);
return -EOPNOTSUPP;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 032b472d2368..ccb19d960690 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -645,6 +645,9 @@ struct key_info {
#define HCLGE_FD_USER_DEF_DATA GENMASK(15, 0)
#define HCLGE_FD_USER_DEF_OFFSET GENMASK(15, 0)
#define HCLGE_FD_USER_DEF_OFFSET_UNMASK GENMASK(15, 0)
+#define HCLGE_FD_VXLAN_VNI_UNMASK GENMASK(31, 0)
+
+#define HCLGE_VNI_LENGTH 3
/* assigned by firmware, the real filter number for each pf may be less */
#define MAX_FD_FILTER_NUM 4096
@@ -738,6 +741,7 @@ struct hclge_fd_rule_tuples {
u32 l4_user_def;
u8 ip_tos;
u8 ip_proto;
+ u32 outer_tun_vni;
};
struct hclge_fd_rule {
--
2.33.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH net-next 5/6] net: hns3: debugfs support for dumping fd rules
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
` (3 preceding siblings ...)
2026-05-18 9:35 ` [PATCH net-next 4/6] net: hns3: support IP and tunnel VNI dissectors " Jijie Shao
@ 2026-05-18 9:35 ` Jijie Shao
2026-05-20 1:49 ` Jakub Kicinski
2026-05-18 9:35 ` [PATCH net-next 6/6] net: hns3: move fd code to a separate file Jijie Shao
5 siblings, 1 reply; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
Currently, the tc tool only supports adding and deleting rules from
the driver but does not support querying rules from the driver.
This patch adds a rule dump file in debugfs to check whether the driver's
configuration matches the configuration issued by tc flow.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
drivers/net/ethernet/hisilicon/hns3/hnae3.h | 1 +
.../ethernet/hisilicon/hns3/hns3_debugfs.c | 6 +
.../hisilicon/hns3/hns3pf/hclge_debugfs.c | 156 ++++++++++++++++++
3 files changed, 163 insertions(+)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index a724935b655a..a8798eecd9fb 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -331,6 +331,7 @@ enum hnae3_dbg_cmd {
HNAE3_DBG_CMD_TX_QUEUE_INFO,
HNAE3_DBG_CMD_FD_TCAM,
HNAE3_DBG_CMD_FD_COUNTER,
+ HNAE3_DBG_CMD_FD_RULE,
HNAE3_DBG_CMD_MAC_TNL_STATUS,
HNAE3_DBG_CMD_SERV_INFO,
HNAE3_DBG_CMD_UMV_INFO,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
index 4cce4f4ba6b0..1347edac7699 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
@@ -273,6 +273,12 @@ static struct hns3_dbg_cmd_info hns3_dbg_cmd[] = {
.dentry = HNS3_DBG_DENTRY_FD,
.init = hns3_dbg_common_init_t2,
},
+ {
+ .name = "fd_rule",
+ .cmd = HNAE3_DBG_CMD_FD_RULE,
+ .dentry = HNS3_DBG_DENTRY_FD,
+ .init = hns3_dbg_common_init_t2,
+ },
{
.name = "service_task_info",
.cmd = HNAE3_DBG_CMD_SERV_INFO,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
index 3dab3a271aa6..d5009809420c 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
@@ -2121,6 +2121,158 @@ static int hclge_dbg_dump_fd_counter(struct seq_file *s, void *data)
return 0;
}
+static void hclge_fd_dump_u32(struct seq_file *s,
+ struct hclge_fd_rule *rule,
+ u32 type, const char *name,
+ const char *fmt, u32 key, u32 mask)
+{
+ if (rule->unused_tuple & BIT(type))
+ return;
+
+ seq_printf(s, "\t\t%s: ", name);
+ seq_printf(s, fmt, key);
+ seq_putc(s, '\n');
+
+ seq_printf(s, "\t\t%s_mask: ", name);
+ seq_printf(s, fmt, mask);
+ seq_putc(s, '\n');
+}
+
+static void hclge_fd_dump_ptr(struct seq_file *s,
+ struct hclge_fd_rule *rule,
+ u32 type, const char *name,
+ const char *fmt,
+ const void *key, const void *mask)
+{
+ if (rule->unused_tuple & BIT(type))
+ return;
+
+ seq_printf(s, "\t\t%s: ", name);
+ seq_printf(s, fmt, key);
+ seq_putc(s, '\n');
+
+ seq_printf(s, "\t\t%s_mask: ", name);
+ seq_printf(s, fmt, mask);
+ seq_putc(s, '\n');
+}
+
+#define HCLGE_IS_IPV4(ip) ({ \
+ typeof(ip) _ip = (ip); \
+ (!((_ip)[0]) && !((_ip)[1]) && !((_ip)[2]) && (_ip)[IPV4_INDEX]); })
+
+static void hclge_fd_dump_ip(struct seq_file *s,
+ struct hclge_fd_rule *rule,
+ u32 type, const char *name,
+ const u32 *ip, const u32 *mask)
+{
+ u32 be_mask[IPV6_ADDR_WORDS];
+ u32 be_ip[IPV6_ADDR_WORDS];
+
+ if (rule->unused_tuple & BIT(type))
+ return;
+
+ ipv6_addr_cpu_to_be32(be_ip, ip);
+ ipv6_addr_cpu_to_be32(be_mask, mask);
+
+ if (HCLGE_IS_IPV4(ip))
+ hclge_fd_dump_ptr(s, rule, type, name, "%pI4",
+ &be_ip[IPV4_INDEX], &be_mask[IPV4_INDEX]);
+ else
+ hclge_fd_dump_ptr(s, rule, type, name, "%pI6",
+ be_ip, be_mask);
+}
+
+static void hclge_dbg_dump_fd_tuples(struct seq_file *s,
+ struct hclge_fd_rule *rule)
+{
+ seq_puts(s, "\trule tuples:\n");
+
+ hclge_fd_dump_ptr(s, rule, INNER_DST_MAC, "dst_mac", "%pM",
+ rule->tuples.dst_mac, rule->tuples_mask.dst_mac);
+ hclge_fd_dump_ptr(s, rule, INNER_SRC_MAC, "src_mac", "%pM",
+ rule->tuples.src_mac, rule->tuples_mask.src_mac);
+ hclge_fd_dump_u32(s, rule, INNER_VLAN_TAG_FST, "vlan_tag", "0x%04x",
+ rule->tuples.vlan_tag1, rule->tuples_mask.vlan_tag1);
+ hclge_fd_dump_u32(s, rule, INNER_ETH_TYPE, "ether_proto", "0x%04x",
+ rule->tuples.ether_proto,
+ rule->tuples_mask.ether_proto);
+ hclge_fd_dump_u32(s, rule, INNER_L2_RSV, "l2_user_def", "0x%04x",
+ rule->tuples.l2_user_def,
+ rule->tuples_mask.l2_user_def);
+ hclge_fd_dump_ip(s, rule, INNER_SRC_IP, "src_ip",
+ rule->tuples.src_ip, rule->tuples_mask.src_ip);
+ hclge_fd_dump_ip(s, rule, INNER_DST_IP, "dst_ip",
+ rule->tuples.dst_ip, rule->tuples_mask.dst_ip);
+ hclge_fd_dump_u32(s, rule, INNER_IP_TOS, "ip_tos", "0x%02x",
+ rule->tuples.ip_tos, rule->tuples_mask.ip_tos);
+ hclge_fd_dump_u32(s, rule, INNER_IP_PROTO, "ip_proto", "0x%02x",
+ rule->tuples.ip_proto, rule->tuples_mask.ip_proto);
+ hclge_fd_dump_u32(s, rule, INNER_L3_RSV, "l3_user_def", "0x%04x",
+ rule->tuples.l3_user_def,
+ rule->tuples_mask.l3_user_def);
+ hclge_fd_dump_u32(s, rule, INNER_SRC_PORT, "src_port", "0x%04x",
+ rule->tuples.src_port, rule->tuples_mask.src_port);
+ hclge_fd_dump_u32(s, rule, INNER_DST_PORT, "dst_port", "0x%04x",
+ rule->tuples.dst_port, rule->tuples_mask.dst_port);
+ hclge_fd_dump_u32(s, rule, INNER_L4_RSV, "l4_user_def", "0x%08x",
+ rule->tuples.l4_user_def,
+ rule->tuples_mask.l4_user_def);
+ hclge_fd_dump_u32(s, rule, OUTER_TUN_VNI, "outer_tun_vni", "0x%06x",
+ rule->tuples.outer_tun_vni,
+ rule->tuples_mask.outer_tun_vni);
+}
+
+static void hclge_dbg_dump_fd_action(struct seq_file *s,
+ struct hclge_fd_rule *rule)
+{
+ static const char * const action_str[] = {
+ [HCLGE_FD_ACTION_SELECT_QUEUE] = "select_queue",
+ [HCLGE_FD_ACTION_DROP_PACKET] = "drop_packet",
+ [HCLGE_FD_ACTION_SELECT_TC] = "select_tc",
+ };
+
+ seq_printf(s, "\taction: %s\n", action_str[rule->action]);
+
+ if (rule->action == HCLGE_FD_ACTION_SELECT_QUEUE)
+ seq_printf(s, "\tqueue_id: %u\n", rule->queue_id);
+ else if (rule->action == HCLGE_FD_ACTION_SELECT_TC)
+ seq_printf(s, "\ttc: %u\n", rule->cls_flower.tc);
+}
+
+static void hclge_dbg_dump_fd_type(struct hclge_dev *hdev, struct seq_file *s)
+{
+ static const char *const rule_type_str[] = {
+ [HCLGE_FD_RULE_NONE] = "none",
+ [HCLGE_FD_ARFS_ACTIVE] = "arfs",
+ [HCLGE_FD_EP_ACTIVE] = "ep",
+ [HCLGE_FD_TC_FLOWER_ACTIVE] = "tc_flow"
+ };
+
+ seq_printf(s, "fd type: %s\n", rule_type_str[hdev->fd_active_type]);
+}
+
+static int hclge_dbg_dump_fd_rule(struct seq_file *s, void *data)
+{
+ struct hclge_dev *hdev = hclge_seq_file_to_hdev(s);
+ struct hclge_fd_rule *rule;
+
+ hclge_dbg_dump_fd_type(hdev, s);
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ hlist_for_each_entry(rule, &hdev->fd_rule_list, rule_node) {
+ if (rule->state != HCLGE_FD_ACTIVE)
+ continue;
+
+ seq_printf(s, "location: %u\n", rule->location);
+ seq_printf(s, "vport_id: %u\n", rule->vf_id);
+ hclge_dbg_dump_fd_action(s, rule);
+ hclge_dbg_dump_fd_tuples(s, rule);
+ }
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ return 0;
+}
+
static const struct hclge_dbg_status_dfx_info hclge_dbg_rst_info[] = {
{HCLGE_MISC_VECTOR_REG_BASE, "vector0 interrupt enable status"},
{HCLGE_MISC_RESET_STS_REG, "reset interrupt source"},
@@ -2913,6 +3065,10 @@ static const struct hclge_dbg_func hclge_dbg_cmd_func[] = {
.cmd = HNAE3_DBG_CMD_FD_TCAM,
.dbg_read_func = hclge_dbg_dump_fd_tcam,
},
+ {
+ .cmd = HNAE3_DBG_CMD_FD_RULE,
+ .dbg_read_func = hclge_dbg_dump_fd_rule,
+ },
{
.cmd = HNAE3_DBG_CMD_MAC_TNL_STATUS,
.dbg_read_func = hclge_dbg_dump_mac_tnl_status,
--
2.33.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH net-next 6/6] net: hns3: move fd code to a separate file
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
` (4 preceding siblings ...)
2026-05-18 9:35 ` [PATCH net-next 5/6] net: hns3: debugfs support for dumping fd rules Jijie Shao
@ 2026-05-18 9:35 ` Jijie Shao
5 siblings, 0 replies; 9+ messages in thread
From: Jijie Shao @ 2026-05-18 9:35 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: shenjian15, liuyonglong, chenhao418, huangdonghua3, yangshuaisong,
netdev, linux-kernel, shaojijie
The hclge_main.c file has become very large,
so the fd code has been moved to a separate hclge_fd.c file.
This patch only moves the code and does not modify any functionality.
Signed-off-by: Jijie Shao <shaojijie@huawei.com>
---
drivers/net/ethernet/hisilicon/hns3/Makefile | 1 +
.../ethernet/hisilicon/hns3/hns3pf/hclge_fd.c | 2574 ++++++++++++++++
.../ethernet/hisilicon/hns3/hns3pf/hclge_fd.h | 33 +
.../hisilicon/hns3/hns3pf/hclge_main.c | 2655 +----------------
.../hisilicon/hns3/hns3pf/hclge_main.h | 2 +
5 files changed, 2653 insertions(+), 2612 deletions(-)
create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
create mode 100644 drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile
index e8af26da1fc1..5785d4c5709e 100644
--- a/drivers/net/ethernet/hisilicon/hns3/Makefile
+++ b/drivers/net/ethernet/hisilicon/hns3/Makefile
@@ -24,5 +24,6 @@ hclgevf-objs = hns3vf/hclgevf_main.o hns3vf/hclgevf_mbx.o hns3vf/hclgevf_devlin
obj-$(CONFIG_HNS3_HCLGE) += hclge.o hclge-common.o
hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hclge_regs.o \
hns3pf/hclge_mbx.o hns3pf/hclge_err.o hns3pf/hclge_debugfs.o hns3pf/hclge_ptp.o hns3pf/hclge_devlink.o \
+ hns3pf/hclge_fd.o
hclge-$(CONFIG_HNS3_DCB) += hns3pf/hclge_dcb.o
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
new file mode 100644
index 000000000000..ee3864cca5b9
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c
@@ -0,0 +1,2574 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright (c) 2026 Hisilicon Limited.
+
+#include <linux/ethtool.h>
+#include <net/flow_offload.h>
+#include <net/vxlan.h>
+#include "hclge_fd.h"
+#include "hclge_main.h"
+
+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, KEY_OPT_MAC, -1, -1 },
+ { OUTER_SRC_MAC, 48, KEY_OPT_MAC, -1, -1 },
+ { OUTER_VLAN_TAG_FST, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_ETH_TYPE, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_L2_RSV, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_IP_TOS, 8, KEY_OPT_U8, -1, -1 },
+ { OUTER_IP_PROTO, 8, KEY_OPT_U8, -1, -1 },
+ { OUTER_SRC_IP, 32, KEY_OPT_IP, -1, -1 },
+ { OUTER_DST_IP, 32, KEY_OPT_IP, -1, -1 },
+ { OUTER_L3_RSV, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 },
+ { OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 },
+ { OUTER_TUN_VNI, 24, KEY_OPT_VNI,
+ offsetof(struct hclge_fd_rule, tuples.outer_tun_vni),
+ offsetof(struct hclge_fd_rule, tuples_mask.outer_tun_vni) },
+ { OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 },
+ { INNER_DST_MAC, 48, KEY_OPT_MAC,
+ offsetof(struct hclge_fd_rule, tuples.dst_mac),
+ offsetof(struct hclge_fd_rule, tuples_mask.dst_mac) },
+ { INNER_SRC_MAC, 48, KEY_OPT_MAC,
+ offsetof(struct hclge_fd_rule, tuples.src_mac),
+ offsetof(struct hclge_fd_rule, tuples_mask.src_mac) },
+ { INNER_VLAN_TAG_FST, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.vlan_tag1),
+ offsetof(struct hclge_fd_rule, tuples_mask.vlan_tag1) },
+ { INNER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
+ { INNER_ETH_TYPE, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.ether_proto),
+ offsetof(struct hclge_fd_rule, tuples_mask.ether_proto) },
+ { INNER_L2_RSV, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.l2_user_def),
+ offsetof(struct hclge_fd_rule, tuples_mask.l2_user_def) },
+ { INNER_IP_TOS, 8, KEY_OPT_U8,
+ offsetof(struct hclge_fd_rule, tuples.ip_tos),
+ offsetof(struct hclge_fd_rule, tuples_mask.ip_tos) },
+ { INNER_IP_PROTO, 8, KEY_OPT_U8,
+ offsetof(struct hclge_fd_rule, tuples.ip_proto),
+ offsetof(struct hclge_fd_rule, tuples_mask.ip_proto) },
+ { INNER_SRC_IP, 32, KEY_OPT_IP,
+ offsetof(struct hclge_fd_rule, tuples.src_ip),
+ offsetof(struct hclge_fd_rule, tuples_mask.src_ip) },
+ { INNER_DST_IP, 32, KEY_OPT_IP,
+ offsetof(struct hclge_fd_rule, tuples.dst_ip),
+ offsetof(struct hclge_fd_rule, tuples_mask.dst_ip) },
+ { INNER_L3_RSV, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.l3_user_def),
+ offsetof(struct hclge_fd_rule, tuples_mask.l3_user_def) },
+ { INNER_SRC_PORT, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.src_port),
+ offsetof(struct hclge_fd_rule, tuples_mask.src_port) },
+ { INNER_DST_PORT, 16, KEY_OPT_LE16,
+ offsetof(struct hclge_fd_rule, tuples.dst_port),
+ offsetof(struct hclge_fd_rule, tuples_mask.dst_port) },
+ { INNER_L4_RSV, 32, KEY_OPT_LE32,
+ offsetof(struct hclge_fd_rule, tuples.l4_user_def),
+ offsetof(struct hclge_fd_rule, tuples_mask.l4_user_def) },
+};
+
+static void hclge_sync_fd_state(struct hclge_dev *hdev)
+{
+ if (hlist_empty(&hdev->fd_rule_list))
+ hdev->fd_active_type = HCLGE_FD_RULE_NONE;
+}
+
+static void hclge_fd_inc_rule_cnt(struct hclge_dev *hdev, u16 location)
+{
+ if (!test_bit(location, hdev->fd_bmap)) {
+ set_bit(location, hdev->fd_bmap);
+ hdev->hclge_fd_rule_num++;
+ }
+}
+
+static void hclge_fd_dec_rule_cnt(struct hclge_dev *hdev, u16 location)
+{
+ if (test_bit(location, hdev->fd_bmap)) {
+ clear_bit(location, hdev->fd_bmap);
+ hdev->hclge_fd_rule_num--;
+ }
+}
+
+static void hclge_fd_free_node(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ hlist_del(&rule->rule_node);
+ kfree(rule);
+ hclge_sync_fd_state(hdev);
+}
+
+static void hclge_update_fd_rule_node(struct hclge_dev *hdev,
+ struct hclge_fd_rule *old_rule,
+ struct hclge_fd_rule *new_rule,
+ enum HCLGE_FD_NODE_STATE state)
+{
+ switch (state) {
+ case HCLGE_FD_TO_ADD:
+ case HCLGE_FD_ACTIVE:
+ /* 1) if the new state is TO_ADD, just replace the old rule
+ * with the same location, no matter its state, because the
+ * new rule will be configured to the hardware.
+ * 2) if the new state is ACTIVE, it means the new rule
+ * has been configured to the hardware, so just replace
+ * the old rule node with the same location.
+ * 3) for it doesn't add a new node to the list, so it's
+ * unnecessary to update the rule number and fd_bmap.
+ */
+ new_rule->rule_node.next = old_rule->rule_node.next;
+ new_rule->rule_node.pprev = old_rule->rule_node.pprev;
+ memcpy(old_rule, new_rule, sizeof(*old_rule));
+ kfree(new_rule);
+ break;
+ case HCLGE_FD_DELETED:
+ hclge_fd_dec_rule_cnt(hdev, old_rule->location);
+ hclge_fd_free_node(hdev, old_rule);
+ break;
+ case HCLGE_FD_TO_DEL:
+ /* if new request is TO_DEL, and old rule is existent
+ * 1) the state of old rule is TO_DEL, we need do nothing,
+ * because we delete rule by location, other rule content
+ * is unnecessary.
+ * 2) the state of old rule is ACTIVE, we need to change its
+ * state to TO_DEL, so the rule will be deleted when periodic
+ * task being scheduled.
+ * 3) the state of old rule is TO_ADD, it means the rule hasn't
+ * been added to hardware, so we just delete the rule node from
+ * fd_rule_list directly.
+ */
+ if (old_rule->state == HCLGE_FD_TO_ADD) {
+ hclge_fd_dec_rule_cnt(hdev, old_rule->location);
+ hclge_fd_free_node(hdev, old_rule);
+ return;
+ }
+ old_rule->state = HCLGE_FD_TO_DEL;
+ break;
+ }
+}
+
+static struct hclge_fd_rule *hclge_find_fd_rule(struct hlist_head *hlist,
+ u16 location,
+ struct hclge_fd_rule **parent)
+{
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+
+ hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
+ if (rule->location == location)
+ return rule;
+ else if (rule->location > location)
+ return NULL;
+ /* record the parent node, use to keep the nodes in fd_rule_list
+ * in ascend order.
+ */
+ *parent = rule;
+ }
+
+ return NULL;
+}
+
+/* insert fd rule node in ascend order according to rule->location */
+static void hclge_fd_insert_rule_node(struct hlist_head *hlist,
+ struct hclge_fd_rule *rule,
+ struct hclge_fd_rule *parent)
+{
+ INIT_HLIST_NODE(&rule->rule_node);
+
+ if (parent)
+ hlist_add_behind(&rule->rule_node, &parent->rule_node);
+ else
+ hlist_add_head(&rule->rule_node, hlist);
+}
+
+static int hclge_fd_set_user_def_cmd(struct hclge_dev *hdev,
+ struct hclge_fd_user_def_cfg *cfg)
+{
+ struct hclge_fd_user_def_cfg_cmd *req;
+ struct hclge_desc desc;
+ u16 data = 0;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false);
+
+ req = (struct hclge_fd_user_def_cfg_cmd *)desc.data;
+
+ hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[0].ref_cnt > 0);
+ hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
+ HCLGE_FD_USER_DEF_OFT_S, cfg[0].offset);
+ req->ol2_cfg = cpu_to_le16(data);
+
+ data = 0;
+ hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[1].ref_cnt > 0);
+ hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
+ HCLGE_FD_USER_DEF_OFT_S, cfg[1].offset);
+ req->ol3_cfg = cpu_to_le16(data);
+
+ data = 0;
+ hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[2].ref_cnt > 0);
+ hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
+ HCLGE_FD_USER_DEF_OFT_S, cfg[2].offset);
+ req->ol4_cfg = cpu_to_le16(data);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to set fd user def data, ret= %d\n", ret);
+ return ret;
+}
+
+static void hclge_sync_fd_user_def_cfg(struct hclge_dev *hdev, bool locked)
+{
+ int ret;
+
+ if (!test_and_clear_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state))
+ return;
+
+ if (!locked)
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ ret = hclge_fd_set_user_def_cmd(hdev, hdev->fd_cfg.user_def_cfg);
+ if (ret)
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+
+ if (!locked)
+ spin_unlock_bh(&hdev->fd_rule_lock);
+}
+
+static int hclge_fd_check_user_def_refcnt(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ struct hlist_head *hlist = &hdev->fd_rule_list;
+ struct hclge_fd_rule *fd_rule, *parent = NULL;
+ struct hclge_fd_user_def_info *info, *old_info;
+ struct hclge_fd_user_def_cfg *cfg;
+
+ if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
+ rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
+ return 0;
+
+ /* for valid layer is start from 1, so need minus 1 to get the cfg */
+ cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
+ info = &rule->ep.user_def;
+
+ if (!cfg->ref_cnt || cfg->offset == info->offset)
+ return 0;
+
+ if (cfg->ref_cnt > 1)
+ goto error;
+
+ fd_rule = hclge_find_fd_rule(hlist, rule->location, &parent);
+ if (fd_rule) {
+ old_info = &fd_rule->ep.user_def;
+ if (info->layer == old_info->layer)
+ return 0;
+ }
+
+error:
+ dev_err(&hdev->pdev->dev,
+ "No available offset for layer%d fd rule, each layer only support one user def offset.\n",
+ info->layer + 1);
+ return -ENOSPC;
+}
+
+static void hclge_fd_inc_user_def_refcnt(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ struct hclge_fd_user_def_cfg *cfg;
+
+ if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
+ rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
+ return;
+
+ cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
+ if (!cfg->ref_cnt) {
+ cfg->offset = rule->ep.user_def.offset;
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+ }
+ cfg->ref_cnt++;
+}
+
+static void hclge_fd_dec_user_def_refcnt(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ struct hclge_fd_user_def_cfg *cfg;
+
+ if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
+ rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
+ return;
+
+ cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
+ if (!cfg->ref_cnt)
+ return;
+
+ cfg->ref_cnt--;
+ if (!cfg->ref_cnt) {
+ cfg->offset = 0;
+ set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+ }
+}
+
+static void hclge_update_fd_list(struct hclge_dev *hdev,
+ enum HCLGE_FD_NODE_STATE state, u16 location,
+ struct hclge_fd_rule *new_rule)
+{
+ struct hlist_head *hlist = &hdev->fd_rule_list;
+ struct hclge_fd_rule *fd_rule, *parent = NULL;
+
+ fd_rule = hclge_find_fd_rule(hlist, location, &parent);
+ if (fd_rule) {
+ hclge_fd_dec_user_def_refcnt(hdev, fd_rule);
+ if (state == HCLGE_FD_ACTIVE)
+ hclge_fd_inc_user_def_refcnt(hdev, new_rule);
+ hclge_sync_fd_user_def_cfg(hdev, true);
+
+ hclge_update_fd_rule_node(hdev, fd_rule, new_rule, state);
+ return;
+ }
+
+ /* it's unlikely to fail here, because we have checked the rule
+ * exist before.
+ */
+ if (unlikely(state == HCLGE_FD_TO_DEL || state == HCLGE_FD_DELETED)) {
+ dev_warn(&hdev->pdev->dev,
+ "failed to delete fd rule %u, it's inexistent\n",
+ location);
+ return;
+ }
+
+ hclge_fd_inc_user_def_refcnt(hdev, new_rule);
+ hclge_sync_fd_user_def_cfg(hdev, true);
+
+ hclge_fd_insert_rule_node(hlist, new_rule, parent);
+ hclge_fd_inc_rule_cnt(hdev, new_rule->location);
+
+ if (state == HCLGE_FD_TO_ADD) {
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+ hclge_task_schedule(hdev, 0);
+ }
+}
+
+static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode)
+{
+ struct hclge_get_fd_mode_cmd *req;
+ struct hclge_desc desc;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true);
+
+ req = (struct hclge_get_fd_mode_cmd *)desc.data;
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret) {
+ dev_err(&hdev->pdev->dev, "get fd mode fail, ret=%d\n", ret);
+ return ret;
+ }
+
+ *fd_mode = req->mode;
+
+ return ret;
+}
+
+static int hclge_get_fd_allocation(struct hclge_dev *hdev,
+ u32 *stage1_entry_num,
+ u32 *stage2_entry_num,
+ u16 *stage1_counter_num,
+ u16 *stage2_counter_num)
+{
+ struct hclge_get_fd_allocation_cmd *req;
+ struct hclge_desc desc;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_GET_ALLOCATION, true);
+
+ req = (struct hclge_get_fd_allocation_cmd *)desc.data;
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret) {
+ dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ *stage1_entry_num = le32_to_cpu(req->stage1_entry_num);
+ *stage2_entry_num = le32_to_cpu(req->stage2_entry_num);
+ *stage1_counter_num = le16_to_cpu(req->stage1_counter_num);
+ *stage2_counter_num = le16_to_cpu(req->stage2_counter_num);
+
+ return ret;
+}
+
+static int hclge_set_fd_key_config(struct hclge_dev *hdev,
+ enum HCLGE_FD_STAGE stage_num)
+{
+ struct hclge_set_fd_key_config_cmd *req;
+ struct hclge_fd_key_cfg *stage;
+ struct hclge_desc desc;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_KEY_CONFIG, false);
+
+ req = (struct hclge_set_fd_key_config_cmd *)desc.data;
+ stage = &hdev->fd_cfg.key_cfg[stage_num];
+ req->stage = stage_num;
+ req->key_select = stage->key_sel;
+ req->inner_sipv6_word_en = stage->inner_sipv6_word_en;
+ req->inner_dipv6_word_en = stage->inner_dipv6_word_en;
+ req->outer_sipv6_word_en = stage->outer_sipv6_word_en;
+ req->outer_dipv6_word_en = stage->outer_dipv6_word_en;
+ req->tuple_mask = cpu_to_le32(~stage->tuple_active);
+ req->meta_data_mask = cpu_to_le32(~stage->meta_data_active);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev, "set fd key fail, ret=%d\n", ret);
+
+ return ret;
+}
+
+static void hclge_fd_disable_user_def(struct hclge_dev *hdev)
+{
+ struct hclge_fd_user_def_cfg *cfg = hdev->fd_cfg.user_def_cfg;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ memset(cfg, 0, sizeof(hdev->fd_cfg.user_def_cfg));
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ hclge_fd_set_user_def_cmd(hdev, cfg);
+}
+
+int hclge_init_fd_config(struct hclge_dev *hdev)
+{
+#define LOW_2_WORDS 0x03
+ struct hclge_fd_key_cfg *key_cfg;
+ int ret;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return 0;
+
+ ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode);
+ if (ret)
+ return ret;
+
+ switch (hdev->fd_cfg.fd_mode) {
+ case HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1:
+ hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH;
+ break;
+ case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1:
+ hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH / 2;
+ break;
+ default:
+ dev_err(&hdev->pdev->dev,
+ "Unsupported flow director mode %u\n",
+ hdev->fd_cfg.fd_mode);
+ return -EOPNOTSUPP;
+ }
+
+ key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1];
+ key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE;
+ key_cfg->inner_sipv6_word_en = LOW_2_WORDS;
+ key_cfg->inner_dipv6_word_en = LOW_2_WORDS;
+ key_cfg->outer_sipv6_word_en = 0;
+ key_cfg->outer_dipv6_word_en = 0;
+
+ key_cfg->tuple_active = BIT(INNER_VLAN_TAG_FST) | 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 (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
+ key_cfg->tuple_active |= BIT(INNER_DST_MAC) |
+ BIT(INNER_SRC_MAC) |
+ BIT(OUTER_TUN_VNI);
+ if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3)
+ key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
+ }
+
+ /* roce_type is used to filter roce frames
+ * dst_vport is used to specify the rule
+ */
+ key_cfg->meta_data_active = BIT(ROCE_TYPE) | BIT(DST_VPORT);
+
+ ret = hclge_get_fd_allocation(hdev,
+ &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1],
+ &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_2],
+ &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1],
+ &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_2]);
+ if (ret)
+ return ret;
+
+ return hclge_set_fd_key_config(hdev, HCLGE_FD_STAGE_1);
+}
+
+static int hclge_fd_tcam_config(struct hclge_dev *hdev, u8 stage, bool sel_x,
+ int loc, u8 *key, bool is_add)
+{
+ struct hclge_fd_tcam_config_1_cmd *req1;
+ struct hclge_fd_tcam_config_2_cmd *req2;
+ struct hclge_fd_tcam_config_3_cmd *req3;
+ struct hclge_desc desc[3];
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, false);
+ desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
+ hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false);
+ desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
+ hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false);
+
+ req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data;
+ req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data;
+ req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data;
+
+ req1->stage = stage;
+ req1->xy_sel = sel_x ? 1 : 0;
+ hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0);
+ req1->index = cpu_to_le32(loc);
+ req1->entry_vld = sel_x ? is_add : 0;
+
+ if (key) {
+ memcpy(req1->tcam_data, &key[0], sizeof(req1->tcam_data));
+ memcpy(req2->tcam_data, &key[sizeof(req1->tcam_data)],
+ sizeof(req2->tcam_data));
+ memcpy(req3->tcam_data, &key[sizeof(req1->tcam_data) +
+ sizeof(req2->tcam_data)], sizeof(req3->tcam_data));
+ }
+
+ ret = hclge_cmd_send(&hdev->hw, desc, 3);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "config tcam key fail, ret=%d\n",
+ ret);
+
+ return ret;
+}
+
+static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc,
+ struct hclge_fd_ad_data *action)
+{
+ struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
+ struct hclge_fd_ad_config_cmd *req;
+ struct hclge_desc desc;
+ u64 ad_data = 0;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false);
+
+ req = (struct hclge_fd_ad_config_cmd *)desc.data;
+ req->index = cpu_to_le32(loc);
+ req->stage = stage;
+
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_WR_RULE_ID_B,
+ action->write_rule_id_to_bd);
+ hnae3_set_field(ad_data, HCLGE_FD_AD_RULE_ID_M, HCLGE_FD_AD_RULE_ID_S,
+ action->rule_id);
+ if (test_bit(HNAE3_DEV_SUPPORT_FD_FORWARD_TC_B, ae_dev->caps)) {
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_TC_OVRD_B,
+ action->override_tc);
+ hnae3_set_field(ad_data, HCLGE_FD_AD_TC_SIZE_M,
+ HCLGE_FD_AD_TC_SIZE_S, (u32)action->tc_size);
+ }
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_QID_H_B,
+ action->queue_id >= HCLGE_TQP_MAX_SIZE_DEV_V2 ? 1 : 0);
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_COUNTER_NUM_H_B,
+ action->counter_id >= HCLGE_FD_COUNTER_MAX_SIZE_DEV_V2 ?
+ 1 : 0);
+ ad_data <<= 32;
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_DROP_B, action->drop_packet);
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_DIRECT_QID_B,
+ action->forward_to_direct_queue);
+ hnae3_set_field(ad_data, HCLGE_FD_AD_QID_L_M, HCLGE_FD_AD_QID_L_S,
+ action->queue_id);
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_USE_COUNTER_B, action->use_counter);
+ hnae3_set_field(ad_data, HCLGE_FD_AD_COUNTER_NUM_L_M,
+ HCLGE_FD_AD_COUNTER_NUM_L_S, action->counter_id);
+ hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage);
+ hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S,
+ action->next_input_key);
+
+ req->ad_data = cpu_to_le64(ad_data);
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev, "fd ad config fail, ret=%d\n", ret);
+
+ return ret;
+}
+
+static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y,
+ struct hclge_fd_rule *rule)
+{
+ int offset, moffset, ip_offset;
+ enum HCLGE_FD_KEY_OPT key_opt;
+ u16 tmp_x_s, tmp_y_s;
+ u32 tmp_x_l, tmp_y_l;
+ u8 *p = (u8 *)rule;
+ int i;
+
+ if (rule->unused_tuple & BIT(tuple_bit))
+ return true;
+
+ key_opt = tuple_key_info[tuple_bit].key_opt;
+ offset = tuple_key_info[tuple_bit].offset;
+ moffset = tuple_key_info[tuple_bit].moffset;
+
+ switch (key_opt) {
+ case KEY_OPT_U8:
+ calc_x(*key_x, p[offset], p[moffset]);
+ calc_y(*key_y, p[offset], p[moffset]);
+
+ return true;
+ case KEY_OPT_LE16:
+ calc_x(tmp_x_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
+ calc_y(tmp_y_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
+ *(__le16 *)key_x = cpu_to_le16(tmp_x_s);
+ *(__le16 *)key_y = cpu_to_le16(tmp_y_s);
+
+ return true;
+ case KEY_OPT_LE32:
+ calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ *(__le32 *)key_x = cpu_to_le32(tmp_x_l);
+ *(__le32 *)key_y = cpu_to_le32(tmp_y_l);
+
+ return true;
+ case KEY_OPT_MAC:
+ for (i = 0; i < ETH_ALEN; i++) {
+ calc_x(key_x[ETH_ALEN - 1 - i], p[offset + i],
+ p[moffset + i]);
+ calc_y(key_y[ETH_ALEN - 1 - i], p[offset + i],
+ p[moffset + i]);
+ }
+
+ return true;
+ case KEY_OPT_IP:
+ ip_offset = IPV4_INDEX * sizeof(u32);
+ calc_x(tmp_x_l, *(u32 *)(&p[offset + ip_offset]),
+ *(u32 *)(&p[moffset + ip_offset]));
+ calc_y(tmp_y_l, *(u32 *)(&p[offset + ip_offset]),
+ *(u32 *)(&p[moffset + ip_offset]));
+ *(__le32 *)key_x = cpu_to_le32(tmp_x_l);
+ *(__le32 *)key_y = cpu_to_le32(tmp_y_l);
+
+ return true;
+ case KEY_OPT_VNI:
+ calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
+ for (i = 0; i < HCLGE_VNI_LENGTH; i++) {
+ key_x[i] = (cpu_to_le32(tmp_x_l) >>
+ (i * BITS_PER_BYTE)) & 0xFF;
+ key_y[i] = (cpu_to_le32(tmp_y_l) >>
+ (i * BITS_PER_BYTE)) & 0xFF;
+ }
+
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void hclge_fd_convert_meta_data(struct hclge_fd_key_cfg *key_cfg,
+ __le32 *key_x, __le32 *key_y,
+ struct hclge_fd_rule *rule)
+{
+ u32 tuple_bit, meta_data = 0, tmp_x, tmp_y, port_number;
+ u8 cur_pos = 0, tuple_size, shift_bits;
+ unsigned int i;
+
+ for (i = 0; i < MAX_META_DATA; i++) {
+ tuple_size = meta_data_key_info[i].key_length;
+ tuple_bit = key_cfg->meta_data_active & BIT(i);
+
+ switch (tuple_bit) {
+ case BIT(ROCE_TYPE):
+ hnae3_set_bit(meta_data, cur_pos, NIC_PACKET);
+ cur_pos += tuple_size;
+ break;
+ case BIT(DST_VPORT):
+ port_number = hclge_get_port_number(HOST_PORT, 0,
+ rule->vf_id, 0);
+ hnae3_set_field(meta_data,
+ GENMASK(cur_pos + tuple_size, cur_pos),
+ cur_pos, port_number);
+ cur_pos += tuple_size;
+ break;
+ default:
+ break;
+ }
+ }
+
+ calc_x(tmp_x, meta_data, 0xFFFFFFFF);
+ calc_y(tmp_y, meta_data, 0xFFFFFFFF);
+ shift_bits = sizeof(meta_data) * 8 - cur_pos;
+
+ *key_x = cpu_to_le32(tmp_x << shift_bits);
+ *key_y = cpu_to_le32(tmp_y << shift_bits);
+}
+
+/* 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 hclge_config_key(struct hclge_dev *hdev, u8 stage,
+ struct hclge_fd_rule *rule)
+{
+ struct hclge_fd_key_cfg *key_cfg = &hdev->fd_cfg.key_cfg[stage];
+ u8 key_x[MAX_KEY_BYTES], key_y[MAX_KEY_BYTES];
+ u8 *cur_key_x, *cur_key_y;
+ u8 meta_data_region;
+ u8 tuple_size;
+ int ret;
+ u32 i;
+
+ memset(key_x, 0, sizeof(key_x));
+ memset(key_y, 0, sizeof(key_y));
+ cur_key_x = key_x;
+ cur_key_y = key_y;
+
+ for (i = 0; i < MAX_TUPLE; i++) {
+ bool tuple_valid;
+
+ tuple_size = tuple_key_info[i].key_length / 8;
+ if (!(key_cfg->tuple_active & BIT(i)))
+ continue;
+
+ tuple_valid = hclge_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 = hdev->fd_cfg.max_key_length / 8 -
+ MAX_META_DATA_LENGTH / 8;
+
+ hclge_fd_convert_meta_data(key_cfg,
+ (__le32 *)(key_x + meta_data_region),
+ (__le32 *)(key_y + meta_data_region),
+ rule);
+
+ ret = hclge_fd_tcam_config(hdev, stage, false, rule->location, key_y,
+ true);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "fd key_y config fail, loc=%u, ret=%d\n",
+ rule->queue_id, ret);
+ return ret;
+ }
+
+ ret = hclge_fd_tcam_config(hdev, stage, true, rule->location, key_x,
+ true);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "fd key_x config fail, loc=%u, ret=%d\n",
+ rule->queue_id, ret);
+ return ret;
+}
+
+static int hclge_config_action(struct hclge_dev *hdev, u8 stage,
+ struct hclge_fd_rule *rule)
+{
+ struct hclge_vport *vport = hdev->vport;
+ struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
+ struct hclge_fd_ad_data ad_data;
+
+ memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data));
+ ad_data.ad_id = rule->location;
+
+ if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
+ ad_data.drop_packet = true;
+ } else if (rule->action == HCLGE_FD_ACTION_SELECT_TC) {
+ ad_data.override_tc = true;
+ ad_data.queue_id =
+ kinfo->tc_info.tqp_offset[rule->cls_flower.tc];
+ ad_data.tc_size =
+ ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]);
+ } else {
+ ad_data.forward_to_direct_queue = true;
+ ad_data.queue_id = rule->queue_id;
+ }
+
+ if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) {
+ ad_data.use_counter = true;
+ ad_data.counter_id = rule->vf_id %
+ hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1];
+ } else {
+ ad_data.use_counter = false;
+ ad_data.counter_id = 0;
+ }
+
+ ad_data.use_next_stage = false;
+ ad_data.next_input_key = 0;
+
+ ad_data.write_rule_id_to_bd = true;
+ ad_data.rule_id = rule->location;
+
+ return hclge_fd_ad_config(hdev, stage, ad_data.ad_id, &ad_data);
+}
+
+static int hclge_fd_check_tcpip4_tuple(struct ethtool_tcpip4_spec *spec,
+ u32 *unused_tuple)
+{
+ if (!spec || !unused_tuple)
+ return -EINVAL;
+
+ *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
+
+ if (!spec->ip4src)
+ *unused_tuple |= BIT(INNER_SRC_IP);
+
+ if (!spec->ip4dst)
+ *unused_tuple |= BIT(INNER_DST_IP);
+
+ if (!spec->psrc)
+ *unused_tuple |= BIT(INNER_SRC_PORT);
+
+ if (!spec->pdst)
+ *unused_tuple |= BIT(INNER_DST_PORT);
+
+ if (!spec->tos)
+ *unused_tuple |= BIT(INNER_IP_TOS);
+
+ return 0;
+}
+
+static int hclge_fd_check_ip4_tuple(struct ethtool_usrip4_spec *spec,
+ u32 *unused_tuple)
+{
+ if (!spec || !unused_tuple)
+ return -EINVAL;
+
+ *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
+ BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
+
+ if (!spec->ip4src)
+ *unused_tuple |= BIT(INNER_SRC_IP);
+
+ if (!spec->ip4dst)
+ *unused_tuple |= BIT(INNER_DST_IP);
+
+ if (!spec->tos)
+ *unused_tuple |= BIT(INNER_IP_TOS);
+
+ if (!spec->proto)
+ *unused_tuple |= BIT(INNER_IP_PROTO);
+
+ if (spec->l4_4_bytes)
+ return -EOPNOTSUPP;
+
+ if (spec->ip_ver != ETH_RX_NFC_IP4)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec,
+ u32 *unused_tuple)
+{
+ if (!spec || !unused_tuple)
+ return -EINVAL;
+
+ *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
+
+ /* check whether src/dst ip address used */
+ if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
+ *unused_tuple |= BIT(INNER_SRC_IP);
+
+ if (ipv6_addr_any((struct in6_addr *)spec->ip6dst))
+ *unused_tuple |= BIT(INNER_DST_IP);
+
+ if (!spec->psrc)
+ *unused_tuple |= BIT(INNER_SRC_PORT);
+
+ if (!spec->pdst)
+ *unused_tuple |= BIT(INNER_DST_PORT);
+
+ if (!spec->tclass)
+ *unused_tuple |= BIT(INNER_IP_TOS);
+
+ return 0;
+}
+
+static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec,
+ u32 *unused_tuple)
+{
+ if (!spec || !unused_tuple)
+ return -EINVAL;
+
+ *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
+ BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
+
+ /* check whether src/dst ip address used */
+ if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
+ *unused_tuple |= BIT(INNER_SRC_IP);
+
+ if (ipv6_addr_any((struct in6_addr *)spec->ip6dst))
+ *unused_tuple |= BIT(INNER_DST_IP);
+
+ if (!spec->l4_proto)
+ *unused_tuple |= BIT(INNER_IP_PROTO);
+
+ if (!spec->tclass)
+ *unused_tuple |= BIT(INNER_IP_TOS);
+
+ if (spec->l4_4_bytes)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int hclge_fd_check_ether_tuple(struct ethhdr *spec, u32 *unused_tuple)
+{
+ if (!spec || !unused_tuple)
+ return -EINVAL;
+
+ *unused_tuple |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
+ BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) |
+ BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO);
+
+ if (is_zero_ether_addr(spec->h_source))
+ *unused_tuple |= BIT(INNER_SRC_MAC);
+
+ if (is_zero_ether_addr(spec->h_dest))
+ *unused_tuple |= BIT(INNER_DST_MAC);
+
+ if (!spec->h_proto)
+ *unused_tuple |= BIT(INNER_ETH_TYPE);
+
+ return 0;
+}
+
+static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ u32 *unused_tuple)
+{
+ if (fs->flow_type & FLOW_EXT) {
+ if (fs->h_ext.vlan_etype) {
+ dev_err(&hdev->pdev->dev, "vlan-etype is not supported!\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!fs->h_ext.vlan_tci)
+ *unused_tuple |= BIT(INNER_VLAN_TAG_FST);
+
+ if (fs->m_ext.vlan_tci &&
+ be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) {
+ dev_err(&hdev->pdev->dev,
+ "failed to config vlan_tci, invalid vlan_tci: %u, max is %d.\n",
+ ntohs(fs->h_ext.vlan_tci), VLAN_N_VID - 1);
+ return -EINVAL;
+ }
+ } else {
+ *unused_tuple |= BIT(INNER_VLAN_TAG_FST);
+ }
+
+ if (fs->flow_type & FLOW_MAC_EXT) {
+ if (hdev->fd_cfg.fd_mode !=
+ HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
+ dev_err(&hdev->pdev->dev,
+ "FLOW_MAC_EXT is not supported in current fd mode!\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (is_zero_ether_addr(fs->h_ext.h_dest))
+ *unused_tuple |= BIT(INNER_DST_MAC);
+ else
+ *unused_tuple &= ~BIT(INNER_DST_MAC);
+ }
+
+ return 0;
+}
+
+static int hclge_fd_get_user_def_layer(u32 flow_type, u32 *unused_tuple,
+ struct hclge_fd_user_def_info *info)
+{
+ switch (flow_type) {
+ case ETHER_FLOW:
+ info->layer = HCLGE_FD_USER_DEF_L2;
+ *unused_tuple &= ~BIT(INNER_L2_RSV);
+ break;
+ case IP_USER_FLOW:
+ case IPV6_USER_FLOW:
+ info->layer = HCLGE_FD_USER_DEF_L3;
+ *unused_tuple &= ~BIT(INNER_L3_RSV);
+ break;
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ info->layer = HCLGE_FD_USER_DEF_L4;
+ *unused_tuple &= ~BIT(INNER_L4_RSV);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *fs)
+{
+ return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) == 0;
+}
+
+static int hclge_fd_parse_user_def_field(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ u32 *unused_tuple,
+ struct hclge_fd_user_def_info *info)
+{
+ u32 tuple_active = hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active;
+ u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+ u16 data, offset, data_mask, offset_mask;
+ int ret;
+
+ info->layer = HCLGE_FD_USER_DEF_NONE;
+ *unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
+
+ if (!(fs->flow_type & FLOW_EXT) || hclge_fd_is_user_def_all_masked(fs))
+ return 0;
+
+ /* user-def data from ethtool is 64 bit value, the bit0~15 is used
+ * for data, and bit32~47 is used for offset.
+ */
+ data = be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
+ data_mask = be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
+ offset = be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
+ offset_mask = be32_to_cpu(fs->m_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
+
+ if (!(tuple_active & HCLGE_FD_TUPLE_USER_DEF_TUPLES)) {
+ dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (offset > HCLGE_FD_MAX_USER_DEF_OFFSET) {
+ dev_err(&hdev->pdev->dev,
+ "user-def offset[%u] should be no more than %u\n",
+ offset, HCLGE_FD_MAX_USER_DEF_OFFSET);
+ return -EINVAL;
+ }
+
+ if (offset_mask != HCLGE_FD_USER_DEF_OFFSET_UNMASK) {
+ dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n");
+ return -EINVAL;
+ }
+
+ ret = hclge_fd_get_user_def_layer(flow_type, unused_tuple, info);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "unsupported flow type for user-def bytes, ret = %d\n",
+ ret);
+ return ret;
+ }
+
+ info->data = data;
+ info->data_mask = data_mask;
+ info->offset = offset;
+
+ return 0;
+}
+
+static int hclge_fd_check_spec(struct hclge_dev *hdev,
+ struct ethtool_rx_flow_spec *fs,
+ u32 *unused_tuple,
+ struct hclge_fd_user_def_info *info)
+{
+ u32 flow_type;
+ int ret;
+
+ if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
+ dev_err(&hdev->pdev->dev,
+ "failed to config fd rules, invalid rule location: %u, max is %u\n.",
+ fs->location,
+ hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1] - 1);
+ return -EINVAL;
+ }
+
+ ret = hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info);
+ if (ret)
+ return ret;
+
+ flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+ switch (flow_type) {
+ case SCTP_V4_FLOW:
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ ret = hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec,
+ unused_tuple);
+ break;
+ case IP_USER_FLOW:
+ ret = hclge_fd_check_ip4_tuple(&fs->h_u.usr_ip4_spec,
+ unused_tuple);
+ break;
+ case SCTP_V6_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ ret = hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec,
+ unused_tuple);
+ break;
+ case IPV6_USER_FLOW:
+ ret = hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec,
+ unused_tuple);
+ break;
+ case ETHER_FLOW:
+ if (hdev->fd_cfg.fd_mode !=
+ HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
+ dev_err(&hdev->pdev->dev,
+ "ETHER_FLOW is not supported in current fd mode!\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = hclge_fd_check_ether_tuple(&fs->h_u.ether_spec,
+ unused_tuple);
+ break;
+ default:
+ dev_err(&hdev->pdev->dev,
+ "unsupported protocol type, protocol type = %#x\n",
+ flow_type);
+ return -EOPNOTSUPP;
+ }
+
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "failed to check flow union tuple, ret = %d\n",
+ ret);
+ return ret;
+ }
+
+ return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple);
+}
+
+static void hclge_fd_get_tcpip4_tuple(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule, u8 ip_proto)
+{
+ rule->tuples.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src);
+ rule->tuples_mask.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src);
+
+ rule->tuples.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst);
+ rule->tuples_mask.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst);
+
+ rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc);
+ rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc);
+
+ rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst);
+ rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst);
+
+ rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos;
+ rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos;
+
+ rule->tuples.ether_proto = ETH_P_IP;
+ rule->tuples_mask.ether_proto = 0xFFFF;
+
+ rule->tuples.ip_proto = ip_proto;
+ rule->tuples_mask.ip_proto = 0xFF;
+}
+
+static void hclge_fd_get_ip4_tuple(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ rule->tuples.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src);
+ rule->tuples_mask.src_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src);
+
+ rule->tuples.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst);
+ rule->tuples_mask.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst);
+
+ rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos;
+ rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos;
+
+ rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto;
+ rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto;
+
+ rule->tuples.ether_proto = ETH_P_IP;
+ rule->tuples_mask.ether_proto = 0xFFFF;
+}
+
+static void hclge_fd_get_tcpip6_tuple(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule, u8 ip_proto)
+{
+ ipv6_addr_be32_to_cpu(rule->tuples.src_ip,
+ fs->h_u.tcp_ip6_spec.ip6src);
+ ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip,
+ fs->m_u.tcp_ip6_spec.ip6src);
+
+ ipv6_addr_be32_to_cpu(rule->tuples.dst_ip,
+ fs->h_u.tcp_ip6_spec.ip6dst);
+ ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
+ fs->m_u.tcp_ip6_spec.ip6dst);
+
+ rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc);
+ rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc);
+
+ rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst);
+ rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst);
+
+ rule->tuples.ether_proto = ETH_P_IPV6;
+ rule->tuples_mask.ether_proto = 0xFFFF;
+
+ rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
+ rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
+
+ rule->tuples.ip_proto = ip_proto;
+ rule->tuples_mask.ip_proto = 0xFF;
+}
+
+static void hclge_fd_get_ip6_tuple(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ ipv6_addr_be32_to_cpu(rule->tuples.src_ip,
+ fs->h_u.usr_ip6_spec.ip6src);
+ ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip,
+ fs->m_u.usr_ip6_spec.ip6src);
+
+ ipv6_addr_be32_to_cpu(rule->tuples.dst_ip,
+ fs->h_u.usr_ip6_spec.ip6dst);
+ ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
+ fs->m_u.usr_ip6_spec.ip6dst);
+
+ rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto;
+ rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto;
+
+ rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
+ rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
+
+ rule->tuples.ether_proto = ETH_P_IPV6;
+ rule->tuples_mask.ether_proto = 0xFFFF;
+}
+
+static void hclge_fd_get_ether_tuple(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ ether_addr_copy(rule->tuples.src_mac, fs->h_u.ether_spec.h_source);
+ ether_addr_copy(rule->tuples_mask.src_mac, fs->m_u.ether_spec.h_source);
+
+ ether_addr_copy(rule->tuples.dst_mac, fs->h_u.ether_spec.h_dest);
+ ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_u.ether_spec.h_dest);
+
+ rule->tuples.ether_proto = be16_to_cpu(fs->h_u.ether_spec.h_proto);
+ rule->tuples_mask.ether_proto = be16_to_cpu(fs->m_u.ether_spec.h_proto);
+}
+
+static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *info,
+ struct hclge_fd_rule *rule)
+{
+ switch (info->layer) {
+ case HCLGE_FD_USER_DEF_L2:
+ rule->tuples.l2_user_def = info->data;
+ rule->tuples_mask.l2_user_def = info->data_mask;
+ break;
+ case HCLGE_FD_USER_DEF_L3:
+ rule->tuples.l3_user_def = info->data;
+ rule->tuples_mask.l3_user_def = info->data_mask;
+ break;
+ case HCLGE_FD_USER_DEF_L4:
+ rule->tuples.l4_user_def = (u32)info->data << 16;
+ rule->tuples_mask.l4_user_def = (u32)info->data_mask << 16;
+ break;
+ default:
+ break;
+ }
+
+ rule->ep.user_def = *info;
+}
+
+static int hclge_fd_get_tuple(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule,
+ struct hclge_fd_user_def_info *info)
+{
+ u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+
+ switch (flow_type) {
+ case SCTP_V4_FLOW:
+ hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_SCTP);
+ break;
+ case TCP_V4_FLOW:
+ hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_TCP);
+ break;
+ case UDP_V4_FLOW:
+ hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_UDP);
+ break;
+ case IP_USER_FLOW:
+ hclge_fd_get_ip4_tuple(fs, rule);
+ break;
+ case SCTP_V6_FLOW:
+ hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_SCTP);
+ break;
+ case TCP_V6_FLOW:
+ hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_TCP);
+ break;
+ case UDP_V6_FLOW:
+ hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_UDP);
+ break;
+ case IPV6_USER_FLOW:
+ hclge_fd_get_ip6_tuple(fs, rule);
+ break;
+ case ETHER_FLOW:
+ hclge_fd_get_ether_tuple(fs, rule);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (fs->flow_type & FLOW_EXT) {
+ rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci);
+ rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci);
+ hclge_fd_get_user_def_tuple(info, rule);
+ }
+
+ if (fs->flow_type & FLOW_MAC_EXT) {
+ ether_addr_copy(rule->tuples.dst_mac, fs->h_ext.h_dest);
+ ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_ext.h_dest);
+ }
+
+ return 0;
+}
+
+static int hclge_fd_config_rule(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ int ret;
+
+ ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
+ if (ret)
+ return ret;
+
+ return hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
+}
+
+static int hclge_add_fd_entry_common(struct hclge_dev *hdev,
+ struct hclge_fd_rule *rule)
+{
+ int ret;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ if (hdev->fd_active_type != rule->rule_type &&
+ (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
+ hdev->fd_active_type == HCLGE_FD_EP_ACTIVE)) {
+ dev_err(&hdev->pdev->dev,
+ "mode conflict(new type %d, active type %d), please delete existent rules first\n",
+ rule->rule_type, hdev->fd_active_type);
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -EINVAL;
+ }
+
+ ret = hclge_fd_check_user_def_refcnt(hdev, rule);
+ if (ret)
+ goto out;
+
+ ret = hclge_clear_arfs_rules(hdev);
+ if (ret)
+ goto out;
+
+ ret = hclge_fd_config_rule(hdev, rule);
+ if (ret)
+ goto out;
+
+ rule->state = HCLGE_FD_ACTIVE;
+ hdev->fd_active_type = rule->rule_type;
+ hclge_update_fd_list(hdev, rule->state, rule->location, rule);
+
+out:
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return ret;
+}
+
+bool hclge_is_cls_flower_active(struct hnae3_handle *handle)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+
+ return hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE;
+}
+
+static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_cookie,
+ u16 *vport_id, u8 *action, u16 *queue_id)
+{
+ struct hclge_vport *vport = hdev->vport;
+
+ if (ring_cookie == RX_CLS_FLOW_DISC) {
+ *action = HCLGE_FD_ACTION_DROP_PACKET;
+ } else {
+ u32 ring = ethtool_get_flow_spec_ring(ring_cookie);
+ u8 vf = ethtool_get_flow_spec_ring_vf(ring_cookie);
+ u16 tqps;
+
+ /* To keep consistent with user's configuration, minus 1 when
+ * printing 'vf', because vf id from ethtool is added 1 for vf.
+ */
+ if (vf > hdev->num_req_vfs) {
+ dev_err(&hdev->pdev->dev,
+ "Error: vf id (%u) should be less than %u\n",
+ vf - 1U, hdev->num_req_vfs);
+ return -EINVAL;
+ }
+
+ *vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id;
+ tqps = hdev->vport[vf].nic.kinfo.num_tqps;
+
+ if (ring >= tqps) {
+ dev_err(&hdev->pdev->dev,
+ "Error: queue id (%u) > max tqp num (%u)\n",
+ ring, tqps - 1U);
+ return -EINVAL;
+ }
+
+ *action = HCLGE_FD_ACTION_SELECT_QUEUE;
+ *queue_id = ring;
+ }
+
+ return 0;
+}
+
+int hclge_add_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_user_def_info info;
+ u16 dst_vport_id = 0, q_index = 0;
+ struct ethtool_rx_flow_spec *fs;
+ struct hclge_fd_rule *rule;
+ u32 unused = 0;
+ u8 action;
+ int ret;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
+ dev_err(&hdev->pdev->dev,
+ "flow table director is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!hdev->fd_en) {
+ dev_err(&hdev->pdev->dev,
+ "please enable flow director first\n");
+ return -EOPNOTSUPP;
+ }
+
+ fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
+
+ ret = hclge_fd_check_spec(hdev, fs, &unused, &info);
+ if (ret)
+ return ret;
+
+ ret = hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id,
+ &action, &q_index);
+ if (ret)
+ return ret;
+
+ rule = kzalloc_obj(*rule);
+ if (!rule)
+ return -ENOMEM;
+
+ ret = hclge_fd_get_tuple(fs, rule, &info);
+ if (ret) {
+ kfree(rule);
+ return ret;
+ }
+
+ rule->flow_type = fs->flow_type;
+ rule->location = fs->location;
+ rule->unused_tuple = unused;
+ rule->vf_id = dst_vport_id;
+ rule->queue_id = q_index;
+ rule->action = action;
+ rule->rule_type = HCLGE_FD_EP_ACTIVE;
+
+ ret = hclge_add_fd_entry_common(hdev, rule);
+ if (ret)
+ kfree(rule);
+
+ return ret;
+}
+
+int hclge_del_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ struct ethtool_rx_flow_spec *fs;
+ int ret;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return -EOPNOTSUPP;
+
+ fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
+
+ if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
+ return -EINVAL;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ if (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
+ !test_bit(fs->location, hdev->fd_bmap)) {
+ dev_err(&hdev->pdev->dev,
+ "Delete fail, rule %u is inexistent\n", fs->location);
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -ENOENT;
+ }
+
+ ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, fs->location,
+ NULL, false);
+ if (ret)
+ goto out;
+
+ hclge_update_fd_list(hdev, HCLGE_FD_DELETED, fs->location, NULL);
+
+out:
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return ret;
+}
+
+static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev,
+ bool clear_list)
+{
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+ u16 location;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ for_each_set_bit(location, hdev->fd_bmap,
+ hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
+ hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location,
+ NULL, false);
+
+ if (clear_list) {
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list,
+ rule_node) {
+ hlist_del(&rule->rule_node);
+ kfree(rule);
+ }
+ hdev->fd_active_type = HCLGE_FD_RULE_NONE;
+ hdev->hclge_fd_rule_num = 0;
+ bitmap_zero(hdev->fd_bmap,
+ hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
+ }
+
+ spin_unlock_bh(&hdev->fd_rule_lock);
+}
+
+void hclge_del_all_fd_entries(struct hclge_dev *hdev)
+{
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return;
+
+ hclge_clear_fd_rules_in_list(hdev, true);
+ hclge_fd_disable_user_def(hdev);
+}
+
+int hclge_restore_fd_entries(struct hnae3_handle *handle)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+
+ /* Return ok here, because reset error handling will check this
+ * return value. If error is returned here, the reset process will
+ * fail.
+ */
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return 0;
+
+ /* if fd is disabled, should not restore it when reset */
+ if (!hdev->fd_en)
+ return 0;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ if (rule->state == HCLGE_FD_ACTIVE)
+ rule->state = HCLGE_FD_TO_ADD;
+ }
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+
+ return 0;
+}
+
+int hclge_get_fd_rule_cnt(struct hnae3_handle *handle,
+ struct ethtool_rxnfc *cmd)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) ||
+ hclge_is_cls_flower_active(handle))
+ return -EOPNOTSUPP;
+
+ cmd->rule_cnt = hdev->hclge_fd_rule_num;
+ cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
+
+ return 0;
+}
+
+static void hclge_fd_get_tcpip4_info(struct hclge_fd_rule *rule,
+ struct ethtool_tcpip4_spec *spec,
+ struct ethtool_tcpip4_spec *spec_mask)
+{
+ spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
+ spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
+ 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
+
+ spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
+ spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
+ 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
+
+ spec->psrc = cpu_to_be16(rule->tuples.src_port);
+ spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
+ 0 : cpu_to_be16(rule->tuples_mask.src_port);
+
+ spec->pdst = cpu_to_be16(rule->tuples.dst_port);
+ spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
+ 0 : cpu_to_be16(rule->tuples_mask.dst_port);
+
+ spec->tos = rule->tuples.ip_tos;
+ spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+ 0 : rule->tuples_mask.ip_tos;
+}
+
+static void hclge_fd_get_ip4_info(struct hclge_fd_rule *rule,
+ struct ethtool_usrip4_spec *spec,
+ struct ethtool_usrip4_spec *spec_mask)
+{
+ spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
+ spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
+ 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
+
+ spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
+ spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
+ 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
+
+ spec->tos = rule->tuples.ip_tos;
+ spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+ 0 : rule->tuples_mask.ip_tos;
+
+ spec->proto = rule->tuples.ip_proto;
+ spec_mask->proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
+ 0 : rule->tuples_mask.ip_proto;
+
+ spec->ip_ver = ETH_RX_NFC_IP4;
+}
+
+static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule,
+ struct ethtool_tcpip6_spec *spec,
+ struct ethtool_tcpip6_spec *spec_mask)
+{
+ ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip);
+ ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip);
+ if (rule->unused_tuple & BIT(INNER_SRC_IP))
+ memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
+ else
+ ipv6_addr_cpu_to_be32(spec_mask->ip6src,
+ rule->tuples_mask.src_ip);
+
+ if (rule->unused_tuple & BIT(INNER_DST_IP))
+ memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
+ else
+ ipv6_addr_cpu_to_be32(spec_mask->ip6dst,
+ rule->tuples_mask.dst_ip);
+
+ spec->tclass = rule->tuples.ip_tos;
+ spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+ 0 : rule->tuples_mask.ip_tos;
+
+ spec->psrc = cpu_to_be16(rule->tuples.src_port);
+ spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
+ 0 : cpu_to_be16(rule->tuples_mask.src_port);
+
+ spec->pdst = cpu_to_be16(rule->tuples.dst_port);
+ spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
+ 0 : cpu_to_be16(rule->tuples_mask.dst_port);
+}
+
+static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule,
+ struct ethtool_usrip6_spec *spec,
+ struct ethtool_usrip6_spec *spec_mask)
+{
+ ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip);
+ ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip);
+ if (rule->unused_tuple & BIT(INNER_SRC_IP))
+ memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
+ else
+ ipv6_addr_cpu_to_be32(spec_mask->ip6src,
+ rule->tuples_mask.src_ip);
+
+ if (rule->unused_tuple & BIT(INNER_DST_IP))
+ memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
+ else
+ ipv6_addr_cpu_to_be32(spec_mask->ip6dst,
+ rule->tuples_mask.dst_ip);
+
+ spec->tclass = rule->tuples.ip_tos;
+ spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+ 0 : rule->tuples_mask.ip_tos;
+
+ spec->l4_proto = rule->tuples.ip_proto;
+ spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
+ 0 : rule->tuples_mask.ip_proto;
+}
+
+static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule,
+ struct ethhdr *spec,
+ struct ethhdr *spec_mask)
+{
+ ether_addr_copy(spec->h_source, rule->tuples.src_mac);
+ ether_addr_copy(spec->h_dest, rule->tuples.dst_mac);
+
+ if (rule->unused_tuple & BIT(INNER_SRC_MAC))
+ eth_zero_addr(spec_mask->h_source);
+ else
+ ether_addr_copy(spec_mask->h_source, rule->tuples_mask.src_mac);
+
+ if (rule->unused_tuple & BIT(INNER_DST_MAC))
+ eth_zero_addr(spec_mask->h_dest);
+ else
+ ether_addr_copy(spec_mask->h_dest, rule->tuples_mask.dst_mac);
+
+ spec->h_proto = cpu_to_be16(rule->tuples.ether_proto);
+ spec_mask->h_proto = rule->unused_tuple & BIT(INNER_ETH_TYPE) ?
+ 0 : cpu_to_be16(rule->tuples_mask.ether_proto);
+}
+
+static void hclge_fd_get_user_def_info(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ if ((rule->unused_tuple & HCLGE_FD_TUPLE_USER_DEF_TUPLES) ==
+ HCLGE_FD_TUPLE_USER_DEF_TUPLES) {
+ fs->h_ext.data[0] = 0;
+ fs->h_ext.data[1] = 0;
+ fs->m_ext.data[0] = 0;
+ fs->m_ext.data[1] = 0;
+ } else {
+ fs->h_ext.data[0] = cpu_to_be32(rule->ep.user_def.offset);
+ fs->h_ext.data[1] = cpu_to_be32(rule->ep.user_def.data);
+ fs->m_ext.data[0] =
+ cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK);
+ fs->m_ext.data[1] = cpu_to_be32(rule->ep.user_def.data_mask);
+ }
+}
+
+static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ if (fs->flow_type & FLOW_EXT) {
+ fs->h_ext.vlan_tci = cpu_to_be16(rule->tuples.vlan_tag1);
+ fs->m_ext.vlan_tci =
+ rule->unused_tuple & BIT(INNER_VLAN_TAG_FST) ?
+ 0 : cpu_to_be16(rule->tuples_mask.vlan_tag1);
+
+ hclge_fd_get_user_def_info(fs, rule);
+ }
+
+ if (fs->flow_type & FLOW_MAC_EXT) {
+ ether_addr_copy(fs->h_ext.h_dest, rule->tuples.dst_mac);
+ if (rule->unused_tuple & BIT(INNER_DST_MAC))
+ eth_zero_addr(fs->m_u.ether_spec.h_dest);
+ else
+ ether_addr_copy(fs->m_u.ether_spec.h_dest,
+ rule->tuples_mask.dst_mac);
+ }
+}
+
+static struct hclge_fd_rule *hclge_get_fd_rule(struct hclge_dev *hdev,
+ u16 location)
+{
+ struct hclge_fd_rule *rule = NULL;
+ struct hlist_node *node2;
+
+ hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
+ if (rule->location == location)
+ return rule;
+ else if (rule->location > location)
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static void hclge_fd_get_ring_cookie(struct ethtool_rx_flow_spec *fs,
+ struct hclge_fd_rule *rule)
+{
+ if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
+ fs->ring_cookie = RX_CLS_FLOW_DISC;
+ } else {
+ u64 vf_id;
+
+ fs->ring_cookie = rule->queue_id;
+ vf_id = rule->vf_id;
+ vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
+ fs->ring_cookie |= vf_id;
+ }
+}
+
+int hclge_get_fd_rule_info(struct hnae3_handle *handle,
+ struct ethtool_rxnfc *cmd)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_fd_rule *rule = NULL;
+ struct hclge_dev *hdev = vport->back;
+ struct ethtool_rx_flow_spec *fs;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return -EOPNOTSUPP;
+
+ fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ rule = hclge_get_fd_rule(hdev, fs->location);
+ if (!rule) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -ENOENT;
+ }
+
+ fs->flow_type = rule->flow_type;
+ switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
+ case SCTP_V4_FLOW:
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ hclge_fd_get_tcpip4_info(rule, &fs->h_u.tcp_ip4_spec,
+ &fs->m_u.tcp_ip4_spec);
+ break;
+ case IP_USER_FLOW:
+ hclge_fd_get_ip4_info(rule, &fs->h_u.usr_ip4_spec,
+ &fs->m_u.usr_ip4_spec);
+ break;
+ case SCTP_V6_FLOW:
+ case TCP_V6_FLOW:
+ case UDP_V6_FLOW:
+ hclge_fd_get_tcpip6_info(rule, &fs->h_u.tcp_ip6_spec,
+ &fs->m_u.tcp_ip6_spec);
+ break;
+ case IPV6_USER_FLOW:
+ hclge_fd_get_ip6_info(rule, &fs->h_u.usr_ip6_spec,
+ &fs->m_u.usr_ip6_spec);
+ break;
+ /* The flow type of fd rule has been checked before adding in to rule
+ * list. As other flow types have been handled, it must be ETHER_FLOW
+ * for the default case
+ */
+ default:
+ hclge_fd_get_ether_info(rule, &fs->h_u.ether_spec,
+ &fs->m_u.ether_spec);
+ break;
+ }
+
+ hclge_fd_get_ext_info(fs, rule);
+
+ hclge_fd_get_ring_cookie(fs, rule);
+
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ return 0;
+}
+
+int hclge_get_all_rules(struct hnae3_handle *handle,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node2;
+ u32 cnt = 0;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return -EOPNOTSUPP;
+
+ cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ hlist_for_each_entry_safe(rule, node2,
+ &hdev->fd_rule_list, rule_node) {
+ if (cnt == cmd->rule_cnt) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -EMSGSIZE;
+ }
+
+ if (rule->state == HCLGE_FD_TO_DEL)
+ continue;
+
+ rule_locs[cnt] = rule->location;
+ cnt++;
+ }
+
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ cmd->rule_cnt = cnt;
+
+ return 0;
+}
+
+static void hclge_fd_get_flow_tuples(const struct flow_keys *fkeys,
+ struct hclge_fd_rule_tuples *tuples)
+{
+#define flow_ip6_src fkeys->addrs.v6addrs.src.in6_u.u6_addr32
+#define flow_ip6_dst fkeys->addrs.v6addrs.dst.in6_u.u6_addr32
+
+ tuples->ether_proto = be16_to_cpu(fkeys->basic.n_proto);
+ tuples->ip_proto = fkeys->basic.ip_proto;
+ tuples->dst_port = be16_to_cpu(fkeys->ports.dst);
+
+ if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
+ tuples->src_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.src);
+ tuples->dst_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.dst);
+ } else {
+ int i;
+
+ for (i = 0; i < IPV6_ADDR_WORDS; i++) {
+ tuples->src_ip[i] = be32_to_cpu(flow_ip6_src[i]);
+ tuples->dst_ip[i] = be32_to_cpu(flow_ip6_dst[i]);
+ }
+ }
+}
+
+/* traverse all rules, check whether an existed rule has the same tuples */
+static struct hclge_fd_rule *
+hclge_fd_search_flow_keys(struct hclge_dev *hdev,
+ const struct hclge_fd_rule_tuples *tuples)
+{
+ struct hclge_fd_rule *rule = NULL;
+ struct hlist_node *node;
+
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ if (!memcmp(tuples, &rule->tuples, sizeof(*tuples)))
+ return rule;
+ }
+
+ return NULL;
+}
+
+static void hclge_fd_build_arfs_rule(const struct hclge_fd_rule_tuples *tuples,
+ struct hclge_fd_rule *rule)
+{
+ rule->unused_tuple = BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
+ BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) |
+ BIT(INNER_SRC_PORT);
+ rule->action = 0;
+ rule->vf_id = 0;
+ rule->rule_type = HCLGE_FD_ARFS_ACTIVE;
+ rule->state = HCLGE_FD_TO_ADD;
+ if (tuples->ether_proto == ETH_P_IP) {
+ if (tuples->ip_proto == IPPROTO_TCP)
+ rule->flow_type = TCP_V4_FLOW;
+ else
+ rule->flow_type = UDP_V4_FLOW;
+ } else {
+ if (tuples->ip_proto == IPPROTO_TCP)
+ rule->flow_type = TCP_V6_FLOW;
+ else
+ rule->flow_type = UDP_V6_FLOW;
+ }
+ memcpy(&rule->tuples, tuples, sizeof(rule->tuples));
+ memset(&rule->tuples_mask, 0xFF, sizeof(rule->tuples_mask));
+}
+
+int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
+ u16 flow_id, struct flow_keys *fkeys)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_fd_rule_tuples new_tuples = {};
+ struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_rule *rule;
+ u16 bit_id;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return -EOPNOTSUPP;
+
+ /* when there is already fd rule existed add by user,
+ * arfs should not work
+ */
+ spin_lock_bh(&hdev->fd_rule_lock);
+ if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE &&
+ hdev->fd_active_type != HCLGE_FD_RULE_NONE) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -EOPNOTSUPP;
+ }
+
+ hclge_fd_get_flow_tuples(fkeys, &new_tuples);
+
+ /* check is there flow director filter existed for this flow,
+ * if not, create a new filter for it;
+ * if filter exist with different queue id, modify the filter;
+ * if filter exist with same queue id, do nothing
+ */
+ rule = hclge_fd_search_flow_keys(hdev, &new_tuples);
+ if (!rule) {
+ bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM);
+ if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -ENOSPC;
+ }
+
+ rule = kzalloc_obj(*rule, GFP_ATOMIC);
+ if (!rule) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -ENOMEM;
+ }
+
+ rule->location = bit_id;
+ rule->arfs.flow_id = flow_id;
+ rule->queue_id = queue_id;
+ hclge_fd_build_arfs_rule(&new_tuples, rule);
+ hclge_update_fd_list(hdev, rule->state, rule->location, rule);
+ hdev->fd_active_type = HCLGE_FD_ARFS_ACTIVE;
+ } else if (rule->queue_id != queue_id) {
+ rule->queue_id = queue_id;
+ rule->state = HCLGE_FD_TO_ADD;
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+ hclge_task_schedule(hdev, 0);
+ }
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return rule->location;
+}
+
+void hclge_rfs_filter_expire(struct hclge_dev *hdev)
+{
+#ifdef CONFIG_RFS_ACCEL
+ struct hnae3_handle *handle = &hdev->vport[0].nic;
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+ if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return;
+ }
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ if (rule->state != HCLGE_FD_ACTIVE)
+ continue;
+ if (rps_may_expire_flow(handle->netdev, rule->queue_id,
+ rule->arfs.flow_id, rule->location)) {
+ rule->state = HCLGE_FD_TO_DEL;
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+ }
+ }
+ spin_unlock_bh(&hdev->fd_rule_lock);
+#endif
+}
+
+/* make sure being called after lock up with fd_rule_lock */
+int hclge_clear_arfs_rules(struct hclge_dev *hdev)
+{
+#ifdef CONFIG_RFS_ACCEL
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+ int ret;
+
+ if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE)
+ return 0;
+
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ switch (rule->state) {
+ case HCLGE_FD_TO_DEL:
+ case HCLGE_FD_ACTIVE:
+ ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
+ rule->location, NULL, false);
+ if (ret)
+ return ret;
+ fallthrough;
+ case HCLGE_FD_TO_ADD:
+ hclge_fd_dec_rule_cnt(hdev, rule->location);
+ hlist_del(&rule->rule_node);
+ kfree(rule);
+ break;
+ default:
+ break;
+ }
+ }
+ hclge_sync_fd_state(hdev);
+
+#endif
+ return 0;
+}
+
+static void hclge_get_cls_key_basic(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_BASIC)) {
+ struct flow_match_basic match;
+ u16 ethtype_key, ethtype_mask;
+
+ flow_rule_match_basic(flow, &match);
+ ethtype_key = ntohs(match.key->n_proto);
+ ethtype_mask = ntohs(match.mask->n_proto);
+
+ if (ethtype_key == ETH_P_ALL) {
+ ethtype_key = 0;
+ ethtype_mask = 0;
+ }
+ rule->tuples.ether_proto = ethtype_key;
+ rule->tuples_mask.ether_proto = ethtype_mask;
+ rule->tuples.ip_proto = match.key->ip_proto;
+ rule->tuples_mask.ip_proto = match.mask->ip_proto;
+ } else {
+ rule->unused_tuple |= BIT(INNER_IP_PROTO);
+ rule->unused_tuple |= BIT(INNER_ETH_TYPE);
+ }
+}
+
+static void hclge_get_cls_key_mac(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+ struct flow_match_eth_addrs match;
+
+ flow_rule_match_eth_addrs(flow, &match);
+ ether_addr_copy(rule->tuples.dst_mac, match.key->dst);
+ ether_addr_copy(rule->tuples_mask.dst_mac, match.mask->dst);
+ ether_addr_copy(rule->tuples.src_mac, match.key->src);
+ ether_addr_copy(rule->tuples_mask.src_mac, match.mask->src);
+ if (is_zero_ether_addr(match.key->dst))
+ rule->unused_tuple |= BIT(INNER_DST_MAC);
+ if (is_zero_ether_addr(match.key->src))
+ rule->unused_tuple |= BIT(INNER_SRC_MAC);
+ } else {
+ rule->unused_tuple |= BIT(INNER_DST_MAC);
+ rule->unused_tuple |= BIT(INNER_SRC_MAC);
+ }
+}
+
+static void hclge_get_cls_key_vlan(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_VLAN)) {
+ struct flow_match_vlan match;
+
+ flow_rule_match_vlan(flow, &match);
+ rule->tuples.vlan_tag1 = match.key->vlan_id |
+ (match.key->vlan_priority << VLAN_PRIO_SHIFT);
+ rule->tuples_mask.vlan_tag1 = match.mask->vlan_id |
+ (match.mask->vlan_priority << VLAN_PRIO_SHIFT);
+ } else {
+ rule->unused_tuple |= BIT(INNER_VLAN_TAG_FST);
+ }
+}
+
+static int hclge_get_cls_key_ip(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule,
+ struct netlink_ext_ack *extack)
+{
+ u16 addr_type = 0;
+
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) {
+ struct flow_match_control match;
+
+ flow_rule_match_control(flow, &match);
+ addr_type = match.key->addr_type;
+
+ if (flow_rule_has_control_flags(match.mask->flags, extack))
+ return -EOPNOTSUPP;
+ }
+
+ if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+ struct flow_match_ipv4_addrs match;
+
+ flow_rule_match_ipv4_addrs(flow, &match);
+ rule->tuples.src_ip[IPV4_INDEX] = be32_to_cpu(match.key->src);
+ rule->tuples_mask.src_ip[IPV4_INDEX] =
+ be32_to_cpu(match.mask->src);
+ rule->tuples.dst_ip[IPV4_INDEX] = be32_to_cpu(match.key->dst);
+ rule->tuples_mask.dst_ip[IPV4_INDEX] =
+ be32_to_cpu(match.mask->dst);
+ if (!match.key->src)
+ rule->unused_tuple |= BIT(INNER_SRC_IP);
+ if (!match.key->dst)
+ rule->unused_tuple |= BIT(INNER_DST_IP);
+ } else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+ struct flow_match_ipv6_addrs match;
+
+ flow_rule_match_ipv6_addrs(flow, &match);
+ ipv6_addr_be32_to_cpu(rule->tuples.src_ip,
+ match.key->src.s6_addr32);
+ ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip,
+ match.mask->src.s6_addr32);
+ ipv6_addr_be32_to_cpu(rule->tuples.dst_ip,
+ match.key->dst.s6_addr32);
+ ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
+ match.mask->dst.s6_addr32);
+ if (ipv6_addr_any((struct in6_addr *)match.key->src.s6_addr32))
+ rule->unused_tuple |= BIT(INNER_SRC_IP);
+ if (ipv6_addr_any((struct in6_addr *)match.key->dst.s6_addr32))
+ rule->unused_tuple |= BIT(INNER_DST_IP);
+ } else {
+ rule->unused_tuple |= BIT(INNER_SRC_IP);
+ rule->unused_tuple |= BIT(INNER_DST_IP);
+ }
+
+ return 0;
+}
+
+static void hclge_get_cls_key_port(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_PORTS)) {
+ struct flow_match_ports match;
+
+ flow_rule_match_ports(flow, &match);
+
+ rule->tuples.src_port = be16_to_cpu(match.key->src);
+ rule->tuples_mask.src_port = be16_to_cpu(match.mask->src);
+ rule->tuples.dst_port = be16_to_cpu(match.key->dst);
+ rule->tuples_mask.dst_port = be16_to_cpu(match.mask->dst);
+ } else {
+ rule->unused_tuple |= BIT(INNER_SRC_PORT);
+ rule->unused_tuple |= BIT(INNER_DST_PORT);
+ }
+}
+
+static int hclge_get_cls_enc_keyid(struct hclge_dev *hdev,
+ const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+ struct flow_match_enc_keyid match;
+
+ flow_rule_match_enc_keyid(flow, &match);
+
+ /* vni is only 24 bits and must be greater than 0,
+ * and it can not be masked.
+ */
+ if (match.mask->keyid != HCLGE_FD_VXLAN_VNI_UNMASK ||
+ be32_to_cpu(match.key->keyid) >= VXLAN_N_VID ||
+ !match.key->keyid) {
+ dev_err(&hdev->pdev->dev, "invalid enc_keyid\n");
+ return -EINVAL;
+ }
+
+ rule->tuples.outer_tun_vni = be32_to_cpu(match.key->keyid);
+ rule->tuples_mask.outer_tun_vni =
+ be32_to_cpu(match.mask->keyid);
+ } else {
+ rule->unused_tuple |= BIT(OUTER_TUN_VNI);
+ }
+
+ return 0;
+}
+
+static void hclge_get_cls_key_ip_tos(const struct flow_rule *flow,
+ struct hclge_fd_rule *rule)
+{
+ if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) {
+ struct flow_match_ip match;
+
+ flow_rule_match_ip(flow, &match);
+
+ rule->tuples.ip_tos = match.key->tos;
+ rule->tuples_mask.ip_tos = match.mask->tos;
+ if (!rule->tuples.ip_tos)
+ rule->unused_tuple |= BIT(INNER_IP_TOS);
+ } else {
+ rule->unused_tuple |= BIT(INNER_IP_TOS);
+ }
+}
+
+static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
+ struct flow_cls_offload *cls_flower,
+ struct hclge_fd_rule *rule)
+{
+ struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
+ struct hnae3_handle *handle = &hdev->vport[0].nic;
+ struct flow_action *action = &flow->action;
+ struct flow_action_entry *act;
+ int tc;
+
+ if (!flow_action_has_entries(&flow->action)) {
+ tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid);
+ if (tc < 0 || tc > hdev->tc_max) {
+ dev_err(&hdev->pdev->dev,
+ "invalid traffic class: %d\n", tc);
+ return -EINVAL;
+ }
+
+ rule->action = HCLGE_FD_ACTION_SELECT_TC;
+ rule->cls_flower.tc = tc;
+ return 0;
+ }
+
+ act = &action->entries[0];
+ switch (act->id) {
+ case FLOW_ACTION_RX_QUEUE_MAPPING:
+ if (act->rx_queue >= handle->kinfo.num_tqps) {
+ dev_err(&hdev->pdev->dev,
+ "queue id (%u) should be less than %u\n",
+ act->rx_queue, handle->kinfo.num_tqps - 1);
+ return -EINVAL;
+ }
+
+ rule->queue_id = act->rx_queue;
+ rule->action = HCLGE_FD_ACTION_SELECT_QUEUE;
+ return 0;
+ case FLOW_ACTION_DROP:
+ rule->action = HCLGE_FD_ACTION_DROP_PACKET;
+ return 0;
+ default:
+ dev_err(&hdev->pdev->dev, "unsupported action(%d)\n", act->id);
+ return -EOPNOTSUPP;
+ }
+}
+
+static int hclge_parse_cls_flower(struct hclge_dev *hdev,
+ struct flow_cls_offload *cls_flower,
+ struct hclge_fd_rule *rule)
+{
+ struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
+ struct netlink_ext_ack *extack = cls_flower->common.extack;
+ int ret;
+
+ /* not support any user def tuples */
+ rule->unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
+
+ hclge_get_cls_key_basic(flow, rule);
+ hclge_get_cls_key_mac(flow, rule);
+ hclge_get_cls_key_vlan(flow, rule);
+
+ ret = hclge_get_cls_key_ip(flow, rule, extack);
+ if (ret)
+ return ret;
+
+ hclge_get_cls_key_port(flow, rule);
+ hclge_get_cls_key_ip_tos(flow, rule);
+
+ return hclge_get_cls_enc_keyid(hdev, flow, rule);
+}
+
+static int hclge_check_cls_flower(struct hclge_dev *hdev,
+ struct flow_cls_offload *cls_flower)
+{
+ struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
+ struct flow_dissector *dissector = flow->match.dissector;
+ u32 prio = cls_flower->common.prio;
+
+ if (prio == 0 ||
+ prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
+ dev_err(&hdev->pdev->dev,
+ "prio %u should be in range[1, %u]\n",
+ prio, hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
+ return -EINVAL;
+ }
+
+ if (test_bit(prio - 1, hdev->fd_bmap)) {
+ dev_err(&hdev->pdev->dev, "prio %u is already used\n", prio);
+ return -EINVAL;
+ }
+
+ if (dissector->used_keys &
+ ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
+ BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
+ dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n",
+ dissector->used_keys);
+ return -EOPNOTSUPP;
+ }
+
+ /* driver will parses classid into an action */
+ if (cls_flower->classid && flow_action_has_entries(&flow->action)) {
+ dev_err(&hdev->pdev->dev,
+ "please not set classid and action together\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) {
+ dev_err(&hdev->pdev->dev, "please set action or classid\n");
+ return -EINVAL;
+ }
+
+ if (flow_action_has_entries(&flow->action) &&
+ !flow_offload_has_one_action(&flow->action)) {
+ dev_err(&hdev->pdev->dev, "unsupported multiple actions\n");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+int hclge_add_cls_flower(struct hnae3_handle *handle,
+ struct flow_cls_offload *cls_flower)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_rule *rule;
+ int ret;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
+ dev_err(&hdev->pdev->dev,
+ "cls flower is not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = hclge_check_cls_flower(hdev, cls_flower);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "failed to check cls flower params, ret = %d\n", ret);
+ return ret;
+ }
+
+ rule = kzalloc_obj(*rule);
+ if (!rule)
+ return -ENOMEM;
+
+ ret = hclge_parse_cls_flower(hdev, cls_flower, rule);
+ if (ret) {
+ kfree(rule);
+ return ret;
+ }
+
+ ret = hclge_get_tc_flower_action(hdev, cls_flower, rule);
+ if (ret) {
+ kfree(rule);
+ return ret;
+ }
+
+ rule->location = cls_flower->common.prio - 1;
+ rule->vf_id = 0;
+ rule->cls_flower.cookie = cls_flower->cookie;
+ rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE;
+
+ ret = hclge_add_fd_entry_common(hdev, rule);
+ if (ret)
+ kfree(rule);
+
+ return ret;
+}
+
+static struct hclge_fd_rule *hclge_find_cls_flower(struct hclge_dev *hdev,
+ unsigned long cookie)
+{
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+
+ hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
+ if (rule->cls_flower.cookie == cookie)
+ return rule;
+ }
+
+ return NULL;
+}
+
+int hclge_del_cls_flower(struct hnae3_handle *handle,
+ struct flow_cls_offload *cls_flower)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+ struct hclge_fd_rule *rule;
+ int ret;
+
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return -EOPNOTSUPP;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ rule = hclge_find_cls_flower(hdev, cls_flower->cookie);
+ if (!rule) {
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return -EINVAL;
+ }
+
+ ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location,
+ NULL, false);
+ if (ret) {
+ /* if tcam config fail, set rule state to TO_DEL,
+ * so the rule will be deleted when periodic
+ * task being scheduled.
+ */
+ hclge_update_fd_list(hdev, HCLGE_FD_TO_DEL,
+ rule->location, NULL);
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+ spin_unlock_bh(&hdev->fd_rule_lock);
+ return ret;
+ }
+
+ hclge_update_fd_list(hdev, HCLGE_FD_DELETED, rule->location, NULL);
+ spin_unlock_bh(&hdev->fd_rule_lock);
+
+ return 0;
+}
+
+static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist)
+{
+ struct hclge_fd_rule *rule;
+ struct hlist_node *node;
+ int ret = 0;
+
+ if (!test_and_clear_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state))
+ return;
+
+ spin_lock_bh(&hdev->fd_rule_lock);
+
+ hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
+ switch (rule->state) {
+ case HCLGE_FD_TO_ADD:
+ ret = hclge_fd_config_rule(hdev, rule);
+ if (ret)
+ goto out;
+ rule->state = HCLGE_FD_ACTIVE;
+ break;
+ case HCLGE_FD_TO_DEL:
+ ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
+ rule->location, NULL, false);
+ if (ret)
+ goto out;
+ hclge_fd_dec_rule_cnt(hdev, rule->location);
+ hclge_fd_free_node(hdev, rule);
+ break;
+ default:
+ break;
+ }
+ }
+
+out:
+ if (ret)
+ set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
+
+ spin_unlock_bh(&hdev->fd_rule_lock);
+}
+
+void hclge_sync_fd_table(struct hclge_dev *hdev)
+{
+ if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
+ return;
+
+ if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) {
+ bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE;
+
+ hclge_clear_fd_rules_in_list(hdev, clear_list);
+ }
+
+ hclge_sync_fd_user_def_cfg(hdev, false);
+
+ hclge_sync_fd_list(hdev, &hdev->fd_rule_list);
+}
+
+void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
+{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+
+ hdev->fd_en = enable;
+
+ if (!enable)
+ set_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state);
+ else
+ hclge_restore_fd_entries(handle);
+
+ hclge_task_schedule(hdev, 0);
+}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
new file mode 100644
index 000000000000..e0c03699d47e
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2024 Hisilicon Limited. */
+
+#ifndef __HCLGE_FD_H
+#define __HCLGE_FD_H
+
+struct hnae3_handle;
+struct hclge_dev;
+
+int hclge_init_fd_config(struct hclge_dev *hdev);
+int hclge_add_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd);
+int hclge_del_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *cmd);
+void hclge_del_all_fd_entries(struct hclge_dev *hdev);
+int hclge_restore_fd_entries(struct hnae3_handle *handle);
+int hclge_get_fd_rule_cnt(struct hnae3_handle *handle,
+ struct ethtool_rxnfc *cmd);
+int hclge_get_fd_rule_info(struct hnae3_handle *handle,
+ struct ethtool_rxnfc *cmd);
+int hclge_get_all_rules(struct hnae3_handle *handle,
+ struct ethtool_rxnfc *cmd, u32 *rule_locs);
+void hclge_enable_fd(struct hnae3_handle *handle, bool enable);
+int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
+ u16 flow_id, struct flow_keys *fkeys);
+int hclge_add_cls_flower(struct hnae3_handle *handle,
+ struct flow_cls_offload *cls_flower);
+int hclge_del_cls_flower(struct hnae3_handle *handle,
+ struct flow_cls_offload *cls_flower);
+bool hclge_is_cls_flower_active(struct hnae3_handle *handle);
+int hclge_clear_arfs_rules(struct hclge_dev *hdev);
+void hclge_sync_fd_table(struct hclge_dev *hdev);
+void hclge_rfs_filter_expire(struct hclge_dev *hdev);
+
+#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 35380dbd1ecf..2f1984930da2 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -16,9 +16,9 @@
#include <linux/crash_dump.h>
#include <net/rtnetlink.h>
-#include <net/vxlan.h>
#include "hclge_cmd.h"
#include "hclge_dcb.h"
+#include "hclge_fd.h"
#include "hclge_main.h"
#include "hclge_mbx.h"
#include "hclge_mdio.h"
@@ -52,8 +52,6 @@ static int hclge_init_vlan_config(struct hclge_dev *hdev);
static void hclge_sync_vlan_filter(struct hclge_dev *hdev);
static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle);
-static void hclge_rfs_filter_expire(struct hclge_dev *hdev);
-static int hclge_clear_arfs_rules(struct hclge_dev *hdev);
static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev,
unsigned long *addr);
static int hclge_set_default_loopback(struct hclge_dev *hdev);
@@ -61,7 +59,6 @@ static int hclge_set_default_loopback(struct hclge_dev *hdev);
static void hclge_sync_mac_table(struct hclge_dev *hdev);
static void hclge_restore_hw_table(struct hclge_dev *hdev);
static void hclge_sync_promisc_mode(struct hclge_dev *hdev);
-static void hclge_sync_fd_table(struct hclge_dev *hdev);
static void hclge_update_fec_stats(struct hclge_dev *hdev);
static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret,
int wait_cnt);
@@ -311,78 +308,6 @@ static const struct hclge_mac_mgr_tbl_entry_cmd hclge_mgr_table[] = {
},
};
-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, KEY_OPT_MAC, -1, -1 },
- { OUTER_SRC_MAC, 48, KEY_OPT_MAC, -1, -1 },
- { OUTER_VLAN_TAG_FST, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_ETH_TYPE, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_L2_RSV, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_IP_TOS, 8, KEY_OPT_U8, -1, -1 },
- { OUTER_IP_PROTO, 8, KEY_OPT_U8, -1, -1 },
- { OUTER_SRC_IP, 32, KEY_OPT_IP, -1, -1 },
- { OUTER_DST_IP, 32, KEY_OPT_IP, -1, -1 },
- { OUTER_L3_RSV, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 },
- { OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 },
- { OUTER_TUN_VNI, 24, KEY_OPT_VNI,
- offsetof(struct hclge_fd_rule, tuples.outer_tun_vni),
- offsetof(struct hclge_fd_rule, tuples_mask.outer_tun_vni) },
- { OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 },
- { INNER_DST_MAC, 48, KEY_OPT_MAC,
- offsetof(struct hclge_fd_rule, tuples.dst_mac),
- offsetof(struct hclge_fd_rule, tuples_mask.dst_mac) },
- { INNER_SRC_MAC, 48, KEY_OPT_MAC,
- offsetof(struct hclge_fd_rule, tuples.src_mac),
- offsetof(struct hclge_fd_rule, tuples_mask.src_mac) },
- { INNER_VLAN_TAG_FST, 16, KEY_OPT_LE16,
- offsetof(struct hclge_fd_rule, tuples.vlan_tag1),
- offsetof(struct hclge_fd_rule, tuples_mask.vlan_tag1) },
- { INNER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
- { INNER_ETH_TYPE, 16, KEY_OPT_LE16,
- offsetof(struct hclge_fd_rule, tuples.ether_proto),
- offsetof(struct hclge_fd_rule, tuples_mask.ether_proto) },
- { INNER_L2_RSV, 16, KEY_OPT_LE16,
- offsetof(struct hclge_fd_rule, tuples.l2_user_def),
- offsetof(struct hclge_fd_rule, tuples_mask.l2_user_def) },
- { INNER_IP_TOS, 8, KEY_OPT_U8,
- offsetof(struct hclge_fd_rule, tuples.ip_tos),
- offsetof(struct hclge_fd_rule, tuples_mask.ip_tos) },
- { INNER_IP_PROTO, 8, KEY_OPT_U8,
- offsetof(struct hclge_fd_rule, tuples.ip_proto),
- offsetof(struct hclge_fd_rule, tuples_mask.ip_proto) },
- { INNER_SRC_IP, 32, KEY_OPT_IP,
- offsetof(struct hclge_fd_rule, tuples.src_ip),
- offsetof(struct hclge_fd_rule, tuples_mask.src_ip) },
- { INNER_DST_IP, 32, KEY_OPT_IP,
- offsetof(struct hclge_fd_rule, tuples.dst_ip),
- offsetof(struct hclge_fd_rule, tuples_mask.dst_ip) },
- { INNER_L3_RSV, 16, KEY_OPT_LE16,
- offsetof(struct hclge_fd_rule, tuples.l3_user_def),
- offsetof(struct hclge_fd_rule, tuples_mask.l3_user_def) },
- { INNER_SRC_PORT, 16, KEY_OPT_LE16,
- offsetof(struct hclge_fd_rule, tuples.src_port),
- offsetof(struct hclge_fd_rule, tuples_mask.src_port) },
- { INNER_DST_PORT, 16, KEY_OPT_LE16,
- offsetof(struct hclge_fd_rule, tuples.dst_port),
- offsetof(struct hclge_fd_rule, tuples_mask.dst_port) },
- { INNER_L4_RSV, 32, KEY_OPT_LE32,
- offsetof(struct hclge_fd_rule, tuples.l4_user_def),
- offsetof(struct hclge_fd_rule, tuples_mask.l4_user_def) },
-};
-
/**
* hclge_cmd_send - send command to command queue
* @hw: pointer to the hw struct
@@ -5183,605 +5108,80 @@ static void hclge_request_update_promisc_mode(struct hnae3_handle *handle)
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
}
-static void hclge_sync_fd_state(struct hclge_dev *hdev)
-{
- if (hlist_empty(&hdev->fd_rule_list))
- hdev->fd_active_type = HCLGE_FD_RULE_NONE;
-}
-
-static void hclge_fd_inc_rule_cnt(struct hclge_dev *hdev, u16 location)
-{
- if (!test_bit(location, hdev->fd_bmap)) {
- set_bit(location, hdev->fd_bmap);
- hdev->hclge_fd_rule_num++;
- }
-}
-
-static void hclge_fd_dec_rule_cnt(struct hclge_dev *hdev, u16 location)
-{
- if (test_bit(location, hdev->fd_bmap)) {
- clear_bit(location, hdev->fd_bmap);
- hdev->hclge_fd_rule_num--;
- }
-}
-
-static void hclge_fd_free_node(struct hclge_dev *hdev,
- struct hclge_fd_rule *rule)
-{
- hlist_del(&rule->rule_node);
- kfree(rule);
- hclge_sync_fd_state(hdev);
-}
-
-static void hclge_update_fd_rule_node(struct hclge_dev *hdev,
- struct hclge_fd_rule *old_rule,
- struct hclge_fd_rule *new_rule,
- enum HCLGE_FD_NODE_STATE state)
-{
- switch (state) {
- case HCLGE_FD_TO_ADD:
- case HCLGE_FD_ACTIVE:
- /* 1) if the new state is TO_ADD, just replace the old rule
- * with the same location, no matter its state, because the
- * new rule will be configured to the hardware.
- * 2) if the new state is ACTIVE, it means the new rule
- * has been configured to the hardware, so just replace
- * the old rule node with the same location.
- * 3) for it doesn't add a new node to the list, so it's
- * unnecessary to update the rule number and fd_bmap.
- */
- new_rule->rule_node.next = old_rule->rule_node.next;
- new_rule->rule_node.pprev = old_rule->rule_node.pprev;
- memcpy(old_rule, new_rule, sizeof(*old_rule));
- kfree(new_rule);
- break;
- case HCLGE_FD_DELETED:
- hclge_fd_dec_rule_cnt(hdev, old_rule->location);
- hclge_fd_free_node(hdev, old_rule);
- break;
- case HCLGE_FD_TO_DEL:
- /* if new request is TO_DEL, and old rule is existent
- * 1) the state of old rule is TO_DEL, we need do nothing,
- * because we delete rule by location, other rule content
- * is unncessary.
- * 2) the state of old rule is ACTIVE, we need to change its
- * state to TO_DEL, so the rule will be deleted when periodic
- * task being scheduled.
- * 3) the state of old rule is TO_ADD, it means the rule hasn't
- * been added to hardware, so we just delete the rule node from
- * fd_rule_list directly.
- */
- if (old_rule->state == HCLGE_FD_TO_ADD) {
- hclge_fd_dec_rule_cnt(hdev, old_rule->location);
- hclge_fd_free_node(hdev, old_rule);
- return;
- }
- old_rule->state = HCLGE_FD_TO_DEL;
- break;
- }
-}
-
-static struct hclge_fd_rule *hclge_find_fd_rule(struct hlist_head *hlist,
- u16 location,
- struct hclge_fd_rule **parent)
-{
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
-
- hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
- if (rule->location == location)
- return rule;
- else if (rule->location > location)
- return NULL;
- /* record the parent node, use to keep the nodes in fd_rule_list
- * in ascend order.
- */
- *parent = rule;
- }
-
- return NULL;
-}
-
-/* insert fd rule node in ascend order according to rule->location */
-static void hclge_fd_insert_rule_node(struct hlist_head *hlist,
- struct hclge_fd_rule *rule,
- struct hclge_fd_rule *parent)
-{
- INIT_HLIST_NODE(&rule->rule_node);
-
- if (parent)
- hlist_add_behind(&rule->rule_node, &parent->rule_node);
- else
- hlist_add_head(&rule->rule_node, hlist);
-}
-
-static int hclge_fd_set_user_def_cmd(struct hclge_dev *hdev,
- struct hclge_fd_user_def_cfg *cfg)
-{
- struct hclge_fd_user_def_cfg_cmd *req;
- struct hclge_desc desc;
- u16 data = 0;
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false);
-
- req = (struct hclge_fd_user_def_cfg_cmd *)desc.data;
-
- hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[0].ref_cnt > 0);
- hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
- HCLGE_FD_USER_DEF_OFT_S, cfg[0].offset);
- req->ol2_cfg = cpu_to_le16(data);
-
- data = 0;
- hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[1].ref_cnt > 0);
- hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
- HCLGE_FD_USER_DEF_OFT_S, cfg[1].offset);
- req->ol3_cfg = cpu_to_le16(data);
-
- data = 0;
- hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[2].ref_cnt > 0);
- hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
- HCLGE_FD_USER_DEF_OFT_S, cfg[2].offset);
- req->ol4_cfg = cpu_to_le16(data);
-
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret)
- dev_err(&hdev->pdev->dev,
- "failed to set fd user def data, ret= %d\n", ret);
- return ret;
-}
-
-static void hclge_sync_fd_user_def_cfg(struct hclge_dev *hdev, bool locked)
+static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle)
{
- int ret;
-
- if (!test_and_clear_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state))
- return;
-
- if (!locked)
- spin_lock_bh(&hdev->fd_rule_lock);
-
- ret = hclge_fd_set_user_def_cmd(hdev, hdev->fd_cfg.user_def_cfg);
- if (ret)
- set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
- if (!locked)
- spin_unlock_bh(&hdev->fd_rule_lock);
+ return hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG) ||
+ hclge_read_dev(&hdev->hw, HCLGE_FUN_RST_ING);
}
-static int hclge_fd_check_user_def_refcnt(struct hclge_dev *hdev,
- struct hclge_fd_rule *rule)
+static bool hclge_get_cmdq_stat(struct hnae3_handle *handle)
{
- struct hlist_head *hlist = &hdev->fd_rule_list;
- struct hclge_fd_rule *fd_rule, *parent = NULL;
- struct hclge_fd_user_def_info *info, *old_info;
- struct hclge_fd_user_def_cfg *cfg;
-
- if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
- rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
- return 0;
-
- /* for valid layer is start from 1, so need minus 1 to get the cfg */
- cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
- info = &rule->ep.user_def;
-
- if (!cfg->ref_cnt || cfg->offset == info->offset)
- return 0;
-
- if (cfg->ref_cnt > 1)
- goto error;
-
- fd_rule = hclge_find_fd_rule(hlist, rule->location, &parent);
- if (fd_rule) {
- old_info = &fd_rule->ep.user_def;
- if (info->layer == old_info->layer)
- return 0;
- }
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
-error:
- dev_err(&hdev->pdev->dev,
- "No available offset for layer%d fd rule, each layer only support one user def offset.\n",
- info->layer + 1);
- return -ENOSPC;
+ return test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
}
-static void hclge_fd_inc_user_def_refcnt(struct hclge_dev *hdev,
- struct hclge_fd_rule *rule)
+static bool hclge_ae_dev_resetting(struct hnae3_handle *handle)
{
- struct hclge_fd_user_def_cfg *cfg;
-
- if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
- rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
- return;
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
- cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
- if (!cfg->ref_cnt) {
- cfg->offset = rule->ep.user_def.offset;
- set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
- }
- cfg->ref_cnt++;
+ return test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
}
-static void hclge_fd_dec_user_def_refcnt(struct hclge_dev *hdev,
- struct hclge_fd_rule *rule)
+static unsigned long hclge_ae_dev_reset_cnt(struct hnae3_handle *handle)
{
- struct hclge_fd_user_def_cfg *cfg;
-
- if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
- rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
- return;
-
- cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
- if (!cfg->ref_cnt)
- return;
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
- cfg->ref_cnt--;
- if (!cfg->ref_cnt) {
- cfg->offset = 0;
- set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
- }
+ return hdev->rst_stats.hw_reset_done_cnt;
}
-static void hclge_update_fd_list(struct hclge_dev *hdev,
- enum HCLGE_FD_NODE_STATE state, u16 location,
- struct hclge_fd_rule *new_rule)
+static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
{
- struct hlist_head *hlist = &hdev->fd_rule_list;
- struct hclge_fd_rule *fd_rule, *parent = NULL;
-
- fd_rule = hclge_find_fd_rule(hlist, location, &parent);
- if (fd_rule) {
- hclge_fd_dec_user_def_refcnt(hdev, fd_rule);
- if (state == HCLGE_FD_ACTIVE)
- hclge_fd_inc_user_def_refcnt(hdev, new_rule);
- hclge_sync_fd_user_def_cfg(hdev, true);
-
- hclge_update_fd_rule_node(hdev, fd_rule, new_rule, state);
- return;
- }
-
- /* it's unlikely to fail here, because we have checked the rule
- * exist before.
- */
- if (unlikely(state == HCLGE_FD_TO_DEL || state == HCLGE_FD_DELETED)) {
- dev_warn(&hdev->pdev->dev,
- "failed to delete fd rule %u, it's inexistent\n",
- location);
- return;
- }
-
- hclge_fd_inc_user_def_refcnt(hdev, new_rule);
- hclge_sync_fd_user_def_cfg(hdev, true);
-
- hclge_fd_insert_rule_node(hlist, new_rule, parent);
- hclge_fd_inc_rule_cnt(hdev, new_rule->location);
-
- if (state == HCLGE_FD_TO_ADD) {
- set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
- hclge_task_schedule(hdev, 0);
- }
-}
+#define HCLGE_LINK_STATUS_WAIT_CNT 3
-static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode)
-{
- struct hclge_get_fd_mode_cmd *req;
struct hclge_desc desc;
+ struct hclge_config_mac_mode_cmd *req =
+ (struct hclge_config_mac_mode_cmd *)desc.data;
+ u32 loop_en = 0;
int ret;
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true);
-
- req = (struct hclge_get_fd_mode_cmd *)desc.data;
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false);
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret) {
- dev_err(&hdev->pdev->dev, "get fd mode fail, ret=%d\n", ret);
- return ret;
+ if (enable) {
+ hnae3_set_bit(loop_en, HCLGE_MAC_TX_EN_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_RX_EN_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_PAD_TX_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_PAD_RX_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_FCS_TX_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_STRIP_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, 1U);
+ hnae3_set_bit(loop_en, HCLGE_MAC_TX_UNDER_MIN_ERR_B, 1U);
}
- *fd_mode = req->mode;
-
- return ret;
-}
-
-static int hclge_get_fd_allocation(struct hclge_dev *hdev,
- u32 *stage1_entry_num,
- u32 *stage2_entry_num,
- u16 *stage1_counter_num,
- u16 *stage2_counter_num)
-{
- struct hclge_get_fd_allocation_cmd *req;
- struct hclge_desc desc;
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_GET_ALLOCATION, true);
-
- req = (struct hclge_get_fd_allocation_cmd *)desc.data;
+ req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
- dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=%d\n",
- ret);
- return ret;
- }
-
- *stage1_entry_num = le32_to_cpu(req->stage1_entry_num);
- *stage2_entry_num = le32_to_cpu(req->stage2_entry_num);
- *stage1_counter_num = le16_to_cpu(req->stage1_counter_num);
- *stage2_counter_num = le16_to_cpu(req->stage2_counter_num);
-
- return ret;
-}
-
-static int hclge_set_fd_key_config(struct hclge_dev *hdev,
- enum HCLGE_FD_STAGE stage_num)
-{
- struct hclge_set_fd_key_config_cmd *req;
- struct hclge_fd_key_cfg *stage;
- struct hclge_desc desc;
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_KEY_CONFIG, false);
-
- req = (struct hclge_set_fd_key_config_cmd *)desc.data;
- stage = &hdev->fd_cfg.key_cfg[stage_num];
- req->stage = stage_num;
- req->key_select = stage->key_sel;
- req->inner_sipv6_word_en = stage->inner_sipv6_word_en;
- req->inner_dipv6_word_en = stage->inner_dipv6_word_en;
- req->outer_sipv6_word_en = stage->outer_sipv6_word_en;
- req->outer_dipv6_word_en = stage->outer_dipv6_word_en;
- req->tuple_mask = cpu_to_le32(~stage->tuple_active);
- req->meta_data_mask = cpu_to_le32(~stage->meta_data_active);
-
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret)
- dev_err(&hdev->pdev->dev, "set fd key fail, ret=%d\n", ret);
-
- return ret;
-}
-
-static void hclge_fd_disable_user_def(struct hclge_dev *hdev)
-{
- struct hclge_fd_user_def_cfg *cfg = hdev->fd_cfg.user_def_cfg;
-
- spin_lock_bh(&hdev->fd_rule_lock);
- memset(cfg, 0, sizeof(hdev->fd_cfg.user_def_cfg));
- spin_unlock_bh(&hdev->fd_rule_lock);
-
- hclge_fd_set_user_def_cmd(hdev, cfg);
-}
-
-static int hclge_init_fd_config(struct hclge_dev *hdev)
-{
-#define LOW_2_WORDS 0x03
- struct hclge_fd_key_cfg *key_cfg;
- int ret;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return 0;
-
- ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode);
- if (ret)
- return ret;
-
- switch (hdev->fd_cfg.fd_mode) {
- case HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1:
- hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH;
- break;
- case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1:
- hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH / 2;
- break;
- default:
dev_err(&hdev->pdev->dev,
- "Unsupported flow director mode %u\n",
- hdev->fd_cfg.fd_mode);
- return -EOPNOTSUPP;
- }
-
- key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1];
- key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE;
- key_cfg->inner_sipv6_word_en = LOW_2_WORDS;
- key_cfg->inner_dipv6_word_en = LOW_2_WORDS;
- key_cfg->outer_sipv6_word_en = 0;
- key_cfg->outer_dipv6_word_en = 0;
-
- key_cfg->tuple_active = BIT(INNER_VLAN_TAG_FST) | 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 (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
- key_cfg->tuple_active |= BIT(INNER_DST_MAC) |
- BIT(INNER_SRC_MAC) |
- BIT(OUTER_TUN_VNI);
- if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3)
- key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
- }
-
- /* roce_type is used to filter roce frames
- * dst_vport is used to specify the rule
- */
- key_cfg->meta_data_active = BIT(ROCE_TYPE) | BIT(DST_VPORT);
-
- ret = hclge_get_fd_allocation(hdev,
- &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1],
- &hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_2],
- &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1],
- &hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_2]);
- if (ret)
- return ret;
-
- return hclge_set_fd_key_config(hdev, HCLGE_FD_STAGE_1);
-}
-
-static int hclge_fd_tcam_config(struct hclge_dev *hdev, u8 stage, bool sel_x,
- int loc, u8 *key, bool is_add)
-{
- struct hclge_fd_tcam_config_1_cmd *req1;
- struct hclge_fd_tcam_config_2_cmd *req2;
- struct hclge_fd_tcam_config_3_cmd *req3;
- struct hclge_desc desc[3];
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, false);
- desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
- hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false);
- desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
- hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false);
-
- req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data;
- req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data;
- req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data;
-
- req1->stage = stage;
- req1->xy_sel = sel_x ? 1 : 0;
- hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0);
- req1->index = cpu_to_le32(loc);
- req1->entry_vld = sel_x ? is_add : 0;
-
- if (key) {
- memcpy(req1->tcam_data, &key[0], sizeof(req1->tcam_data));
- memcpy(req2->tcam_data, &key[sizeof(req1->tcam_data)],
- sizeof(req2->tcam_data));
- memcpy(req3->tcam_data, &key[sizeof(req1->tcam_data) +
- sizeof(req2->tcam_data)], sizeof(req3->tcam_data));
+ "mac enable fail, ret =%d.\n", ret);
+ return;
}
- ret = hclge_cmd_send(&hdev->hw, desc, 3);
- if (ret)
- dev_err(&hdev->pdev->dev,
- "config tcam key fail, ret=%d\n",
- ret);
-
- return ret;
-}
-
-static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc,
- struct hclge_fd_ad_data *action)
-{
- struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
- struct hclge_fd_ad_config_cmd *req;
- struct hclge_desc desc;
- u64 ad_data = 0;
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false);
-
- req = (struct hclge_fd_ad_config_cmd *)desc.data;
- req->index = cpu_to_le32(loc);
- req->stage = stage;
-
- hnae3_set_bit(ad_data, HCLGE_FD_AD_WR_RULE_ID_B,
- action->write_rule_id_to_bd);
- hnae3_set_field(ad_data, HCLGE_FD_AD_RULE_ID_M, HCLGE_FD_AD_RULE_ID_S,
- action->rule_id);
- if (test_bit(HNAE3_DEV_SUPPORT_FD_FORWARD_TC_B, ae_dev->caps)) {
- hnae3_set_bit(ad_data, HCLGE_FD_AD_TC_OVRD_B,
- action->override_tc);
- hnae3_set_field(ad_data, HCLGE_FD_AD_TC_SIZE_M,
- HCLGE_FD_AD_TC_SIZE_S, (u32)action->tc_size);
- }
- hnae3_set_bit(ad_data, HCLGE_FD_AD_QID_H_B,
- action->queue_id >= HCLGE_TQP_MAX_SIZE_DEV_V2 ? 1 : 0);
- hnae3_set_bit(ad_data, HCLGE_FD_AD_COUNTER_NUM_H_B,
- action->counter_id >= HCLGE_FD_COUNTER_MAX_SIZE_DEV_V2 ?
- 1 : 0);
- ad_data <<= 32;
- hnae3_set_bit(ad_data, HCLGE_FD_AD_DROP_B, action->drop_packet);
- hnae3_set_bit(ad_data, HCLGE_FD_AD_DIRECT_QID_B,
- action->forward_to_direct_queue);
- hnae3_set_field(ad_data, HCLGE_FD_AD_QID_L_M, HCLGE_FD_AD_QID_L_S,
- action->queue_id);
- hnae3_set_bit(ad_data, HCLGE_FD_AD_USE_COUNTER_B, action->use_counter);
- hnae3_set_field(ad_data, HCLGE_FD_AD_COUNTER_NUM_L_M,
- HCLGE_FD_AD_COUNTER_NUM_L_S, action->counter_id);
- hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage);
- hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S,
- action->next_input_key);
-
- req->ad_data = cpu_to_le64(ad_data);
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret)
- dev_err(&hdev->pdev->dev, "fd ad config fail, ret=%d\n", ret);
-
- return ret;
-}
-
-static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y,
- struct hclge_fd_rule *rule)
-{
- int offset, moffset, ip_offset;
- enum HCLGE_FD_KEY_OPT key_opt;
- u16 tmp_x_s, tmp_y_s;
- u32 tmp_x_l, tmp_y_l;
- u8 *p = (u8 *)rule;
- int i;
-
- if (rule->unused_tuple & BIT(tuple_bit))
- return true;
-
- key_opt = tuple_key_info[tuple_bit].key_opt;
- offset = tuple_key_info[tuple_bit].offset;
- moffset = tuple_key_info[tuple_bit].moffset;
-
- switch (key_opt) {
- case KEY_OPT_U8:
- calc_x(*key_x, p[offset], p[moffset]);
- calc_y(*key_y, p[offset], p[moffset]);
-
- return true;
- case KEY_OPT_LE16:
- calc_x(tmp_x_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
- calc_y(tmp_y_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
- *(__le16 *)key_x = cpu_to_le16(tmp_x_s);
- *(__le16 *)key_y = cpu_to_le16(tmp_y_s);
-
- return true;
- case KEY_OPT_LE32:
- calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
- calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
- *(__le32 *)key_x = cpu_to_le32(tmp_x_l);
- *(__le32 *)key_y = cpu_to_le32(tmp_y_l);
-
- return true;
- case KEY_OPT_MAC:
- for (i = 0; i < ETH_ALEN; i++) {
- calc_x(key_x[ETH_ALEN - 1 - i], p[offset + i],
- p[moffset + i]);
- calc_y(key_y[ETH_ALEN - 1 - i], p[offset + i],
- p[moffset + i]);
- }
-
- return true;
- case KEY_OPT_IP:
- ip_offset = IPV4_INDEX * sizeof(u32);
- calc_x(tmp_x_l, *(u32 *)(&p[offset + ip_offset]),
- *(u32 *)(&p[moffset + ip_offset]));
- calc_y(tmp_y_l, *(u32 *)(&p[offset + ip_offset]),
- *(u32 *)(&p[moffset + ip_offset]));
- *(__le32 *)key_x = cpu_to_le32(tmp_x_l);
- *(__le32 *)key_y = cpu_to_le32(tmp_y_l);
-
- return true;
- case KEY_OPT_VNI:
- calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
- calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
- for (i = 0; i < HCLGE_VNI_LENGTH; i++) {
- key_x[i] = (cpu_to_le32(tmp_x_l) >>
- (i * BITS_PER_BYTE)) & 0xFF;
- key_y[i] = (cpu_to_le32(tmp_y_l) >>
- (i * BITS_PER_BYTE)) & 0xFF;
- }
-
- return true;
- default:
- return false;
- }
+ if (!enable)
+ hclge_mac_link_status_wait(hdev, HCLGE_LINK_STATUS_DOWN,
+ HCLGE_LINK_STATUS_WAIT_CNT);
}
-static u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id,
- u8 vf_id, u8 network_port_id)
+u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id,
+ u8 vf_id, u8 network_port_id)
{
u32 port_number = 0;
@@ -5800,1975 +5200,6 @@ static u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id,
return port_number;
}
-static void hclge_fd_convert_meta_data(struct hclge_fd_key_cfg *key_cfg,
- __le32 *key_x, __le32 *key_y,
- struct hclge_fd_rule *rule)
-{
- u32 tuple_bit, meta_data = 0, tmp_x, tmp_y, port_number;
- u8 cur_pos = 0, tuple_size, shift_bits;
- unsigned int i;
-
- for (i = 0; i < MAX_META_DATA; i++) {
- tuple_size = meta_data_key_info[i].key_length;
- tuple_bit = key_cfg->meta_data_active & BIT(i);
-
- switch (tuple_bit) {
- case BIT(ROCE_TYPE):
- hnae3_set_bit(meta_data, cur_pos, NIC_PACKET);
- cur_pos += tuple_size;
- break;
- case BIT(DST_VPORT):
- port_number = hclge_get_port_number(HOST_PORT, 0,
- rule->vf_id, 0);
- hnae3_set_field(meta_data,
- GENMASK(cur_pos + tuple_size, cur_pos),
- cur_pos, port_number);
- cur_pos += tuple_size;
- break;
- default:
- break;
- }
- }
-
- calc_x(tmp_x, meta_data, 0xFFFFFFFF);
- calc_y(tmp_y, meta_data, 0xFFFFFFFF);
- shift_bits = sizeof(meta_data) * 8 - cur_pos;
-
- *key_x = cpu_to_le32(tmp_x << shift_bits);
- *key_y = cpu_to_le32(tmp_y << shift_bits);
-}
-
-/* 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 hclge_config_key(struct hclge_dev *hdev, u8 stage,
- struct hclge_fd_rule *rule)
-{
- struct hclge_fd_key_cfg *key_cfg = &hdev->fd_cfg.key_cfg[stage];
- u8 key_x[MAX_KEY_BYTES], key_y[MAX_KEY_BYTES];
- u8 *cur_key_x, *cur_key_y;
- u8 meta_data_region;
- u8 tuple_size;
- int ret;
- u32 i;
-
- memset(key_x, 0, sizeof(key_x));
- memset(key_y, 0, sizeof(key_y));
- cur_key_x = key_x;
- cur_key_y = key_y;
-
- for (i = 0; i < MAX_TUPLE; i++) {
- bool tuple_valid;
-
- tuple_size = tuple_key_info[i].key_length / 8;
- if (!(key_cfg->tuple_active & BIT(i)))
- continue;
-
- tuple_valid = hclge_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 = hdev->fd_cfg.max_key_length / 8 -
- MAX_META_DATA_LENGTH / 8;
-
- hclge_fd_convert_meta_data(key_cfg,
- (__le32 *)(key_x + meta_data_region),
- (__le32 *)(key_y + meta_data_region),
- rule);
-
- ret = hclge_fd_tcam_config(hdev, stage, false, rule->location, key_y,
- true);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "fd key_y config fail, loc=%u, ret=%d\n",
- rule->queue_id, ret);
- return ret;
- }
-
- ret = hclge_fd_tcam_config(hdev, stage, true, rule->location, key_x,
- true);
- if (ret)
- dev_err(&hdev->pdev->dev,
- "fd key_x config fail, loc=%u, ret=%d\n",
- rule->queue_id, ret);
- return ret;
-}
-
-static int hclge_config_action(struct hclge_dev *hdev, u8 stage,
- struct hclge_fd_rule *rule)
-{
- struct hclge_vport *vport = hdev->vport;
- struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
- struct hclge_fd_ad_data ad_data;
-
- memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data));
- ad_data.ad_id = rule->location;
-
- if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
- ad_data.drop_packet = true;
- } else if (rule->action == HCLGE_FD_ACTION_SELECT_TC) {
- ad_data.override_tc = true;
- ad_data.queue_id =
- kinfo->tc_info.tqp_offset[rule->cls_flower.tc];
- ad_data.tc_size =
- ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]);
- } else {
- ad_data.forward_to_direct_queue = true;
- ad_data.queue_id = rule->queue_id;
- }
-
- if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) {
- ad_data.use_counter = true;
- ad_data.counter_id = rule->vf_id %
- hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1];
- } else {
- ad_data.use_counter = false;
- ad_data.counter_id = 0;
- }
-
- ad_data.use_next_stage = false;
- ad_data.next_input_key = 0;
-
- ad_data.write_rule_id_to_bd = true;
- ad_data.rule_id = rule->location;
-
- return hclge_fd_ad_config(hdev, stage, ad_data.ad_id, &ad_data);
-}
-
-static int hclge_fd_check_tcpip4_tuple(struct ethtool_tcpip4_spec *spec,
- u32 *unused_tuple)
-{
- if (!spec || !unused_tuple)
- return -EINVAL;
-
- *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
-
- if (!spec->ip4src)
- *unused_tuple |= BIT(INNER_SRC_IP);
-
- if (!spec->ip4dst)
- *unused_tuple |= BIT(INNER_DST_IP);
-
- if (!spec->psrc)
- *unused_tuple |= BIT(INNER_SRC_PORT);
-
- if (!spec->pdst)
- *unused_tuple |= BIT(INNER_DST_PORT);
-
- if (!spec->tos)
- *unused_tuple |= BIT(INNER_IP_TOS);
-
- return 0;
-}
-
-static int hclge_fd_check_ip4_tuple(struct ethtool_usrip4_spec *spec,
- u32 *unused_tuple)
-{
- if (!spec || !unused_tuple)
- return -EINVAL;
-
- *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
- BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
-
- if (!spec->ip4src)
- *unused_tuple |= BIT(INNER_SRC_IP);
-
- if (!spec->ip4dst)
- *unused_tuple |= BIT(INNER_DST_IP);
-
- if (!spec->tos)
- *unused_tuple |= BIT(INNER_IP_TOS);
-
- if (!spec->proto)
- *unused_tuple |= BIT(INNER_IP_PROTO);
-
- if (spec->l4_4_bytes)
- return -EOPNOTSUPP;
-
- if (spec->ip_ver != ETH_RX_NFC_IP4)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec,
- u32 *unused_tuple)
-{
- if (!spec || !unused_tuple)
- return -EINVAL;
-
- *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
-
- /* check whether src/dst ip address used */
- if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
- *unused_tuple |= BIT(INNER_SRC_IP);
-
- if (ipv6_addr_any((struct in6_addr *)spec->ip6dst))
- *unused_tuple |= BIT(INNER_DST_IP);
-
- if (!spec->psrc)
- *unused_tuple |= BIT(INNER_SRC_PORT);
-
- if (!spec->pdst)
- *unused_tuple |= BIT(INNER_DST_PORT);
-
- if (!spec->tclass)
- *unused_tuple |= BIT(INNER_IP_TOS);
-
- return 0;
-}
-
-static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec,
- u32 *unused_tuple)
-{
- if (!spec || !unused_tuple)
- return -EINVAL;
-
- *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
- BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
-
- /* check whether src/dst ip address used */
- if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
- *unused_tuple |= BIT(INNER_SRC_IP);
-
- if (ipv6_addr_any((struct in6_addr *)spec->ip6dst))
- *unused_tuple |= BIT(INNER_DST_IP);
-
- if (!spec->l4_proto)
- *unused_tuple |= BIT(INNER_IP_PROTO);
-
- if (!spec->tclass)
- *unused_tuple |= BIT(INNER_IP_TOS);
-
- if (spec->l4_4_bytes)
- return -EOPNOTSUPP;
-
- return 0;
-}
-
-static int hclge_fd_check_ether_tuple(struct ethhdr *spec, u32 *unused_tuple)
-{
- if (!spec || !unused_tuple)
- return -EINVAL;
-
- *unused_tuple |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
- BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) |
- BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO);
-
- if (is_zero_ether_addr(spec->h_source))
- *unused_tuple |= BIT(INNER_SRC_MAC);
-
- if (is_zero_ether_addr(spec->h_dest))
- *unused_tuple |= BIT(INNER_DST_MAC);
-
- if (!spec->h_proto)
- *unused_tuple |= BIT(INNER_ETH_TYPE);
-
- return 0;
-}
-
-static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev,
- struct ethtool_rx_flow_spec *fs,
- u32 *unused_tuple)
-{
- if (fs->flow_type & FLOW_EXT) {
- if (fs->h_ext.vlan_etype) {
- dev_err(&hdev->pdev->dev, "vlan-etype is not supported!\n");
- return -EOPNOTSUPP;
- }
-
- if (!fs->h_ext.vlan_tci)
- *unused_tuple |= BIT(INNER_VLAN_TAG_FST);
-
- if (fs->m_ext.vlan_tci &&
- be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) {
- dev_err(&hdev->pdev->dev,
- "failed to config vlan_tci, invalid vlan_tci: %u, max is %d.\n",
- ntohs(fs->h_ext.vlan_tci), VLAN_N_VID - 1);
- return -EINVAL;
- }
- } else {
- *unused_tuple |= BIT(INNER_VLAN_TAG_FST);
- }
-
- if (fs->flow_type & FLOW_MAC_EXT) {
- if (hdev->fd_cfg.fd_mode !=
- HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
- dev_err(&hdev->pdev->dev,
- "FLOW_MAC_EXT is not supported in current fd mode!\n");
- return -EOPNOTSUPP;
- }
-
- if (is_zero_ether_addr(fs->h_ext.h_dest))
- *unused_tuple |= BIT(INNER_DST_MAC);
- else
- *unused_tuple &= ~BIT(INNER_DST_MAC);
- }
-
- return 0;
-}
-
-static int hclge_fd_get_user_def_layer(u32 flow_type, u32 *unused_tuple,
- struct hclge_fd_user_def_info *info)
-{
- switch (flow_type) {
- case ETHER_FLOW:
- info->layer = HCLGE_FD_USER_DEF_L2;
- *unused_tuple &= ~BIT(INNER_L2_RSV);
- break;
- case IP_USER_FLOW:
- case IPV6_USER_FLOW:
- info->layer = HCLGE_FD_USER_DEF_L3;
- *unused_tuple &= ~BIT(INNER_L3_RSV);
- break;
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- info->layer = HCLGE_FD_USER_DEF_L4;
- *unused_tuple &= ~BIT(INNER_L4_RSV);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *fs)
-{
- return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) == 0;
-}
-
-static int hclge_fd_parse_user_def_field(struct hclge_dev *hdev,
- struct ethtool_rx_flow_spec *fs,
- u32 *unused_tuple,
- struct hclge_fd_user_def_info *info)
-{
- u32 tuple_active = hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active;
- u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
- u16 data, offset, data_mask, offset_mask;
- int ret;
-
- info->layer = HCLGE_FD_USER_DEF_NONE;
- *unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
-
- if (!(fs->flow_type & FLOW_EXT) || hclge_fd_is_user_def_all_masked(fs))
- return 0;
-
- /* user-def data from ethtool is 64 bit value, the bit0~15 is used
- * for data, and bit32~47 is used for offset.
- */
- data = be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
- data_mask = be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
- offset = be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
- offset_mask = be32_to_cpu(fs->m_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
-
- if (!(tuple_active & HCLGE_FD_TUPLE_USER_DEF_TUPLES)) {
- dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (offset > HCLGE_FD_MAX_USER_DEF_OFFSET) {
- dev_err(&hdev->pdev->dev,
- "user-def offset[%u] should be no more than %u\n",
- offset, HCLGE_FD_MAX_USER_DEF_OFFSET);
- return -EINVAL;
- }
-
- if (offset_mask != HCLGE_FD_USER_DEF_OFFSET_UNMASK) {
- dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n");
- return -EINVAL;
- }
-
- ret = hclge_fd_get_user_def_layer(flow_type, unused_tuple, info);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "unsupported flow type for user-def bytes, ret = %d\n",
- ret);
- return ret;
- }
-
- info->data = data;
- info->data_mask = data_mask;
- info->offset = offset;
-
- return 0;
-}
-
-static int hclge_fd_check_spec(struct hclge_dev *hdev,
- struct ethtool_rx_flow_spec *fs,
- u32 *unused_tuple,
- struct hclge_fd_user_def_info *info)
-{
- u32 flow_type;
- int ret;
-
- if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
- dev_err(&hdev->pdev->dev,
- "failed to config fd rules, invalid rule location: %u, max is %u\n.",
- fs->location,
- hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1] - 1);
- return -EINVAL;
- }
-
- ret = hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info);
- if (ret)
- return ret;
-
- flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
- switch (flow_type) {
- case SCTP_V4_FLOW:
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- ret = hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec,
- unused_tuple);
- break;
- case IP_USER_FLOW:
- ret = hclge_fd_check_ip4_tuple(&fs->h_u.usr_ip4_spec,
- unused_tuple);
- break;
- case SCTP_V6_FLOW:
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- ret = hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec,
- unused_tuple);
- break;
- case IPV6_USER_FLOW:
- ret = hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec,
- unused_tuple);
- break;
- case ETHER_FLOW:
- if (hdev->fd_cfg.fd_mode !=
- HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
- dev_err(&hdev->pdev->dev,
- "ETHER_FLOW is not supported in current fd mode!\n");
- return -EOPNOTSUPP;
- }
-
- ret = hclge_fd_check_ether_tuple(&fs->h_u.ether_spec,
- unused_tuple);
- break;
- default:
- dev_err(&hdev->pdev->dev,
- "unsupported protocol type, protocol type = %#x\n",
- flow_type);
- return -EOPNOTSUPP;
- }
-
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "failed to check flow union tuple, ret = %d\n",
- ret);
- return ret;
- }
-
- return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple);
-}
-
-static void hclge_fd_get_tcpip4_tuple(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule, u8 ip_proto)
-{
- rule->tuples.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src);
- rule->tuples_mask.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src);
-
- rule->tuples.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst);
- rule->tuples_mask.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst);
-
- rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc);
- rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc);
-
- rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst);
- rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst);
-
- rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos;
- rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos;
-
- rule->tuples.ether_proto = ETH_P_IP;
- rule->tuples_mask.ether_proto = 0xFFFF;
-
- rule->tuples.ip_proto = ip_proto;
- rule->tuples_mask.ip_proto = 0xFF;
-}
-
-static void hclge_fd_get_ip4_tuple(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
-{
- rule->tuples.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src);
- rule->tuples_mask.src_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src);
-
- rule->tuples.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst);
- rule->tuples_mask.dst_ip[IPV4_INDEX] =
- be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst);
-
- rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos;
- rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos;
-
- rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto;
- rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto;
-
- rule->tuples.ether_proto = ETH_P_IP;
- rule->tuples_mask.ether_proto = 0xFFFF;
-}
-
-static void hclge_fd_get_tcpip6_tuple(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule, u8 ip_proto)
-{
- ipv6_addr_be32_to_cpu(rule->tuples.src_ip,
- fs->h_u.tcp_ip6_spec.ip6src);
- ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip,
- fs->m_u.tcp_ip6_spec.ip6src);
-
- ipv6_addr_be32_to_cpu(rule->tuples.dst_ip,
- fs->h_u.tcp_ip6_spec.ip6dst);
- ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
- fs->m_u.tcp_ip6_spec.ip6dst);
-
- rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc);
- rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc);
-
- rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst);
- rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst);
-
- rule->tuples.ether_proto = ETH_P_IPV6;
- rule->tuples_mask.ether_proto = 0xFFFF;
-
- rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
- rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
-
- rule->tuples.ip_proto = ip_proto;
- rule->tuples_mask.ip_proto = 0xFF;
-}
-
-static void hclge_fd_get_ip6_tuple(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
-{
- ipv6_addr_be32_to_cpu(rule->tuples.src_ip,
- fs->h_u.usr_ip6_spec.ip6src);
- ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip,
- fs->m_u.usr_ip6_spec.ip6src);
-
- ipv6_addr_be32_to_cpu(rule->tuples.dst_ip,
- fs->h_u.usr_ip6_spec.ip6dst);
- ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
- fs->m_u.usr_ip6_spec.ip6dst);
-
- rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto;
- rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto;
-
- rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
- rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
-
- rule->tuples.ether_proto = ETH_P_IPV6;
- rule->tuples_mask.ether_proto = 0xFFFF;
-}
-
-static void hclge_fd_get_ether_tuple(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
-{
- ether_addr_copy(rule->tuples.src_mac, fs->h_u.ether_spec.h_source);
- ether_addr_copy(rule->tuples_mask.src_mac, fs->m_u.ether_spec.h_source);
-
- ether_addr_copy(rule->tuples.dst_mac, fs->h_u.ether_spec.h_dest);
- ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_u.ether_spec.h_dest);
-
- rule->tuples.ether_proto = be16_to_cpu(fs->h_u.ether_spec.h_proto);
- rule->tuples_mask.ether_proto = be16_to_cpu(fs->m_u.ether_spec.h_proto);
-}
-
-static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *info,
- struct hclge_fd_rule *rule)
-{
- switch (info->layer) {
- case HCLGE_FD_USER_DEF_L2:
- rule->tuples.l2_user_def = info->data;
- rule->tuples_mask.l2_user_def = info->data_mask;
- break;
- case HCLGE_FD_USER_DEF_L3:
- rule->tuples.l3_user_def = info->data;
- rule->tuples_mask.l3_user_def = info->data_mask;
- break;
- case HCLGE_FD_USER_DEF_L4:
- rule->tuples.l4_user_def = (u32)info->data << 16;
- rule->tuples_mask.l4_user_def = (u32)info->data_mask << 16;
- break;
- default:
- break;
- }
-
- rule->ep.user_def = *info;
-}
-
-static int hclge_fd_get_tuple(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule,
- struct hclge_fd_user_def_info *info)
-{
- u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
-
- switch (flow_type) {
- case SCTP_V4_FLOW:
- hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_SCTP);
- break;
- case TCP_V4_FLOW:
- hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_TCP);
- break;
- case UDP_V4_FLOW:
- hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_UDP);
- break;
- case IP_USER_FLOW:
- hclge_fd_get_ip4_tuple(fs, rule);
- break;
- case SCTP_V6_FLOW:
- hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_SCTP);
- break;
- case TCP_V6_FLOW:
- hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_TCP);
- break;
- case UDP_V6_FLOW:
- hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_UDP);
- break;
- case IPV6_USER_FLOW:
- hclge_fd_get_ip6_tuple(fs, rule);
- break;
- case ETHER_FLOW:
- hclge_fd_get_ether_tuple(fs, rule);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- if (fs->flow_type & FLOW_EXT) {
- rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci);
- rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci);
- hclge_fd_get_user_def_tuple(info, rule);
- }
-
- if (fs->flow_type & FLOW_MAC_EXT) {
- ether_addr_copy(rule->tuples.dst_mac, fs->h_ext.h_dest);
- ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_ext.h_dest);
- }
-
- return 0;
-}
-
-static int hclge_fd_config_rule(struct hclge_dev *hdev,
- struct hclge_fd_rule *rule)
-{
- int ret;
-
- ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
- if (ret)
- return ret;
-
- return hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
-}
-
-static int hclge_add_fd_entry_common(struct hclge_dev *hdev,
- struct hclge_fd_rule *rule)
-{
- int ret;
-
- spin_lock_bh(&hdev->fd_rule_lock);
-
- if (hdev->fd_active_type != rule->rule_type &&
- (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
- hdev->fd_active_type == HCLGE_FD_EP_ACTIVE)) {
- dev_err(&hdev->pdev->dev,
- "mode conflict(new type %d, active type %d), please delete existent rules first\n",
- rule->rule_type, hdev->fd_active_type);
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -EINVAL;
- }
-
- ret = hclge_fd_check_user_def_refcnt(hdev, rule);
- if (ret)
- goto out;
-
- ret = hclge_clear_arfs_rules(hdev);
- if (ret)
- goto out;
-
- ret = hclge_fd_config_rule(hdev, rule);
- if (ret)
- goto out;
-
- rule->state = HCLGE_FD_ACTIVE;
- hdev->fd_active_type = rule->rule_type;
- hclge_update_fd_list(hdev, rule->state, rule->location, rule);
-
-out:
- spin_unlock_bh(&hdev->fd_rule_lock);
- return ret;
-}
-
-static bool hclge_is_cls_flower_active(struct hnae3_handle *handle)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- return hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE;
-}
-
-static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_cookie,
- u16 *vport_id, u8 *action, u16 *queue_id)
-{
- struct hclge_vport *vport = hdev->vport;
-
- if (ring_cookie == RX_CLS_FLOW_DISC) {
- *action = HCLGE_FD_ACTION_DROP_PACKET;
- } else {
- u32 ring = ethtool_get_flow_spec_ring(ring_cookie);
- u8 vf = ethtool_get_flow_spec_ring_vf(ring_cookie);
- u16 tqps;
-
- /* To keep consistent with user's configuration, minus 1 when
- * printing 'vf', because vf id from ethtool is added 1 for vf.
- */
- if (vf > hdev->num_req_vfs) {
- dev_err(&hdev->pdev->dev,
- "Error: vf id (%u) should be less than %u\n",
- vf - 1U, hdev->num_req_vfs);
- return -EINVAL;
- }
-
- *vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id;
- tqps = hdev->vport[vf].nic.kinfo.num_tqps;
-
- if (ring >= tqps) {
- dev_err(&hdev->pdev->dev,
- "Error: queue id (%u) > max tqp num (%u)\n",
- ring, tqps - 1U);
- return -EINVAL;
- }
-
- *action = HCLGE_FD_ACTION_SELECT_QUEUE;
- *queue_id = ring;
- }
-
- return 0;
-}
-
-static int hclge_add_fd_entry(struct hnae3_handle *handle,
- struct ethtool_rxnfc *cmd)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct hclge_fd_user_def_info info;
- u16 dst_vport_id = 0, q_index = 0;
- struct ethtool_rx_flow_spec *fs;
- struct hclge_fd_rule *rule;
- u32 unused = 0;
- u8 action;
- int ret;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
- dev_err(&hdev->pdev->dev,
- "flow table director is not supported\n");
- return -EOPNOTSUPP;
- }
-
- if (!hdev->fd_en) {
- dev_err(&hdev->pdev->dev,
- "please enable flow director first\n");
- return -EOPNOTSUPP;
- }
-
- fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
-
- ret = hclge_fd_check_spec(hdev, fs, &unused, &info);
- if (ret)
- return ret;
-
- ret = hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id,
- &action, &q_index);
- if (ret)
- return ret;
-
- rule = kzalloc_obj(*rule);
- if (!rule)
- return -ENOMEM;
-
- ret = hclge_fd_get_tuple(fs, rule, &info);
- if (ret) {
- kfree(rule);
- return ret;
- }
-
- rule->flow_type = fs->flow_type;
- rule->location = fs->location;
- rule->unused_tuple = unused;
- rule->vf_id = dst_vport_id;
- rule->queue_id = q_index;
- rule->action = action;
- rule->rule_type = HCLGE_FD_EP_ACTIVE;
-
- ret = hclge_add_fd_entry_common(hdev, rule);
- if (ret)
- kfree(rule);
-
- return ret;
-}
-
-static int hclge_del_fd_entry(struct hnae3_handle *handle,
- struct ethtool_rxnfc *cmd)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct ethtool_rx_flow_spec *fs;
- int ret;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return -EOPNOTSUPP;
-
- fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
-
- if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
- return -EINVAL;
-
- spin_lock_bh(&hdev->fd_rule_lock);
- if (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
- !test_bit(fs->location, hdev->fd_bmap)) {
- dev_err(&hdev->pdev->dev,
- "Delete fail, rule %u is inexistent\n", fs->location);
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -ENOENT;
- }
-
- ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, fs->location,
- NULL, false);
- if (ret)
- goto out;
-
- hclge_update_fd_list(hdev, HCLGE_FD_DELETED, fs->location, NULL);
-
-out:
- spin_unlock_bh(&hdev->fd_rule_lock);
- return ret;
-}
-
-static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev,
- bool clear_list)
-{
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
- u16 location;
-
- spin_lock_bh(&hdev->fd_rule_lock);
-
- for_each_set_bit(location, hdev->fd_bmap,
- hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
- hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location,
- NULL, false);
-
- if (clear_list) {
- hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list,
- rule_node) {
- hlist_del(&rule->rule_node);
- kfree(rule);
- }
- hdev->fd_active_type = HCLGE_FD_RULE_NONE;
- hdev->hclge_fd_rule_num = 0;
- bitmap_zero(hdev->fd_bmap,
- hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
- }
-
- spin_unlock_bh(&hdev->fd_rule_lock);
-}
-
-static void hclge_del_all_fd_entries(struct hclge_dev *hdev)
-{
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return;
-
- hclge_clear_fd_rules_in_list(hdev, true);
- hclge_fd_disable_user_def(hdev);
-}
-
-static int hclge_restore_fd_entries(struct hnae3_handle *handle)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
-
- /* Return ok here, because reset error handling will check this
- * return value. If error is returned here, the reset process will
- * fail.
- */
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return 0;
-
- /* if fd is disabled, should not restore it when reset */
- if (!hdev->fd_en)
- return 0;
-
- spin_lock_bh(&hdev->fd_rule_lock);
- hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
- if (rule->state == HCLGE_FD_ACTIVE)
- rule->state = HCLGE_FD_TO_ADD;
- }
- spin_unlock_bh(&hdev->fd_rule_lock);
- set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
-
- return 0;
-}
-
-static int hclge_get_fd_rule_cnt(struct hnae3_handle *handle,
- struct ethtool_rxnfc *cmd)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || hclge_is_cls_flower_active(handle))
- return -EOPNOTSUPP;
-
- cmd->rule_cnt = hdev->hclge_fd_rule_num;
- cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
-
- return 0;
-}
-
-static void hclge_fd_get_tcpip4_info(struct hclge_fd_rule *rule,
- struct ethtool_tcpip4_spec *spec,
- struct ethtool_tcpip4_spec *spec_mask)
-{
- spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
- spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
- 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
-
- spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
- spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
- 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
-
- spec->psrc = cpu_to_be16(rule->tuples.src_port);
- spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
- 0 : cpu_to_be16(rule->tuples_mask.src_port);
-
- spec->pdst = cpu_to_be16(rule->tuples.dst_port);
- spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
- 0 : cpu_to_be16(rule->tuples_mask.dst_port);
-
- spec->tos = rule->tuples.ip_tos;
- spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
- 0 : rule->tuples_mask.ip_tos;
-}
-
-static void hclge_fd_get_ip4_info(struct hclge_fd_rule *rule,
- struct ethtool_usrip4_spec *spec,
- struct ethtool_usrip4_spec *spec_mask)
-{
- spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
- spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
- 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
-
- spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
- spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
- 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
-
- spec->tos = rule->tuples.ip_tos;
- spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
- 0 : rule->tuples_mask.ip_tos;
-
- spec->proto = rule->tuples.ip_proto;
- spec_mask->proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
- 0 : rule->tuples_mask.ip_proto;
-
- spec->ip_ver = ETH_RX_NFC_IP4;
-}
-
-static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule,
- struct ethtool_tcpip6_spec *spec,
- struct ethtool_tcpip6_spec *spec_mask)
-{
- ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip);
- ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip);
- if (rule->unused_tuple & BIT(INNER_SRC_IP))
- memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
- else
- ipv6_addr_cpu_to_be32(spec_mask->ip6src,
- rule->tuples_mask.src_ip);
-
- if (rule->unused_tuple & BIT(INNER_DST_IP))
- memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
- else
- ipv6_addr_cpu_to_be32(spec_mask->ip6dst,
- rule->tuples_mask.dst_ip);
-
- spec->tclass = rule->tuples.ip_tos;
- spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
- 0 : rule->tuples_mask.ip_tos;
-
- spec->psrc = cpu_to_be16(rule->tuples.src_port);
- spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
- 0 : cpu_to_be16(rule->tuples_mask.src_port);
-
- spec->pdst = cpu_to_be16(rule->tuples.dst_port);
- spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
- 0 : cpu_to_be16(rule->tuples_mask.dst_port);
-}
-
-static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule,
- struct ethtool_usrip6_spec *spec,
- struct ethtool_usrip6_spec *spec_mask)
-{
- ipv6_addr_cpu_to_be32(spec->ip6src, rule->tuples.src_ip);
- ipv6_addr_cpu_to_be32(spec->ip6dst, rule->tuples.dst_ip);
- if (rule->unused_tuple & BIT(INNER_SRC_IP))
- memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
- else
- ipv6_addr_cpu_to_be32(spec_mask->ip6src,
- rule->tuples_mask.src_ip);
-
- if (rule->unused_tuple & BIT(INNER_DST_IP))
- memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
- else
- ipv6_addr_cpu_to_be32(spec_mask->ip6dst,
- rule->tuples_mask.dst_ip);
-
- spec->tclass = rule->tuples.ip_tos;
- spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
- 0 : rule->tuples_mask.ip_tos;
-
- spec->l4_proto = rule->tuples.ip_proto;
- spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
- 0 : rule->tuples_mask.ip_proto;
-}
-
-static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule,
- struct ethhdr *spec,
- struct ethhdr *spec_mask)
-{
- ether_addr_copy(spec->h_source, rule->tuples.src_mac);
- ether_addr_copy(spec->h_dest, rule->tuples.dst_mac);
-
- if (rule->unused_tuple & BIT(INNER_SRC_MAC))
- eth_zero_addr(spec_mask->h_source);
- else
- ether_addr_copy(spec_mask->h_source, rule->tuples_mask.src_mac);
-
- if (rule->unused_tuple & BIT(INNER_DST_MAC))
- eth_zero_addr(spec_mask->h_dest);
- else
- ether_addr_copy(spec_mask->h_dest, rule->tuples_mask.dst_mac);
-
- spec->h_proto = cpu_to_be16(rule->tuples.ether_proto);
- spec_mask->h_proto = rule->unused_tuple & BIT(INNER_ETH_TYPE) ?
- 0 : cpu_to_be16(rule->tuples_mask.ether_proto);
-}
-
-static void hclge_fd_get_user_def_info(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
-{
- if ((rule->unused_tuple & HCLGE_FD_TUPLE_USER_DEF_TUPLES) ==
- HCLGE_FD_TUPLE_USER_DEF_TUPLES) {
- fs->h_ext.data[0] = 0;
- fs->h_ext.data[1] = 0;
- fs->m_ext.data[0] = 0;
- fs->m_ext.data[1] = 0;
- } else {
- fs->h_ext.data[0] = cpu_to_be32(rule->ep.user_def.offset);
- fs->h_ext.data[1] = cpu_to_be32(rule->ep.user_def.data);
- fs->m_ext.data[0] =
- cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK);
- fs->m_ext.data[1] = cpu_to_be32(rule->ep.user_def.data_mask);
- }
-}
-
-static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
-{
- if (fs->flow_type & FLOW_EXT) {
- fs->h_ext.vlan_tci = cpu_to_be16(rule->tuples.vlan_tag1);
- fs->m_ext.vlan_tci =
- rule->unused_tuple & BIT(INNER_VLAN_TAG_FST) ?
- 0 : cpu_to_be16(rule->tuples_mask.vlan_tag1);
-
- hclge_fd_get_user_def_info(fs, rule);
- }
-
- if (fs->flow_type & FLOW_MAC_EXT) {
- ether_addr_copy(fs->h_ext.h_dest, rule->tuples.dst_mac);
- if (rule->unused_tuple & BIT(INNER_DST_MAC))
- eth_zero_addr(fs->m_u.ether_spec.h_dest);
- else
- ether_addr_copy(fs->m_u.ether_spec.h_dest,
- rule->tuples_mask.dst_mac);
- }
-}
-
-static struct hclge_fd_rule *hclge_get_fd_rule(struct hclge_dev *hdev,
- u16 location)
-{
- struct hclge_fd_rule *rule = NULL;
- struct hlist_node *node2;
-
- hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
- if (rule->location == location)
- return rule;
- else if (rule->location > location)
- return NULL;
- }
-
- return NULL;
-}
-
-static void hclge_fd_get_ring_cookie(struct ethtool_rx_flow_spec *fs,
- struct hclge_fd_rule *rule)
-{
- if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
- fs->ring_cookie = RX_CLS_FLOW_DISC;
- } else {
- u64 vf_id;
-
- fs->ring_cookie = rule->queue_id;
- vf_id = rule->vf_id;
- vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
- fs->ring_cookie |= vf_id;
- }
-}
-
-static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
- struct ethtool_rxnfc *cmd)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_fd_rule *rule = NULL;
- struct hclge_dev *hdev = vport->back;
- struct ethtool_rx_flow_spec *fs;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return -EOPNOTSUPP;
-
- fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
-
- spin_lock_bh(&hdev->fd_rule_lock);
-
- rule = hclge_get_fd_rule(hdev, fs->location);
- if (!rule) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -ENOENT;
- }
-
- fs->flow_type = rule->flow_type;
- switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
- case SCTP_V4_FLOW:
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- hclge_fd_get_tcpip4_info(rule, &fs->h_u.tcp_ip4_spec,
- &fs->m_u.tcp_ip4_spec);
- break;
- case IP_USER_FLOW:
- hclge_fd_get_ip4_info(rule, &fs->h_u.usr_ip4_spec,
- &fs->m_u.usr_ip4_spec);
- break;
- case SCTP_V6_FLOW:
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- hclge_fd_get_tcpip6_info(rule, &fs->h_u.tcp_ip6_spec,
- &fs->m_u.tcp_ip6_spec);
- break;
- case IPV6_USER_FLOW:
- hclge_fd_get_ip6_info(rule, &fs->h_u.usr_ip6_spec,
- &fs->m_u.usr_ip6_spec);
- break;
- /* The flow type of fd rule has been checked before adding in to rule
- * list. As other flow types have been handled, it must be ETHER_FLOW
- * for the default case
- */
- default:
- hclge_fd_get_ether_info(rule, &fs->h_u.ether_spec,
- &fs->m_u.ether_spec);
- break;
- }
-
- hclge_fd_get_ext_info(fs, rule);
-
- hclge_fd_get_ring_cookie(fs, rule);
-
- spin_unlock_bh(&hdev->fd_rule_lock);
-
- return 0;
-}
-
-static int hclge_get_all_rules(struct hnae3_handle *handle,
- struct ethtool_rxnfc *cmd, u32 *rule_locs)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct hclge_fd_rule *rule;
- struct hlist_node *node2;
- u32 cnt = 0;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return -EOPNOTSUPP;
-
- cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
-
- spin_lock_bh(&hdev->fd_rule_lock);
- hlist_for_each_entry_safe(rule, node2,
- &hdev->fd_rule_list, rule_node) {
- if (cnt == cmd->rule_cnt) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -EMSGSIZE;
- }
-
- if (rule->state == HCLGE_FD_TO_DEL)
- continue;
-
- rule_locs[cnt] = rule->location;
- cnt++;
- }
-
- spin_unlock_bh(&hdev->fd_rule_lock);
-
- cmd->rule_cnt = cnt;
-
- return 0;
-}
-
-static void hclge_fd_get_flow_tuples(const struct flow_keys *fkeys,
- struct hclge_fd_rule_tuples *tuples)
-{
-#define flow_ip6_src fkeys->addrs.v6addrs.src.in6_u.u6_addr32
-#define flow_ip6_dst fkeys->addrs.v6addrs.dst.in6_u.u6_addr32
-
- tuples->ether_proto = be16_to_cpu(fkeys->basic.n_proto);
- tuples->ip_proto = fkeys->basic.ip_proto;
- tuples->dst_port = be16_to_cpu(fkeys->ports.dst);
-
- if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
- tuples->src_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.src);
- tuples->dst_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.dst);
- } else {
- int i;
-
- for (i = 0; i < IPV6_ADDR_WORDS; i++) {
- tuples->src_ip[i] = be32_to_cpu(flow_ip6_src[i]);
- tuples->dst_ip[i] = be32_to_cpu(flow_ip6_dst[i]);
- }
- }
-}
-
-/* traverse all rules, check whether an existed rule has the same tuples */
-static struct hclge_fd_rule *
-hclge_fd_search_flow_keys(struct hclge_dev *hdev,
- const struct hclge_fd_rule_tuples *tuples)
-{
- struct hclge_fd_rule *rule = NULL;
- struct hlist_node *node;
-
- hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
- if (!memcmp(tuples, &rule->tuples, sizeof(*tuples)))
- return rule;
- }
-
- return NULL;
-}
-
-static void hclge_fd_build_arfs_rule(const struct hclge_fd_rule_tuples *tuples,
- struct hclge_fd_rule *rule)
-{
- rule->unused_tuple = BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
- BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) |
- BIT(INNER_SRC_PORT);
- rule->action = 0;
- rule->vf_id = 0;
- rule->rule_type = HCLGE_FD_ARFS_ACTIVE;
- rule->state = HCLGE_FD_TO_ADD;
- if (tuples->ether_proto == ETH_P_IP) {
- if (tuples->ip_proto == IPPROTO_TCP)
- rule->flow_type = TCP_V4_FLOW;
- else
- rule->flow_type = UDP_V4_FLOW;
- } else {
- if (tuples->ip_proto == IPPROTO_TCP)
- rule->flow_type = TCP_V6_FLOW;
- else
- rule->flow_type = UDP_V6_FLOW;
- }
- memcpy(&rule->tuples, tuples, sizeof(rule->tuples));
- memset(&rule->tuples_mask, 0xFF, sizeof(rule->tuples_mask));
-}
-
-static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
- u16 flow_id, struct flow_keys *fkeys)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_fd_rule_tuples new_tuples = {};
- struct hclge_dev *hdev = vport->back;
- struct hclge_fd_rule *rule;
- u16 bit_id;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return -EOPNOTSUPP;
-
- /* when there is already fd rule existed add by user,
- * arfs should not work
- */
- spin_lock_bh(&hdev->fd_rule_lock);
- if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE &&
- hdev->fd_active_type != HCLGE_FD_RULE_NONE) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -EOPNOTSUPP;
- }
-
- hclge_fd_get_flow_tuples(fkeys, &new_tuples);
-
- /* check is there flow director filter existed for this flow,
- * if not, create a new filter for it;
- * if filter exist with different queue id, modify the filter;
- * if filter exist with same queue id, do nothing
- */
- rule = hclge_fd_search_flow_keys(hdev, &new_tuples);
- if (!rule) {
- bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM);
- if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -ENOSPC;
- }
-
- rule = kzalloc_obj(*rule, GFP_ATOMIC);
- if (!rule) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -ENOMEM;
- }
-
- rule->location = bit_id;
- rule->arfs.flow_id = flow_id;
- rule->queue_id = queue_id;
- hclge_fd_build_arfs_rule(&new_tuples, rule);
- hclge_update_fd_list(hdev, rule->state, rule->location, rule);
- hdev->fd_active_type = HCLGE_FD_ARFS_ACTIVE;
- } else if (rule->queue_id != queue_id) {
- rule->queue_id = queue_id;
- rule->state = HCLGE_FD_TO_ADD;
- set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
- hclge_task_schedule(hdev, 0);
- }
- spin_unlock_bh(&hdev->fd_rule_lock);
- return rule->location;
-}
-
-static void hclge_rfs_filter_expire(struct hclge_dev *hdev)
-{
-#ifdef CONFIG_RFS_ACCEL
- struct hnae3_handle *handle = &hdev->vport[0].nic;
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
-
- spin_lock_bh(&hdev->fd_rule_lock);
- if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return;
- }
- hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
- if (rule->state != HCLGE_FD_ACTIVE)
- continue;
- if (rps_may_expire_flow(handle->netdev, rule->queue_id,
- rule->arfs.flow_id, rule->location)) {
- rule->state = HCLGE_FD_TO_DEL;
- set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
- }
- }
- spin_unlock_bh(&hdev->fd_rule_lock);
-#endif
-}
-
-/* make sure being called after lock up with fd_rule_lock */
-static int hclge_clear_arfs_rules(struct hclge_dev *hdev)
-{
-#ifdef CONFIG_RFS_ACCEL
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
- int ret;
-
- if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE)
- return 0;
-
- hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
- switch (rule->state) {
- case HCLGE_FD_TO_DEL:
- case HCLGE_FD_ACTIVE:
- ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
- rule->location, NULL, false);
- if (ret)
- return ret;
- fallthrough;
- case HCLGE_FD_TO_ADD:
- hclge_fd_dec_rule_cnt(hdev, rule->location);
- hlist_del(&rule->rule_node);
- kfree(rule);
- break;
- default:
- break;
- }
- }
- hclge_sync_fd_state(hdev);
-
-#endif
- return 0;
-}
-
-static void hclge_get_cls_key_basic(const struct flow_rule *flow,
- struct hclge_fd_rule *rule)
-{
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_BASIC)) {
- struct flow_match_basic match;
- u16 ethtype_key, ethtype_mask;
-
- flow_rule_match_basic(flow, &match);
- ethtype_key = ntohs(match.key->n_proto);
- ethtype_mask = ntohs(match.mask->n_proto);
-
- if (ethtype_key == ETH_P_ALL) {
- ethtype_key = 0;
- ethtype_mask = 0;
- }
- rule->tuples.ether_proto = ethtype_key;
- rule->tuples_mask.ether_proto = ethtype_mask;
- rule->tuples.ip_proto = match.key->ip_proto;
- rule->tuples_mask.ip_proto = match.mask->ip_proto;
- } else {
- rule->unused_tuple |= BIT(INNER_IP_PROTO);
- rule->unused_tuple |= BIT(INNER_ETH_TYPE);
- }
-}
-
-static void hclge_get_cls_key_mac(const struct flow_rule *flow,
- struct hclge_fd_rule *rule)
-{
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- struct flow_match_eth_addrs match;
-
- flow_rule_match_eth_addrs(flow, &match);
- ether_addr_copy(rule->tuples.dst_mac, match.key->dst);
- ether_addr_copy(rule->tuples_mask.dst_mac, match.mask->dst);
- ether_addr_copy(rule->tuples.src_mac, match.key->src);
- ether_addr_copy(rule->tuples_mask.src_mac, match.mask->src);
- if (is_zero_ether_addr(match.key->dst))
- rule->unused_tuple |= BIT(INNER_DST_MAC);
- if (is_zero_ether_addr(match.key->src))
- rule->unused_tuple |= BIT(INNER_SRC_MAC);
- } else {
- rule->unused_tuple |= BIT(INNER_DST_MAC);
- rule->unused_tuple |= BIT(INNER_SRC_MAC);
- }
-}
-
-static void hclge_get_cls_key_vlan(const struct flow_rule *flow,
- struct hclge_fd_rule *rule)
-{
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_VLAN)) {
- struct flow_match_vlan match;
-
- flow_rule_match_vlan(flow, &match);
- rule->tuples.vlan_tag1 = match.key->vlan_id |
- (match.key->vlan_priority << VLAN_PRIO_SHIFT);
- rule->tuples_mask.vlan_tag1 = match.mask->vlan_id |
- (match.mask->vlan_priority << VLAN_PRIO_SHIFT);
- } else {
- rule->unused_tuple |= BIT(INNER_VLAN_TAG_FST);
- }
-}
-
-static int hclge_get_cls_key_ip(const struct flow_rule *flow,
- struct hclge_fd_rule *rule,
- struct netlink_ext_ack *extack)
-{
- u16 addr_type = 0;
-
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) {
- struct flow_match_control match;
-
- flow_rule_match_control(flow, &match);
- addr_type = match.key->addr_type;
-
- if (flow_rule_has_control_flags(match.mask->flags, extack))
- return -EOPNOTSUPP;
- }
-
- if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
- struct flow_match_ipv4_addrs match;
-
- flow_rule_match_ipv4_addrs(flow, &match);
- rule->tuples.src_ip[IPV4_INDEX] = be32_to_cpu(match.key->src);
- rule->tuples_mask.src_ip[IPV4_INDEX] =
- be32_to_cpu(match.mask->src);
- rule->tuples.dst_ip[IPV4_INDEX] = be32_to_cpu(match.key->dst);
- rule->tuples_mask.dst_ip[IPV4_INDEX] =
- be32_to_cpu(match.mask->dst);
- if (!match.key->src)
- rule->unused_tuple |= BIT(INNER_SRC_IP);
- if (!match.key->dst)
- rule->unused_tuple |= BIT(INNER_DST_IP);
- } else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
- struct flow_match_ipv6_addrs match;
-
- flow_rule_match_ipv6_addrs(flow, &match);
- ipv6_addr_be32_to_cpu(rule->tuples.src_ip,
- match.key->src.s6_addr32);
- ipv6_addr_be32_to_cpu(rule->tuples_mask.src_ip,
- match.mask->src.s6_addr32);
- ipv6_addr_be32_to_cpu(rule->tuples.dst_ip,
- match.key->dst.s6_addr32);
- ipv6_addr_be32_to_cpu(rule->tuples_mask.dst_ip,
- match.mask->dst.s6_addr32);
- if (ipv6_addr_any((struct in6_addr *)match.key->src.s6_addr32))
- rule->unused_tuple |= BIT(INNER_SRC_IP);
- if (ipv6_addr_any((struct in6_addr *)match.key->dst.s6_addr32))
- rule->unused_tuple |= BIT(INNER_DST_IP);
- } else {
- rule->unused_tuple |= BIT(INNER_SRC_IP);
- rule->unused_tuple |= BIT(INNER_DST_IP);
- }
-
- return 0;
-}
-
-static void hclge_get_cls_key_port(const struct flow_rule *flow,
- struct hclge_fd_rule *rule)
-{
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_PORTS)) {
- struct flow_match_ports match;
-
- flow_rule_match_ports(flow, &match);
-
- rule->tuples.src_port = be16_to_cpu(match.key->src);
- rule->tuples_mask.src_port = be16_to_cpu(match.mask->src);
- rule->tuples.dst_port = be16_to_cpu(match.key->dst);
- rule->tuples_mask.dst_port = be16_to_cpu(match.mask->dst);
- } else {
- rule->unused_tuple |= BIT(INNER_SRC_PORT);
- rule->unused_tuple |= BIT(INNER_DST_PORT);
- }
-}
-
-static int hclge_get_cls_enc_keyid(struct hclge_dev *hdev,
- const struct flow_rule *flow,
- struct hclge_fd_rule *rule)
-{
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
- struct flow_match_enc_keyid match;
-
- flow_rule_match_enc_keyid(flow, &match);
-
- /* vni is only 24 bits and must be greater than 0,
- * and it can not be masked.
- */
- if (match.mask->keyid != HCLGE_FD_VXLAN_VNI_UNMASK ||
- be32_to_cpu(match.key->keyid) >= VXLAN_N_VID ||
- !match.key->keyid) {
- dev_err(&hdev->pdev->dev, "invalid enc_keyid\n");
- return -EINVAL;
- }
-
- rule->tuples.outer_tun_vni = be32_to_cpu(match.key->keyid);
- rule->tuples_mask.outer_tun_vni =
- be32_to_cpu(match.mask->keyid);
- } else {
- rule->unused_tuple |= BIT(OUTER_TUN_VNI);
- }
-
- return 0;
-}
-
-static void hclge_get_cls_key_ip_tos(const struct flow_rule *flow,
- struct hclge_fd_rule *rule)
-{
- if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) {
- struct flow_match_ip match;
-
- flow_rule_match_ip(flow, &match);
-
- rule->tuples.ip_tos = match.key->tos;
- rule->tuples_mask.ip_tos = match.mask->tos;
- if (!rule->tuples.ip_tos)
- rule->unused_tuple |= BIT(INNER_IP_TOS);
- } else {
- rule->unused_tuple |= BIT(INNER_IP_TOS);
- }
-}
-
-static int hclge_get_tc_flower_action(struct hclge_dev *hdev,
- struct flow_cls_offload *cls_flower,
- struct hclge_fd_rule *rule)
-{
- struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
- struct hnae3_handle *handle = &hdev->vport[0].nic;
- struct flow_action *action = &flow->action;
- struct flow_action_entry *act;
- int tc;
-
- if (!flow_action_has_entries(&flow->action)) {
- tc = tc_classid_to_hwtc(handle->netdev, cls_flower->classid);
- if (tc < 0 || tc > hdev->tc_max) {
- dev_err(&hdev->pdev->dev,
- "invalid traffic class: %d\n", tc);
- return -EINVAL;
- }
-
- rule->action = HCLGE_FD_ACTION_SELECT_TC;
- rule->cls_flower.tc = tc;
- return 0;
- }
-
- act = &action->entries[0];
- switch (act->id) {
- case FLOW_ACTION_RX_QUEUE_MAPPING:
- if (act->rx_queue >= handle->kinfo.num_tqps) {
- dev_err(&hdev->pdev->dev,
- "queue id (%u) should be less than %u\n",
- act->rx_queue, handle->kinfo.num_tqps - 1);
- return -EINVAL;
- }
-
- rule->queue_id = act->rx_queue;
- rule->action = HCLGE_FD_ACTION_SELECT_QUEUE;
- return 0;
- case FLOW_ACTION_DROP:
- rule->action = HCLGE_FD_ACTION_DROP_PACKET;
- return 0;
- default:
- dev_err(&hdev->pdev->dev, "unsupported action(%d)\n", act->id);
- return -EOPNOTSUPP;
- }
-}
-
-static int hclge_parse_cls_flower(struct hclge_dev *hdev,
- struct flow_cls_offload *cls_flower,
- struct hclge_fd_rule *rule)
-{
- struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
- struct netlink_ext_ack *extack = cls_flower->common.extack;
- int ret;
-
- /* not support any user def tuples */
- rule->unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
-
- hclge_get_cls_key_basic(flow, rule);
- hclge_get_cls_key_mac(flow, rule);
- hclge_get_cls_key_vlan(flow, rule);
-
- ret = hclge_get_cls_key_ip(flow, rule, extack);
- if (ret)
- return ret;
-
- hclge_get_cls_key_port(flow, rule);
- hclge_get_cls_key_ip_tos(flow, rule);
-
- return hclge_get_cls_enc_keyid(hdev, flow, rule);
-}
-
-static int hclge_check_cls_flower(struct hclge_dev *hdev,
- struct flow_cls_offload *cls_flower)
-{
- struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
- struct flow_dissector *dissector = flow->match.dissector;
- u32 prio = cls_flower->common.prio;
-
- if (prio == 0 ||
- prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
- dev_err(&hdev->pdev->dev,
- "prio %u should be in range[1, %u]\n",
- prio, hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
- return -EINVAL;
- }
-
- if (test_bit(prio - 1, hdev->fd_bmap)) {
- dev_err(&hdev->pdev->dev, "prio %u is already used\n", prio);
- return -EINVAL;
- }
-
- if (dissector->used_keys &
- ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
- BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
- dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n",
- dissector->used_keys);
- return -EOPNOTSUPP;
- }
-
- /* driver will parses classid into an action */
- if (cls_flower->classid && flow_action_has_entries(&flow->action)) {
- dev_err(&hdev->pdev->dev,
- "please not set classid and action together\n");
- return -EOPNOTSUPP;
- }
-
- if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) {
- dev_err(&hdev->pdev->dev, "please set action or classid\n");
- return -EINVAL;
- }
-
- if (flow_action_has_entries(&flow->action) &&
- !flow_offload_has_one_action(&flow->action)) {
- dev_err(&hdev->pdev->dev, "unsupported multiple actions\n");
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-static int hclge_add_cls_flower(struct hnae3_handle *handle,
- struct flow_cls_offload *cls_flower)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct hclge_fd_rule *rule;
- int ret;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
- dev_err(&hdev->pdev->dev,
- "cls flower is not supported\n");
- return -EOPNOTSUPP;
- }
-
- ret = hclge_check_cls_flower(hdev, cls_flower);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "failed to check cls flower params, ret = %d\n", ret);
- return ret;
- }
-
- rule = kzalloc_obj(*rule);
- if (!rule)
- return -ENOMEM;
-
- ret = hclge_parse_cls_flower(hdev, cls_flower, rule);
- if (ret) {
- kfree(rule);
- return ret;
- }
-
- ret = hclge_get_tc_flower_action(hdev, cls_flower, rule);
- if (ret) {
- kfree(rule);
- return ret;
- }
-
- rule->location = cls_flower->common.prio - 1;
- rule->vf_id = 0;
- rule->cls_flower.cookie = cls_flower->cookie;
- rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE;
-
- ret = hclge_add_fd_entry_common(hdev, rule);
- if (ret)
- kfree(rule);
-
- return ret;
-}
-
-static struct hclge_fd_rule *hclge_find_cls_flower(struct hclge_dev *hdev,
- unsigned long cookie)
-{
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
-
- hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
- if (rule->cls_flower.cookie == cookie)
- return rule;
- }
-
- return NULL;
-}
-
-static int hclge_del_cls_flower(struct hnae3_handle *handle,
- struct flow_cls_offload *cls_flower)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
- struct hclge_fd_rule *rule;
- int ret;
-
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return -EOPNOTSUPP;
-
- spin_lock_bh(&hdev->fd_rule_lock);
-
- rule = hclge_find_cls_flower(hdev, cls_flower->cookie);
- if (!rule) {
- spin_unlock_bh(&hdev->fd_rule_lock);
- return -EINVAL;
- }
-
- ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location,
- NULL, false);
- if (ret) {
- /* if tcam config fail, set rule state to TO_DEL,
- * so the rule will be deleted when periodic
- * task being scheduled.
- */
- hclge_update_fd_list(hdev, HCLGE_FD_TO_DEL, rule->location, NULL);
- set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
- spin_unlock_bh(&hdev->fd_rule_lock);
- return ret;
- }
-
- hclge_update_fd_list(hdev, HCLGE_FD_DELETED, rule->location, NULL);
- spin_unlock_bh(&hdev->fd_rule_lock);
-
- return 0;
-}
-
-static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist)
-{
- struct hclge_fd_rule *rule;
- struct hlist_node *node;
- int ret = 0;
-
- if (!test_and_clear_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state))
- return;
-
- spin_lock_bh(&hdev->fd_rule_lock);
-
- hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
- switch (rule->state) {
- case HCLGE_FD_TO_ADD:
- ret = hclge_fd_config_rule(hdev, rule);
- if (ret)
- goto out;
- rule->state = HCLGE_FD_ACTIVE;
- break;
- case HCLGE_FD_TO_DEL:
- ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
- rule->location, NULL, false);
- if (ret)
- goto out;
- hclge_fd_dec_rule_cnt(hdev, rule->location);
- hclge_fd_free_node(hdev, rule);
- break;
- default:
- break;
- }
- }
-
-out:
- if (ret)
- set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
-
- spin_unlock_bh(&hdev->fd_rule_lock);
-}
-
-static void hclge_sync_fd_table(struct hclge_dev *hdev)
-{
- if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
- return;
-
- if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) {
- bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE;
-
- hclge_clear_fd_rules_in_list(hdev, clear_list);
- }
-
- hclge_sync_fd_user_def_cfg(hdev, false);
-
- hclge_sync_fd_list(hdev, &hdev->fd_rule_list);
-}
-
-static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- return hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG) ||
- hclge_read_dev(&hdev->hw, HCLGE_FUN_RST_ING);
-}
-
-static bool hclge_get_cmdq_stat(struct hnae3_handle *handle)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- return test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
-}
-
-static bool hclge_ae_dev_resetting(struct hnae3_handle *handle)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- return test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
-}
-
-static unsigned long hclge_ae_dev_reset_cnt(struct hnae3_handle *handle)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- return hdev->rst_stats.hw_reset_done_cnt;
-}
-
-static void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
-{
- struct hclge_vport *vport = hclge_get_vport(handle);
- struct hclge_dev *hdev = vport->back;
-
- hdev->fd_en = enable;
-
- if (!enable)
- set_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state);
- else
- hclge_restore_fd_entries(handle);
-
- hclge_task_schedule(hdev, 0);
-}
-
-static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
-{
-#define HCLGE_LINK_STATUS_WAIT_CNT 3
-
- struct hclge_desc desc;
- struct hclge_config_mac_mode_cmd *req =
- (struct hclge_config_mac_mode_cmd *)desc.data;
- u32 loop_en = 0;
- int ret;
-
- hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false);
-
- if (enable) {
- hnae3_set_bit(loop_en, HCLGE_MAC_TX_EN_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_RX_EN_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_PAD_TX_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_PAD_RX_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_FCS_TX_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_STRIP_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, 1U);
- hnae3_set_bit(loop_en, HCLGE_MAC_TX_UNDER_MIN_ERR_B, 1U);
- }
-
- req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en);
-
- ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret) {
- dev_err(&hdev->pdev->dev,
- "mac enable fail, ret =%d.\n", ret);
- return;
- }
-
- if (!enable)
- hclge_mac_link_status_wait(hdev, HCLGE_LINK_STATUS_DOWN,
- HCLGE_LINK_STATUS_WAIT_CNT);
-}
-
static int hclge_config_switch_param(struct hclge_dev *hdev, int vfid,
u8 switch_param, u8 param_mask)
{
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index ccb19d960690..87adeb64e6ea 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -1179,4 +1179,6 @@ int hclge_mac_update_stats(struct hclge_dev *hdev);
struct hclge_vport *hclge_get_vf_vport(struct hclge_dev *hdev, int vf);
int hclge_inform_vf_reset(struct hclge_vport *vport, u16 reset_type);
int hclge_query_scc_version(struct hclge_dev *hdev, u32 *scc_version);
+u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id,
+ u8 vf_id, u8 network_port_id);
#endif
--
2.33.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH net-next 4/6] net: hns3: support IP and tunnel VNI dissectors for tc flow
2026-05-18 9:35 ` [PATCH net-next 4/6] net: hns3: support IP and tunnel VNI dissectors " Jijie Shao
@ 2026-05-20 1:48 ` Jakub Kicinski
0 siblings, 0 replies; 9+ messages in thread
From: Jakub Kicinski @ 2026-05-20 1:48 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, andrew+netdev, horms, shenjian15,
liuyonglong, chenhao418, huangdonghua3, yangshuaisong, netdev,
linux-kernel
On Mon, 18 May 2026 17:35:24 +0800 Jijie Shao wrote:
> Currently, the driver does not support FLOW_DISSECTOR_KEY_IP and
> FLOW_DISSECTOR_KEY_ENC_KEYID. But the hardware supports
> ip_tos (FLOW_DISSECTOR_KEY_IP) and
> outer_tun_vni (FLOW_DISSECTOR_KEY_ENC_KEYID).
>
> This patch adds support for FLOW_DISSECTOR_KEY_IP and
> FLOW_DISSECTOR_KEY_ENC_KEYID.
>
> Additionally, since tc flow cannot effectively support
> l2_user_def, l3_user_def, and l4_user_def,
> this patch explicitly sets them to not be used.
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c:5771:37: warning: restricted __le32 degrades to integer
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c:5773:37: warning: restricted __le32 degrades to integer
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c:7369:31: warning: restricted __be32 degrades to integer
--
pw-bot: cr
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH net-next 5/6] net: hns3: debugfs support for dumping fd rules
2026-05-18 9:35 ` [PATCH net-next 5/6] net: hns3: debugfs support for dumping fd rules Jijie Shao
@ 2026-05-20 1:49 ` Jakub Kicinski
0 siblings, 0 replies; 9+ messages in thread
From: Jakub Kicinski @ 2026-05-20 1:49 UTC (permalink / raw)
To: Jijie Shao
Cc: davem, edumazet, pabeni, andrew+netdev, horms, shenjian15,
liuyonglong, chenhao418, huangdonghua3, yangshuaisong, netdev,
linux-kernel
On Mon, 18 May 2026 17:35:25 +0800 Jijie Shao wrote:
> Currently, the tc tool only supports adding and deleting rules from
> the driver but does not support querying rules from the driver.
>
> This patch adds a rule dump file in debugfs to check whether the driver's
> configuration matches the configuration issued by tc flow.
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c:2155:25: warning: diagnostic behavior may be improved by adding the 'format(printf, 5, 7)' attribute to the declaration of 'hclge_fd_dump_ptr' [-Wmissing-format-attribute]
2141 | static void hclge_fd_dump_ptr(struct seq_file *s,
| __attribute__((format(printf, 5, 7)))
2142 | struct hclge_fd_rule *rule,
2143 | u32 type, const char *name,
2144 | const char *fmt,
2145 | const void *key, const void *mask)
2146 | {
2147 | if (rule->unused_tuple & BIT(type))
2148 | return;
2149 |
2150 | seq_printf(s, "\t\t%s: ", name);
2151 | seq_printf(s, fmt, key);
2152 | seq_putc(s, '\n');
2153 |
2154 | seq_printf(s, "\t\t%s_mask: ", name);
2155 | seq_printf(s, fmt, mask);
| ^
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-05-20 1:49 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-18 9:35 [PATCH net-next 0/6] net: hns3: enhance tc flow offload support Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 1/6] net: hns3: adjust add_cls_flower() for support more action Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 2/6] net: hns3: improve the unused_tuple parameter setting Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 3/6] net: hns3: support two more actions for tc flow Jijie Shao
2026-05-18 9:35 ` [PATCH net-next 4/6] net: hns3: support IP and tunnel VNI dissectors " Jijie Shao
2026-05-20 1:48 ` Jakub Kicinski
2026-05-18 9:35 ` [PATCH net-next 5/6] net: hns3: debugfs support for dumping fd rules Jijie Shao
2026-05-20 1:49 ` Jakub Kicinski
2026-05-18 9:35 ` [PATCH net-next 6/6] net: hns3: move fd code to a separate file Jijie Shao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox