From nobody Thu Jun 11 08:50:44 2026 Received: from canpmsgout03.his.huawei.com (canpmsgout03.his.huawei.com [113.46.200.218]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 04051371CF9; Wed, 10 Jun 2026 06:06:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.218 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071605; cv=none; b=dTlhkHzhaCTDH3JwJs9zhbqz2KZOgtemP936hbZqKnC+CHXYIl9SAylobl29irGos7mtfsltn50yPfFOGeVwv1eV7gvvZVt5XvjbX+R3v3IHLGeNI9dWLCgfe6TwHMbp6exJHOpqKOORIpdiSjyeghyIUPZMrFj7H3PtENEyEEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071605; c=relaxed/simple; bh=ORL8HEf+gZmLveijujYpM4411zyoXEaocR/ndIm+bD0=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=q7dDPwwohXqEeDWIcUOSuGl8GFnSHvIiVuNCF0G8FFX7kVnLEMpfJnUz48CtZR5b0KdQMO7xxS+k2jTqOWQI1Umgnz3LS6k25j2/u7wgCP/sdCm9fbMVVDmEM4o6fp3rDcf4yNbobdSYGykaRUwnWRAOyyyB+ElXJAWB010/TBM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=TbFXtTxP; arc=none smtp.client-ip=113.46.200.218 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="TbFXtTxP" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=44osuMQq6VbJ68FcJd6eZjXdOVAFOTnYwPWqDkLeeGk=; b=TbFXtTxP2ccWtgK4q7snOIHVn4XGXoCUur/1ZFy4u+e6SOTPS95as9kWp/WXqsAB7uDX6idJC txc9WpoDk/eFV2IL6HilB/Yw8/rVqrI7bZRK/kSjtcCNoNO9pjoI7xD0bAD2bIjL2vEqsxbZqHX zSYQ4hKgPkHnkbEJQTFfIeI= Received: from mail.maildlp.com (unknown [172.19.162.140]) by canpmsgout03.his.huawei.com (SkyGuard) with ESMTPS id 4gZw7m1C35zpStT; Wed, 10 Jun 2026 13:58:44 +0800 (CST) Received: from kwepemk100013.china.huawei.com (unknown [7.202.194.61]) by mail.maildlp.com (Postfix) with ESMTPS id A9D5D203B1; Wed, 10 Jun 2026 14:06:34 +0800 (CST) Received: from localhost.huawei.com (10.90.31.46) by kwepemk100013.china.huawei.com (7.202.194.61) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Wed, 10 Jun 2026 14:06:33 +0800 From: Jijie Shao To: , , , , , CC: , , , , , , , Subject: [PATCH V4 net-next 1/6] net: hns3: refactor add_cls_flower to prepare for multiple actions Date: Wed, 10 Jun 2026 14:06:13 +0800 Message-ID: <20260610060618.834987-2-shaojijie@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260610060618.834987-1-shaojijie@huawei.com> References: <20260610060618.834987-1-shaojijie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems200002.china.huawei.com (7.221.188.68) To kwepemk100013.china.huawei.com (7.202.194.61) Content-Type: text/plain; charset="utf-8" Remove the tc parameter from the add_cls_flower() ops callback and refactor action parsing to support future extensions for SELECT_QUEUE and DROP_PACKET actions. Changes: * Remove the tc parameter from the add_cls_flower() callback signature. * Extract TC-based action parsing into hclge_get_tc_flower_action(). * Move the dissector->used_keys check from hclge_parse_cls_flower() to hclge_check_cls_flower(), and restrict ETH_ADDRS to HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1 mode since hardware only supports MAC matching there. * Migrate error reporting from dev_err() to netlink extended ACK (extack). Signed-off-by: Jijie Shao --- drivers/net/ethernet/hisilicon/hns3/hnae3.h | 2 +- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 3 +- .../hisilicon/hns3/hns3pf/hclge_main.c | 93 ++++++++++++------- 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethe= rnet/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 =3D tc_classid_to_hwtc(priv->netdev, flow->classid); struct hnae3_handle *h =3D hns3_get_handle(priv->netdev); =20 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/driv= ers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index e17b92a411a2..77bd23e2c11e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -7328,28 +7328,33 @@ static void hclge_get_cls_key_port(const struct flo= w_rule *flow, } } =20 +static int hclge_get_tc_flower_action(struct hclge_dev *hdev, + struct flow_cls_offload *cls_flower, + struct hclge_fd_rule *rule) +{ + struct netlink_ext_ack *extack =3D cls_flower->common.extack; + struct hnae3_handle *handle =3D &hdev->vport[0].nic; + int tc; + + tc =3D tc_classid_to_hwtc(handle->netdev, cls_flower->classid); + if (tc < 0 || tc > hdev->tc_max) { + NL_SET_ERR_MSG_FMT_MOD(extack, "invalid traffic class: %d", tc); + return -EINVAL; + } + + rule->action =3D HCLGE_FD_ACTION_SELECT_TC; + rule->cls_flower.tc =3D 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 =3D flow_cls_offload_flow_rule(cls_flower); struct netlink_ext_ack *extack =3D cls_flower->common.extack; - struct flow_dissector *dissector =3D flow->match.dissector; int ret; =20 - 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,51 +7369,65 @@ static int hclge_parse_cls_flower(struct hclge_dev = *hdev, } =20 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 =3D flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack =3D cls_flower->common.extack; + struct flow_dissector *dissector =3D flow->match.dissector; u32 prio =3D cls_flower->common.prio; - - if (tc < 0 || tc > hdev->tc_max) { - dev_err(&hdev->pdev->dev, "invalid traffic class\n"); - return -EINVAL; - } + u64 support_keys; =20 if (prio =3D=3D 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]); + NL_SET_ERR_MSG_FMT_MOD(extack, + "prio %u should be in range[1, %u]", + prio, + hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); return -EINVAL; } =20 if (test_bit(prio - 1, hdev->fd_bmap)) { - dev_err(&hdev->pdev->dev, "prio %u is already used\n", prio); + NL_SET_ERR_MSG_FMT_MOD(extack, + "prio %u is already used", prio); return -EINVAL; } + + support_keys =3D BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + 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); + + if (hdev->fd_cfg.fd_mode =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) + support_keys |=3D BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS); + + if (dissector->used_keys & ~support_keys) { + NL_SET_ERR_MSG_FMT_MOD(extack, "unsupported key set: %#llx", + dissector->used_keys); + return -EOPNOTSUPP; + } + return 0; } =20 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 netlink_ext_ack *extack =3D cls_flower->common.extack; struct hclge_vport *vport =3D hclge_get_vport(handle); struct hclge_dev *hdev =3D vport->back; struct hclge_fd_rule *rule; int ret; =20 if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { - dev_err(&hdev->pdev->dev, - "cls flower is not supported\n"); + NL_SET_ERR_MSG_MOD(extack, "cls flower is not supported"); return -EOPNOTSUPP; } =20 - ret =3D hclge_check_cls_flower(hdev, cls_flower, tc); - if (ret) { - dev_err(&hdev->pdev->dev, - "failed to check cls flower params, ret =3D %d\n", ret); + ret =3D hclge_check_cls_flower(hdev, cls_flower); + if (ret) return ret; - } =20 rule =3D kzalloc_obj(*rule); if (!rule) @@ -7420,8 +7439,12 @@ static int hclge_add_cls_flower(struct hnae3_handle = *handle, return ret; } =20 - rule->action =3D HCLGE_FD_ACTION_SELECT_TC; - rule->cls_flower.tc =3D tc; + ret =3D hclge_get_tc_flower_action(hdev, cls_flower, rule); + if (ret) { + kfree(rule); + return ret; + } + rule->location =3D cls_flower->common.prio - 1; rule->vf_id =3D 0; rule->cls_flower.cookie =3D cls_flower->cookie; --=20 2.33.0 From nobody Thu Jun 11 08:50:44 2026 Received: from canpmsgout03.his.huawei.com (canpmsgout03.his.huawei.com [113.46.200.218]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6B281371D08; Wed, 10 Jun 2026 06:06:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.218 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071606; cv=none; b=ZDs1WtBUHOMtfgPUj1kawUUAvGW9CY1QpdEm1EMQr+EKq1urnVHexKZ/MCqBmFbOn8cyOqphNajvb/M9/xPieFhPEdNbv3G9sVBEoR3F0fxDK3+yT0zHWGWwZ4Eu5KwgHfu4KxWHiSMfx4m1fmZjR4Ze28jG8RQ0hLdA5cw5dtM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071606; c=relaxed/simple; bh=uOgRtPaKijMc1G5rKKcKi+qMjlVdXR4JVHankMdJbbA=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=CSZyyTNNBOTiMyh5lGOp/ZIqW4j5zh9wozcrBcY6dlv5Ik8o6/ALtf0lxRrsgM/FhUidIaGWZ1hPQskGkYuFfk53HX5txjAXjnyCdWpoy+hIGQVtZyjbE9r88DXtkQYDeuXm8+zYMIHiGCQDvzXx/B4/3JNKt29c5nFhaFvj7WU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=jETP5Knk; arc=none smtp.client-ip=113.46.200.218 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="jETP5Knk" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=AaXfUqN8wD/nqgz+Hx/HK+nzDb8Bsvr3DwLjrV4kuOw=; b=jETP5KnkTndsjoMem6CTlQxapw3Q5GSmdyEHu8xCb8CApvIjdwtPLFTjLk/h3uivNveSCteEC zyztqQ1VVsEQVrED5K+U+kHPaEoy92dNaRZOTRI+6EO2FMSft7fr5k+bbtMdXcFH0hjjhFQNxLW PT7P9Cute51BPEIYqK8OtY4= Received: from mail.maildlp.com (unknown [172.19.162.140]) by canpmsgout03.his.huawei.com (SkyGuard) with ESMTPS id 4gZw7m5L5zzpStT; Wed, 10 Jun 2026 13:58:44 +0800 (CST) Received: from kwepemk100013.china.huawei.com (unknown [7.202.194.61]) by mail.maildlp.com (Postfix) with ESMTPS id 4631A203B7; Wed, 10 Jun 2026 14:06:35 +0800 (CST) Received: from localhost.huawei.com (10.90.31.46) by kwepemk100013.china.huawei.com (7.202.194.61) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Wed, 10 Jun 2026 14:06:34 +0800 From: Jijie Shao To: , , , , , CC: , , , , , , , Subject: [PATCH V4 net-next 2/6] net: hns3: improve the unused_tuple parameter setting Date: Wed, 10 Jun 2026 14:06:14 +0800 Message-ID: <20260610060618.834987-3-shaojijie@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260610060618.834987-1-shaojijie@huawei.com> References: <20260610060618.834987-1-shaojijie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems200002.china.huawei.com (7.221.188.68) To kwepemk100013.china.huawei.com (7.202.194.61) Content-Type: text/plain; charset="utf-8" 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 mask 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 mask is all zero, the tuple is not configured. In this case, the driver adds the tuple to unused_tuple. Signed-off-by: Jijie Shao --- .../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/driv= ers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 77bd23e2c11e..176ea5aac8aa 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.mask->dst)) + rule->unused_tuple |=3D BIT(INNER_DST_MAC); + if (is_zero_ether_addr(match.mask->src)) + rule->unused_tuple |=3D BIT(INNER_SRC_MAC); } else { rule->unused_tuple |=3D BIT(INNER_DST_MAC); rule->unused_tuple |=3D BIT(INNER_SRC_MAC); @@ -7290,6 +7294,10 @@ static int hclge_get_cls_key_ip(const struct flow_ru= le *flow, rule->tuples.dst_ip[IPV4_INDEX] =3D be32_to_cpu(match.key->dst); rule->tuples_mask.dst_ip[IPV4_INDEX] =3D be32_to_cpu(match.mask->dst); + if (!match.mask->src) + rule->unused_tuple |=3D BIT(INNER_SRC_IP); + if (!match.mask->dst) + rule->unused_tuple |=3D BIT(INNER_DST_IP); } else if (addr_type =3D=3D FLOW_DISSECTOR_KEY_IPV6_ADDRS) { struct flow_match_ipv6_addrs match; =20 @@ -7302,6 +7310,10 @@ static int hclge_get_cls_key_ip(const struct flow_ru= le *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(&match.mask->src)) + rule->unused_tuple |=3D BIT(INNER_SRC_IP); + if (ipv6_addr_any(&match.mask->dst)) + rule->unused_tuple |=3D BIT(INNER_DST_IP); } else { rule->unused_tuple |=3D BIT(INNER_SRC_IP); rule->unused_tuple |=3D BIT(INNER_DST_IP); --=20 2.33.0 From nobody Thu Jun 11 08:50:44 2026 Received: from canpmsgout03.his.huawei.com (canpmsgout03.his.huawei.com [113.46.200.218]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2EEEC368D4D; Wed, 10 Jun 2026 06:06:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.218 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071605; cv=none; b=cb5L6lp4xW9nfrU0unOx7cvTgELr/FOEJyIwv4dw/pwnYIYSlGv+cygueVVG5xfnD6nv2mfPtZbJOfoAMx6fL8NFIkdFqCDoUuo7XGv2uX9o4oXQpfujlLVPsH4AI6J3FIPlMXYpmpXdHwdjs4LL9xUj4q8dDd1up72y2Y9VHUM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071605; c=relaxed/simple; bh=OFwUD5u1SoDZUrt5BR+USGXeaypfWd/TJRpgiwRlTco=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Rxl6jTMnBXs1Z2G1oH6TuzkLB1ckKLfLU6UqN6JQWlwqjRVhgOVPeOAMe8LckNxdKqa1uTx90Whb/DEVBfwvxVmf5w3NyCRmzwgy9uhrOgA72Hgy1c+r+BRB/8jyjxJCspHp4+f/q+QK0E1fA9wjuPwTVsErT1e4KtV8sMJNa5I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=d6qx6F//; arc=none smtp.client-ip=113.46.200.218 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="d6qx6F//" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=0nVw7NtXG7zJSwe8+BGxCBrF5QqZhskdhRg8AYGXQvM=; b=d6qx6F//EisVJ6ro3AQ+rk0jAPJUAFlRu/lHvJF6DuGCmvYfjPgHU8oikeyH6p2BwB2y5sFnA GmX/y/OU02BBMV6KcgWRx4OmREI/F75Gz+G0DDW8XNer5VCVsaUBIt8E42MSFAGCZLtJd//OaAQ rDRoqJWRflEJoJJgbb1r2Pg= Received: from mail.maildlp.com (unknown [172.19.162.197]) by canpmsgout03.his.huawei.com (SkyGuard) with ESMTPS id 4gZw7n1tmXzpTJt; Wed, 10 Jun 2026 13:58:45 +0800 (CST) Received: from kwepemk100013.china.huawei.com (unknown [7.202.194.61]) by mail.maildlp.com (Postfix) with ESMTPS id BCCF440569; Wed, 10 Jun 2026 14:06:35 +0800 (CST) Received: from localhost.huawei.com (10.90.31.46) by kwepemk100013.china.huawei.com (7.202.194.61) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Wed, 10 Jun 2026 14:06:35 +0800 From: Jijie Shao To: , , , , , CC: , , , , , , , Subject: [PATCH V4 net-next 3/6] net: hns3: support two more actions for tc flow Date: Wed, 10 Jun 2026 14:06:15 +0800 Message-ID: <20260610060618.834987-4-shaojijie@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260610060618.834987-1-shaojijie@huawei.com> References: <20260610060618.834987-1-shaojijie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems200002.china.huawei.com (7.221.188.68) To kwepemk100013.china.huawei.com (7.202.194.61) Content-Type: text/plain; charset="utf-8" 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 --- .../hisilicon/hns3/hns3pf/hclge_main.c | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/driv= ers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 176ea5aac8aa..ed7cd0a0d267 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -7344,19 +7344,49 @@ 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 =3D flow_cls_offload_flow_rule(cls_flower); struct netlink_ext_ack *extack =3D cls_flower->common.extack; struct hnae3_handle *handle =3D &hdev->vport[0].nic; + struct flow_action *action =3D &flow->action; + struct flow_action_entry *act; int tc; =20 - tc =3D tc_classid_to_hwtc(handle->netdev, cls_flower->classid); - if (tc < 0 || tc > hdev->tc_max) { - NL_SET_ERR_MSG_FMT_MOD(extack, "invalid traffic class: %d", tc); - return -EINVAL; + if (!flow_action_has_entries(&flow->action)) { + tc =3D tc_classid_to_hwtc(handle->netdev, cls_flower->classid); + if (tc < 0 || tc > hdev->tc_max) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "invalid traffic class: %d", + tc); + return -EINVAL; + } + + rule->action =3D HCLGE_FD_ACTION_SELECT_TC; + rule->cls_flower.tc =3D tc; + return 0; } =20 - rule->action =3D HCLGE_FD_ACTION_SELECT_TC; - rule->cls_flower.tc =3D tc; - return 0; + act =3D &action->entries[0]; + switch (act->id) { + case FLOW_ACTION_RX_QUEUE_MAPPING: + if (act->rx_queue >=3D handle->kinfo.num_tqps) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "queue id (%u) should be less than %u", + act->rx_queue, + handle->kinfo.num_tqps); + return -EINVAL; + } + + rule->queue_id =3D act->rx_queue; + rule->action =3D HCLGE_FD_ACTION_SELECT_QUEUE; + return 0; + case FLOW_ACTION_DROP: + rule->action =3D HCLGE_FD_ACTION_DROP_PACKET; + return 0; + default: + NL_SET_ERR_MSG_FMT_MOD(extack, + "unsupported action(%d)", act->id); + return -EOPNOTSUPP; + } } =20 static int hclge_parse_cls_flower(struct hclge_dev *hdev, @@ -7420,6 +7450,25 @@ static int hclge_check_cls_flower(struct hclge_dev *= hdev, return -EOPNOTSUPP; } =20 + /* driver will parses classid into an action */ + if (cls_flower->classid && flow_action_has_entries(&flow->action)) { + NL_SET_ERR_MSG_MOD(extack, + "cannot specify both classid and action"); + return -EOPNOTSUPP; + } + + if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) { + NL_SET_ERR_MSG_MOD(extack, + "must specify either classid or action"); + return -EINVAL; + } + + if (flow_action_has_entries(&flow->action) && + !flow_offload_has_one_action(&flow->action)) { + NL_SET_ERR_MSG_MOD(extack, "unsupported multiple actions"); + return -EOPNOTSUPP; + } + return 0; } =20 --=20 2.33.0 From nobody Thu Jun 11 08:50:44 2026 Received: from canpmsgout11.his.huawei.com (canpmsgout11.his.huawei.com [113.46.200.226]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 658A5282F2A; Wed, 10 Jun 2026 06:06:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.226 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071603; cv=none; b=glCC13jkzxOwUtDDN6RPhmvbBRRNsE0IDj7zpmtqnfQNlimePEQcvQPPhvAtjBZ316DRylm8WFtwFP9djLS1VzO8X5jkBMLe5E9ImQf5zylhL7t3Bnue9VE+CBgAtx1TV6bOQvBMrDuoo4TUFD+VigmbvCiEindv30Tx8oboJQ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071603; c=relaxed/simple; bh=qUraGaKDVyw3heR703LGav4EN88vkDUqa5PTFzYHybU=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AtCVRDrCFG1R5l1QtAdEx06cyid4BUN26EEyqFBssNV6I4XRUj5IqNTHQbNg9WsTe5WoQrDvS3mI15y7tpirnkfkXVBPMU7dZxInX7OV9gO4+sy2CJGyS/LsFuwwK+2Z34MWiP8R35SM4IselyrzvhIOJe6wD7+Iy6MVwJTSJyk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=Ge/OgChH; arc=none smtp.client-ip=113.46.200.226 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="Ge/OgChH" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=SnSRLf03Jzzsy2DIn0FAY/xl4V+1/cHYD9HQEnA5J+o=; b=Ge/OgChHz3Vo4lF+idoy7dm1/ptDLdvuPXik6D3MFEsr4buZmEu+bOwPAmLVjQVzQk0zVSDt5 ERIDW9KYdxw+4Al5cqL9Pn+98RbM/IkmUZA4ezR3X+04wbZqHV7R1M220wJ+tL+zCemhigZWA4q yIhihrSwuz12pDmpnXbLUzw= Received: from mail.maildlp.com (unknown [172.19.163.127]) by canpmsgout11.his.huawei.com (SkyGuard) with ESMTPS id 4gZw7g4xfDzKm4Y; Wed, 10 Jun 2026 13:58:39 +0800 (CST) Received: from kwepemk100013.china.huawei.com (unknown [7.202.194.61]) by mail.maildlp.com (Postfix) with ESMTPS id 4BA0440572; Wed, 10 Jun 2026 14:06:36 +0800 (CST) Received: from localhost.huawei.com (10.90.31.46) by kwepemk100013.china.huawei.com (7.202.194.61) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Wed, 10 Jun 2026 14:06:35 +0800 From: Jijie Shao To: , , , , , CC: , , , , , , , Subject: [PATCH V4 net-next 4/6] net: hns3: support IP and tunnel VNI dissectors for tc flow Date: Wed, 10 Jun 2026 14:06:16 +0800 Message-ID: <20260610060618.834987-5-shaojijie@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260610060618.834987-1-shaojijie@huawei.com> References: <20260610060618.834987-1-shaojijie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems200002.china.huawei.com (7.221.188.68) To kwepemk100013.china.huawei.com (7.202.194.61) Content-Type: text/plain; charset="utf-8" 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 --- .../hisilicon/hns3/hns3pf/hclge_main.c | 91 +++++++++++++++++-- .../hisilicon/hns3/hns3pf/hclge_main.h | 4 + 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/driv= ers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index ed7cd0a0d267..b8d4858fe39a 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 =20 #include - +#include #include "hclge_cmd.h" #include "hclge_dcb.h" #include "hclge_main.h" @@ -337,7 +337,9 @@ static const struct key_info tuple_key_info[] =3D { { 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 *hde= v) =20 /* If use max 400bit key, we can support tuples for ether type */ if (hdev->fd_cfg.fd_mode =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1= ) { - key_cfg->tuple_active |=3D - BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC); + key_cfg->tuple_active |=3D BIT(INNER_DST_MAC) | + BIT(INNER_SRC_MAC) | + BIT(OUTER_TUN_VNI); if (hdev->ae_dev->dev_version >=3D HNAE3_DEVICE_VERSION_V3) key_cfg->tuple_active |=3D HCLGE_FD_TUPLE_USER_DEF_TUPLES; } @@ -5713,6 +5716,7 @@ static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 = *key_x, u8 *key_y, u16 tmp_x_s, tmp_y_s; u32 tmp_x_l, tmp_y_l; u8 *p =3D (u8 *)rule; + __le32 le_x, le_y; int i; =20 if (rule->unused_tuple & BIT(tuple_bit)) @@ -5760,6 +5764,15 @@ static bool hclge_fd_convert_tuple(u32 tuple_bit, u8= *key_x, u8 *key_y, *(__le32 *)key_x =3D cpu_to_le32(tmp_x_l); *(__le32 *)key_y =3D cpu_to_le32(tmp_y_l); =20 + 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])); + le_x =3D cpu_to_le32(tmp_x_l); + le_y =3D cpu_to_le32(tmp_y_l); + memcpy(key_x, &le_x, HCLGE_VNI_LENGTH); + memcpy(key_y, &le_y, HCLGE_VNI_LENGTH); + return true; default: return false; @@ -7340,6 +7353,62 @@ static void hclge_get_cls_key_port(const struct flow= _rule *flow, } } =20 +static int hclge_get_cls_enc_keyid(struct hclge_dev *hdev, + const struct flow_rule *flow, + struct hclge_fd_rule *rule, + struct netlink_ext_ack *extack) +{ + 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 (be32_to_cpu(match.mask->keyid) !=3D + HCLGE_FD_VXLAN_VNI_UNMASK || + be32_to_cpu(match.key->keyid) >=3D VXLAN_N_VID || + !match.key->keyid) { + NL_SET_ERR_MSG_MOD(extack, "invalid enc_keyid"); + return -EINVAL; + } + + rule->tuples.outer_tun_vni =3D be32_to_cpu(match.key->keyid); + rule->tuples_mask.outer_tun_vni =3D + be32_to_cpu(match.mask->keyid); + } else { + rule->unused_tuple |=3D BIT(OUTER_TUN_VNI); + } + + return 0; +} + +static int hclge_get_cls_key_ip_tos(const struct flow_rule *flow, + struct hclge_fd_rule *rule, + struct netlink_ext_ack *extack) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) { + struct flow_match_ip match; + + flow_rule_match_ip(flow, &match); + + if (match.mask->ttl) { + NL_SET_ERR_MSG_MOD(extack, "unsupported TTL"); + return -EOPNOTSUPP; + } + + rule->tuples.ip_tos =3D match.key->tos; + rule->tuples_mask.ip_tos =3D match.mask->tos; + if (!rule->tuples_mask.ip_tos) + rule->unused_tuple |=3D BIT(INNER_IP_TOS); + } else { + rule->unused_tuple |=3D BIT(INNER_IP_TOS); + } + + return 0; +} + static int hclge_get_tc_flower_action(struct hclge_dev *hdev, struct flow_cls_offload *cls_flower, struct hclge_fd_rule *rule) @@ -7397,6 +7466,9 @@ static int hclge_parse_cls_flower(struct hclge_dev *h= dev, struct netlink_ext_ack *extack =3D cls_flower->common.extack; int ret; =20 + /* not support any user def tuples */ + rule->unused_tuple |=3D 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); @@ -7406,8 +7478,11 @@ static int hclge_parse_cls_flower(struct hclge_dev *= hdev, return ret; =20 hclge_get_cls_key_port(flow, rule); + ret =3D hclge_get_cls_key_ip_tos(flow, rule, extack); + if (ret) + return ret; =20 - return 0; + return hclge_get_cls_enc_keyid(hdev, flow, rule, extack); } =20 static int hclge_check_cls_flower(struct hclge_dev *hdev, @@ -7439,10 +7514,12 @@ 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); =20 if (hdev->fd_cfg.fd_mode =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) - support_keys |=3D BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS); + support_keys |=3D BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID); =20 if (dissector->used_keys & ~support_keys) { NL_SET_ERR_MSG_FMT_MOD(extack, "unsupported key set: %#llx", diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/driv= ers/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 =20 /* 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; }; =20 struct hclge_fd_rule { --=20 2.33.0 From nobody Thu Jun 11 08:50:44 2026 Received: from canpmsgout03.his.huawei.com (canpmsgout03.his.huawei.com [113.46.200.218]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 66774371065; Wed, 10 Jun 2026 06:06:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.218 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071609; cv=none; b=o0i1EV1H460bqYthV9orNTGIMw7y5NJzK713QiXuSjWojqXx6wWUNnA4d5kzPk46SFF6YoPifK16mjCRNooysgdD9cTtFqYNxPBFLBWQfe3SgC34qDZ90Te7TKqd7Uy8QLMRq8lJ+47kk7fMHVb79tSvbKPN20T2uoFd4Edp0Hw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071609; c=relaxed/simple; bh=B4v7OY2Eb1jt7Ha2+zZNKU/ctBmx1BSs25+GP52NnQc=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iGsE2Z6yYNOJBCaM9KICBCYPirxeem0LksygW8H6q4v+yLwUydqZpckstIfSrcTHCIPoENFGFxCVdKIXMMwk8dnutxtHOzf5egObEsKrbSEKQw/AUR4t/sM/qooZ06fa5Lk6h5xjC7tl87pgRqai1vQxjSUKW+006a9DHiWoHXc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=ytdbbEQ0; arc=none smtp.client-ip=113.46.200.218 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="ytdbbEQ0" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=UsxIWiiCoexc8VlQmxvxhP6fUundKy4VHSSoiGcNqE0=; b=ytdbbEQ053YhdRYnAk1gCkV2UaYFs6hkWSrieHLeno4ZoO6iBMlpPnzMj8y3ecj4EqRornQIo so7hfwlW6obOTnl5LLhYprjoYIn6atw4187Lc86mMWmat8hv3Eh2xKPaMgFD6AoYzVFHqPpKt8j nYgdZfFVsKZO8m3vh0T7WLg= Received: from mail.maildlp.com (unknown [172.19.163.104]) by canpmsgout03.his.huawei.com (SkyGuard) with ESMTPS id 4gZw7p2F44zpTJt; Wed, 10 Jun 2026 13:58:46 +0800 (CST) Received: from kwepemk100013.china.huawei.com (unknown [7.202.194.61]) by mail.maildlp.com (Postfix) with ESMTPS id CDD1F4048F; Wed, 10 Jun 2026 14:06:36 +0800 (CST) Received: from localhost.huawei.com (10.90.31.46) by kwepemk100013.china.huawei.com (7.202.194.61) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Wed, 10 Jun 2026 14:06:36 +0800 From: Jijie Shao To: , , , , , CC: , , , , , , , Subject: [PATCH V4 net-next 5/6] net: hns3: debugfs support for dumping fd rules Date: Wed, 10 Jun 2026 14:06:17 +0800 Message-ID: <20260610060618.834987-6-shaojijie@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260610060618.834987-1-shaojijie@huawei.com> References: <20260610060618.834987-1-shaojijie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems200002.china.huawei.com (7.221.188.68) To kwepemk100013.china.huawei.com (7.202.194.61) Content-Type: text/plain; charset="utf-8" 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 --- drivers/net/ethernet/hisilicon/hns3/hnae3.h | 1 + .../ethernet/hisilicon/hns3/hns3_debugfs.c | 6 + .../hisilicon/hns3/hns3pf/hclge_debugfs.c | 155 ++++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethe= rnet/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/n= et/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[] =3D { .dentry =3D HNS3_DBG_DENTRY_FD, .init =3D hns3_dbg_common_init_t2, }, + { + .name =3D "fd_rule", + .cmd =3D HNAE3_DBG_CMD_FD_RULE, + .dentry =3D HNS3_DBG_DENTRY_FD, + .init =3D hns3_dbg_common_init_t2, + }, { .name =3D "service_task_info", .cmd =3D HNAE3_DBG_CMD_SERV_INFO, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/d= rivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 3dab3a271aa6..9a4e29bfa166 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -2121,6 +2121,157 @@ static int hclge_dbg_dump_fd_counter(struct seq_fil= e *s, void *data) return 0; } =20 +__printf(4, 5) +static void hclge_fd_dump_item(struct seq_file *s, const char *name, + const char *suffix, const char *fmt, ...) +{ + va_list args; + + seq_printf(s, "\t\t%s%s: ", name, suffix); + + va_start(args, fmt); + seq_vprintf(s, fmt, args); + va_end(args); + + seq_putc(s, '\n'); +} + +#define hclge_fd_dump_any(s, rule, type, name, fmt, key, mask) \ +do { \ + typeof(s) _s =3D (s); \ + typeof(name) _name =3D (name); \ + typeof(fmt) _fmt =3D (fmt); \ + \ + if (!((rule)->unused_tuple & BIT(type))) { \ + hclge_fd_dump_item(_s, _name, "", _fmt, key); \ + hclge_fd_dump_item(_s, _name, "_mask", _fmt, mask); \ + } \ +} while (0) +#define hclge_fd_dump_u32 hclge_fd_dump_any +#define hclge_fd_dump_ptr hclge_fd_dump_any + +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) +{ + __be32 be_mask[IPV6_ADDR_WORDS]; + __be32 be_ip[IPV6_ADDR_WORDS]; + + if (rule->unused_tuple & BIT(type) || + rule->unused_tuple & BIT(INNER_ETH_TYPE)) + return; + + ipv6_addr_cpu_to_be32(be_ip, ip); + ipv6_addr_cpu_to_be32(be_mask, mask); + + if (rule->tuples.ether_proto =3D=3D ETH_P_IPV6) + hclge_fd_dump_ptr(s, rule, type, name, "%pI6", + be_ip, be_mask); + else + hclge_fd_dump_ptr(s, rule, type, name, "%pI4", + &be_ip[IPV4_INDEX], &be_mask[IPV4_INDEX]); +} + +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[] =3D { + [HCLGE_FD_ACTION_SELECT_QUEUE] =3D "select_queue", + [HCLGE_FD_ACTION_DROP_PACKET] =3D "drop_packet", + [HCLGE_FD_ACTION_SELECT_TC] =3D "select_tc", + }; + + if (rule->action <=3D HCLGE_FD_ACTION_SELECT_TC) + seq_printf(s, "\taction: %s\n", action_str[rule->action]); + else + seq_printf(s, "\taction: %s\n", "unknown"); + + if (rule->action =3D=3D HCLGE_FD_ACTION_SELECT_QUEUE) + seq_printf(s, "\tqueue_id: %u\n", rule->queue_id); + else if (rule->action =3D=3D 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 type_str[] =3D { + [HCLGE_FD_RULE_NONE] =3D "none", + [HCLGE_FD_ARFS_ACTIVE] =3D "arfs", + [HCLGE_FD_EP_ACTIVE] =3D "ep", + [HCLGE_FD_TC_FLOWER_ACTIVE] =3D "tc_flow" + }; + + if (hdev->fd_active_type <=3D HCLGE_FD_TC_FLOWER_ACTIVE) + seq_printf(s, "fd type: %s\n", type_str[hdev->fd_active_type]); + else + seq_printf(s, "fd type: %s\n", "unknown"); +} + +static int hclge_dbg_dump_fd_rule(struct seq_file *s, void *data) +{ + struct hclge_dev *hdev =3D hclge_seq_file_to_hdev(s); + struct hclge_fd_rule *rule; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + spin_lock_bh(&hdev->fd_rule_lock); + hclge_dbg_dump_fd_type(hdev, s); + hlist_for_each_entry(rule, &hdev->fd_rule_list, rule_node) { + if (rule->state !=3D 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[] =3D { {HCLGE_MISC_VECTOR_REG_BASE, "vector0 interrupt enable status"}, {HCLGE_MISC_RESET_STS_REG, "reset interrupt source"}, @@ -2913,6 +3064,10 @@ static const struct hclge_dbg_func hclge_dbg_cmd_fun= c[] =3D { .cmd =3D HNAE3_DBG_CMD_FD_TCAM, .dbg_read_func =3D hclge_dbg_dump_fd_tcam, }, + { + .cmd =3D HNAE3_DBG_CMD_FD_RULE, + .dbg_read_func =3D hclge_dbg_dump_fd_rule, + }, { .cmd =3D HNAE3_DBG_CMD_MAC_TNL_STATUS, .dbg_read_func =3D hclge_dbg_dump_mac_tnl_status, --=20 2.33.0 From nobody Thu Jun 11 08:50:44 2026 Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E1A5E371065; Wed, 10 Jun 2026 06:06:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.249.212.187 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071627; cv=none; b=hqxpFDLrYtFVaX0cmoWAZzTsKbvNxMIO2LJYfooargedcgjO64rE2lCbIhQ2GOabLeksNvdPmWt2cZKOQH+kk0KC/rbzaLSkICdjtEq7/zI5eIeoHOlTeFTQ3V3MX17zSvNUZ3XVb5vjTPX4uEwhtTLoXogOA1V4sa41yd1FS1M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781071627; c=relaxed/simple; bh=/OG1tGtPDRgxC1MU7VcV6ugENyqaDWmiBDYwPrZaRwc=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=J4zzVBb1O9hbpbh5g9BGrDKigDevmNsD9/5jPFWvpWxJM8A/dAkp3okhXW/6V5j+AwDDILpY7F/TisTkA/Qkhx06B7KhDp1E48CCNCWD40XjmPiaUm1iCa3L586PLkUWMF+xxdMOmO+w2td3w2yFd2XAOl40am3gUiOUB/HB0Wk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=D/qIpW3Q; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=D/qIpW3Q; arc=none smtp.client-ip=45.249.212.187 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="D/qIpW3Q"; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="D/qIpW3Q" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=CnRn+rWKFUEcPapk92AbS2ymFCHUzcG0tMVjlath30I=; b=D/qIpW3Q6goSl8Fu1LN5GkEft1cWzVNCaWmta6f6e//wNIRcln2TDs1h6AKNo0Ai4Dno7TgHk ZuJc3vErtxj8+kyXVZ/3p6DM/GagXr1Cd14XVK3BgbKSSBcN+r2R6nd/uCTMAzg53fIPec5Uq7Y CukgXF7MhCnx8kesjSmJjFE= Received: from canpmsgout12.his.huawei.com (unknown [172.19.92.144]) by szxga01-in.huawei.com (SkyGuard) with ESMTPS id 4gZwJm3xGSz1BFtp; Wed, 10 Jun 2026 14:06:32 +0800 (CST) dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=CnRn+rWKFUEcPapk92AbS2ymFCHUzcG0tMVjlath30I=; b=D/qIpW3Q6goSl8Fu1LN5GkEft1cWzVNCaWmta6f6e//wNIRcln2TDs1h6AKNo0Ai4Dno7TgHk ZuJc3vErtxj8+kyXVZ/3p6DM/GagXr1Cd14XVK3BgbKSSBcN+r2R6nd/uCTMAzg53fIPec5Uq7Y CukgXF7MhCnx8kesjSmJjFE= Received: from mail.maildlp.com (unknown [172.19.162.92]) by canpmsgout12.his.huawei.com (SkyGuard) with ESMTPS id 4gZw7r3sPFznTVV; Wed, 10 Jun 2026 13:58:48 +0800 (CST) Received: from kwepemk100013.china.huawei.com (unknown [7.202.194.61]) by mail.maildlp.com (Postfix) with ESMTPS id 9B92C40565; Wed, 10 Jun 2026 14:06:37 +0800 (CST) Received: from localhost.huawei.com (10.90.31.46) by kwepemk100013.china.huawei.com (7.202.194.61) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.36; Wed, 10 Jun 2026 14:06:36 +0800 From: Jijie Shao To: , , , , , CC: , , , , , , , Subject: [PATCH V4 net-next 6/6] net: hns3: move fd code to a separate file Date: Wed, 10 Jun 2026 14:06:18 +0800 Message-ID: <20260610060618.834987-7-shaojijie@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260610060618.834987-1-shaojijie@huawei.com> References: <20260610060618.834987-1-shaojijie@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems200002.china.huawei.com (7.221.188.68) To kwepemk100013.china.huawei.com (7.202.194.61) Content-Type: text/plain; charset="utf-8" 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 --- drivers/net/ethernet/hisilicon/hns3/Makefile | 1 + .../ethernet/hisilicon/hns3/hns3pf/hclge_fd.c | 2593 ++++++++++++++++ .../ethernet/hisilicon/hns3/hns3pf/hclge_fd.h | 33 + .../hisilicon/hns3/hns3pf/hclge_main.c | 2675 +---------------- .../hisilicon/hns3/hns3pf/hclge_main.h | 2 + 5 files changed, 2672 insertions(+), 2632 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/eth= ernet/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 =3D hns3vf/hclgevf_main.o hns3vf/hclgevf_mbx= .o hns3vf/hclgevf_devlin obj-$(CONFIG_HNS3_HCLGE) +=3D hclge.o hclge-common.o hclge-objs =3D hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o h= ns3pf/hclge_regs.o \ hns3pf/hclge_mbx.o hns3pf/hclge_err.o hns3pf/hclge_debugfs.o hns3pf/hcl= ge_ptp.o hns3pf/hclge_devlink.o \ + hns3pf/hclge_fd.o =20 hclge-$(CONFIG_HNS3_DCB) +=3D hns3pf/hclge_dcb.o diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c b/driver= s/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c new file mode 100644 index 000000000000..2fccb0a870b5 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.c @@ -0,0 +1,2593 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2026 Hisilicon Limited. + +#include +#include +#include +#include "hclge_fd.h" +#include "hclge_main.h" + +static const struct key_info meta_data_key_info[] =3D { + { 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[] =3D { + { 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 =3D 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 =3D old_rule->rule_node.next; + new_rule->rule_node.pprev =3D 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 =3D=3D HCLGE_FD_TO_ADD) { + hclge_fd_dec_rule_cnt(hdev, old_rule->location); + hclge_fd_free_node(hdev, old_rule); + return; + } + old_rule->state =3D 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 =3D=3D 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 =3D 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 =3D 0; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false); + + req =3D (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 =3D cpu_to_le16(data); + + data =3D 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 =3D cpu_to_le16(data); + + data =3D 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 =3D cpu_to_le16(data); + + ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "failed to set fd user def data, ret=3D %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 =3D 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 =3D &hdev->fd_rule_list; + struct hclge_fd_rule *fd_rule, *parent =3D NULL; + struct hclge_fd_user_def_info *info, *old_info; + struct hclge_fd_user_def_cfg *cfg; + + if (!rule || rule->rule_type !=3D HCLGE_FD_EP_ACTIVE || + rule->ep.user_def.layer =3D=3D HCLGE_FD_USER_DEF_NONE) + return 0; + + /* for valid layer is start from 1, so need minus 1 to get the cfg */ + cfg =3D &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; + info =3D &rule->ep.user_def; + + if (!cfg->ref_cnt || cfg->offset =3D=3D info->offset) + return 0; + + if (cfg->ref_cnt > 1) + goto error; + + fd_rule =3D hclge_find_fd_rule(hlist, rule->location, &parent); + if (fd_rule) { + old_info =3D &fd_rule->ep.user_def; + if (info->layer =3D=3D old_info->layer) + return 0; + } + +error: + dev_err(&hdev->pdev->dev, + "No available offset for layer%d fd rule, each layer only support one us= er 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 !=3D HCLGE_FD_EP_ACTIVE || + rule->ep.user_def.layer =3D=3D HCLGE_FD_USER_DEF_NONE) + return; + + cfg =3D &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; + if (!cfg->ref_cnt) { + cfg->offset =3D 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 !=3D HCLGE_FD_EP_ACTIVE || + rule->ep.user_def.layer =3D=3D HCLGE_FD_USER_DEF_NONE) + return; + + cfg =3D &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 =3D 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 =3D &hdev->fd_rule_list; + struct hclge_fd_rule *fd_rule, *parent =3D NULL; + + fd_rule =3D hclge_find_fd_rule(hlist, location, &parent); + if (fd_rule) { + hclge_fd_dec_user_def_refcnt(hdev, fd_rule); + if (state =3D=3D 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 =3D=3D HCLGE_FD_TO_DEL || state =3D=3D HCLGE_FD_DELETE= D)) { + 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 =3D=3D 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 =3D (struct hclge_get_fd_mode_cmd *)desc.data; + + ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "get fd mode fail, ret=3D%d\n", ret); + return ret; + } + + *fd_mode =3D 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 =3D (struct hclge_get_fd_allocation_cmd *)desc.data; + + ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=3D%d\n", + ret); + return ret; + } + + *stage1_entry_num =3D le32_to_cpu(req->stage1_entry_num); + *stage2_entry_num =3D le32_to_cpu(req->stage2_entry_num); + *stage1_counter_num =3D le16_to_cpu(req->stage1_counter_num); + *stage2_counter_num =3D 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 =3D (struct hclge_set_fd_key_config_cmd *)desc.data; + stage =3D &hdev->fd_cfg.key_cfg[stage_num]; + req->stage =3D stage_num; + req->key_select =3D stage->key_sel; + req->inner_sipv6_word_en =3D stage->inner_sipv6_word_en; + req->inner_dipv6_word_en =3D stage->inner_dipv6_word_en; + req->outer_sipv6_word_en =3D stage->outer_sipv6_word_en; + req->outer_dipv6_word_en =3D stage->outer_dipv6_word_en; + req->tuple_mask =3D cpu_to_le32(~stage->tuple_active); + req->meta_data_mask =3D cpu_to_le32(~stage->meta_data_active); + + ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, "set fd key fail, ret=3D%d\n", ret); + + return ret; +} + +static void hclge_fd_disable_user_def(struct hclge_dev *hdev) +{ + struct hclge_fd_user_def_cfg *cfg =3D 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 =3D 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 =3D MAX_KEY_LENGTH; + break; + case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1: + hdev->fd_cfg.max_key_length =3D 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 =3D &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1]; + key_cfg->key_sel =3D HCLGE_FD_KEY_BASE_ON_TUPLE; + key_cfg->inner_sipv6_word_en =3D LOW_2_WORDS; + key_cfg->inner_dipv6_word_en =3D LOW_2_WORDS; + key_cfg->outer_sipv6_word_en =3D 0; + key_cfg->outer_dipv6_word_en =3D 0; + + key_cfg->tuple_active =3D 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 =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1= ) { + key_cfg->tuple_active |=3D BIT(INNER_DST_MAC) | + BIT(INNER_SRC_MAC) | + BIT(OUTER_TUN_VNI); + if (hdev->ae_dev->dev_version >=3D HNAE3_DEVICE_VERSION_V3) + key_cfg->tuple_active |=3D 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 =3D BIT(ROCE_TYPE) | BIT(DST_VPORT); + + ret =3D 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 |=3D cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false); + desc[1].flag |=3D cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false); + + req1 =3D (struct hclge_fd_tcam_config_1_cmd *)desc[0].data; + req2 =3D (struct hclge_fd_tcam_config_2_cmd *)desc[1].data; + req3 =3D (struct hclge_fd_tcam_config_3_cmd *)desc[2].data; + + req1->stage =3D stage; + req1->xy_sel =3D sel_x ? 1 : 0; + hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0); + req1->index =3D cpu_to_le32(loc); + req1->entry_vld =3D 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 =3D hclge_cmd_send(&hdev->hw, desc, 3); + if (ret) + dev_err(&hdev->pdev->dev, + "config tcam key fail, ret=3D%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 =3D pci_get_drvdata(hdev->pdev); + struct hclge_fd_ad_config_cmd *req; + struct hclge_desc desc; + u64 ad_data =3D 0; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false); + + req =3D (struct hclge_fd_ad_config_cmd *)desc.data; + req->index =3D cpu_to_le32(loc); + req->stage =3D 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 >=3D HCLGE_TQP_MAX_SIZE_DEV_V2 ? 1 : 0); + hnae3_set_bit(ad_data, HCLGE_FD_AD_COUNTER_NUM_H_B, + action->counter_id >=3D HCLGE_FD_COUNTER_MAX_SIZE_DEV_V2 ? + 1 : 0); + ad_data <<=3D 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 =3D cpu_to_le64(ad_data); + ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, "fd ad config fail, ret=3D%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 =3D (u8 *)rule; + __le32 le_x, le_y; + int i; + + if (rule->unused_tuple & BIT(tuple_bit)) + return true; + + key_opt =3D tuple_key_info[tuple_bit].key_opt; + offset =3D tuple_key_info[tuple_bit].offset; + moffset =3D 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 =3D cpu_to_le16(tmp_x_s); + *(__le16 *)key_y =3D 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 =3D cpu_to_le32(tmp_x_l); + *(__le32 *)key_y =3D cpu_to_le32(tmp_y_l); + + return true; + case KEY_OPT_MAC: + for (i =3D 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 =3D 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 =3D cpu_to_le32(tmp_x_l); + *(__le32 *)key_y =3D 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])); + le_x =3D cpu_to_le32(tmp_x_l); + le_y =3D cpu_to_le32(tmp_y_l); + memcpy(key_x, &le_x, HCLGE_VNI_LENGTH); + memcpy(key_y, &le_y, HCLGE_VNI_LENGTH); + + 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 =3D 0, tmp_x, tmp_y, port_number; + u8 cur_pos =3D 0, tuple_size, shift_bits; + unsigned int i; + + for (i =3D 0; i < MAX_META_DATA; i++) { + tuple_size =3D meta_data_key_info[i].key_length; + tuple_bit =3D 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 +=3D tuple_size; + break; + case BIT(DST_VPORT): + port_number =3D 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 +=3D tuple_size; + break; + default: + break; + } + } + + calc_x(tmp_x, meta_data, 0xFFFFFFFF); + calc_y(tmp_y, meta_data, 0xFFFFFFFF); + shift_bits =3D sizeof(meta_data) * 8 - cur_pos; + + *key_x =3D cpu_to_le32(tmp_x << shift_bits); + *key_y =3D 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 =3D &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 =3D key_x; + cur_key_y =3D key_y; + + for (i =3D 0; i < MAX_TUPLE; i++) { + bool tuple_valid; + + tuple_size =3D tuple_key_info[i].key_length / 8; + if (!(key_cfg->tuple_active & BIT(i))) + continue; + + tuple_valid =3D hclge_fd_convert_tuple(i, cur_key_x, + cur_key_y, rule); + if (tuple_valid) { + cur_key_x +=3D tuple_size; + cur_key_y +=3D tuple_size; + } + } + + meta_data_region =3D 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 =3D 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=3D%u, ret=3D%d\n", + rule->queue_id, ret); + return ret; + } + + ret =3D 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=3D%u, ret=3D%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 =3D hdev->vport; + struct hnae3_knic_private_info *kinfo =3D &vport->nic.kinfo; + struct hclge_fd_ad_data ad_data; + + memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data)); + ad_data.ad_id =3D rule->location; + + if (rule->action =3D=3D HCLGE_FD_ACTION_DROP_PACKET) { + ad_data.drop_packet =3D true; + } else if (rule->action =3D=3D HCLGE_FD_ACTION_SELECT_TC) { + ad_data.override_tc =3D true; + ad_data.queue_id =3D + kinfo->tc_info.tqp_offset[rule->cls_flower.tc]; + ad_data.tc_size =3D + ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]); + } else { + ad_data.forward_to_direct_queue =3D true; + ad_data.queue_id =3D rule->queue_id; + } + + if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) { + ad_data.use_counter =3D true; + ad_data.counter_id =3D rule->vf_id % + hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]; + } else { + ad_data.use_counter =3D false; + ad_data.counter_id =3D 0; + } + + ad_data.use_next_stage =3D false; + ad_data.next_input_key =3D 0; + + ad_data.write_rule_id_to_bd =3D true; + ad_data.rule_id =3D 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 |=3D BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC); + + if (!spec->ip4src) + *unused_tuple |=3D BIT(INNER_SRC_IP); + + if (!spec->ip4dst) + *unused_tuple |=3D BIT(INNER_DST_IP); + + if (!spec->psrc) + *unused_tuple |=3D BIT(INNER_SRC_PORT); + + if (!spec->pdst) + *unused_tuple |=3D BIT(INNER_DST_PORT); + + if (!spec->tos) + *unused_tuple |=3D 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 |=3D BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | + BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); + + if (!spec->ip4src) + *unused_tuple |=3D BIT(INNER_SRC_IP); + + if (!spec->ip4dst) + *unused_tuple |=3D BIT(INNER_DST_IP); + + if (!spec->tos) + *unused_tuple |=3D BIT(INNER_IP_TOS); + + if (!spec->proto) + *unused_tuple |=3D BIT(INNER_IP_PROTO); + + if (spec->l4_4_bytes) + return -EOPNOTSUPP; + + if (spec->ip_ver !=3D 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 |=3D 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 |=3D BIT(INNER_SRC_IP); + + if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) + *unused_tuple |=3D BIT(INNER_DST_IP); + + if (!spec->psrc) + *unused_tuple |=3D BIT(INNER_SRC_PORT); + + if (!spec->pdst) + *unused_tuple |=3D BIT(INNER_DST_PORT); + + if (!spec->tclass) + *unused_tuple |=3D 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 |=3D 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 |=3D BIT(INNER_SRC_IP); + + if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) + *unused_tuple |=3D BIT(INNER_DST_IP); + + if (!spec->l4_proto) + *unused_tuple |=3D BIT(INNER_IP_PROTO); + + if (!spec->tclass) + *unused_tuple |=3D 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_tup= le) +{ + if (!spec || !unused_tuple) + return -EINVAL; + + *unused_tuple |=3D 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 |=3D BIT(INNER_SRC_MAC); + + if (is_zero_ether_addr(spec->h_dest)) + *unused_tuple |=3D BIT(INNER_DST_MAC); + + if (!spec->h_proto) + *unused_tuple |=3D 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 |=3D BIT(INNER_VLAN_TAG_FST); + + if (fs->m_ext.vlan_tci && + be16_to_cpu(fs->h_ext.vlan_tci) >=3D 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 |=3D BIT(INNER_VLAN_TAG_FST); + } + + if (fs->flow_type & FLOW_MAC_EXT) { + if (hdev->fd_cfg.fd_mode !=3D + 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 |=3D BIT(INNER_DST_MAC); + else + *unused_tuple &=3D ~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 =3D HCLGE_FD_USER_DEF_L2; + *unused_tuple &=3D ~BIT(INNER_L2_RSV); + break; + case IP_USER_FLOW: + case IPV6_USER_FLOW: + info->layer =3D HCLGE_FD_USER_DEF_L3; + *unused_tuple &=3D ~BIT(INNER_L3_RSV); + break; + case TCP_V4_FLOW: + case UDP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V6_FLOW: + info->layer =3D HCLGE_FD_USER_DEF_L4; + *unused_tuple &=3D ~BIT(INNER_L4_RSV); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *f= s) +{ + return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) =3D=3D 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 =3D hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active; + u32 flow_type =3D fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + u16 data, offset, data_mask, offset_mask; + int ret; + + info->layer =3D HCLGE_FD_USER_DEF_NONE; + *unused_tuple |=3D 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 =3D be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; + data_mask =3D be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; + offset =3D be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET; + offset_mask =3D 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 !=3D HCLGE_FD_USER_DEF_OFFSET_UNMASK) { + dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n"); + return -EINVAL; + } + + ret =3D 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 =3D %d\n", + ret); + return ret; + } + + info->data =3D data; + info->data_mask =3D data_mask; + info->offset =3D 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 >=3D 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 =3D hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info); + if (ret) + return ret; + + flow_type =3D fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); + switch (flow_type) { + case SCTP_V4_FLOW: + case TCP_V4_FLOW: + case UDP_V4_FLOW: + ret =3D hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec, + unused_tuple); + break; + case IP_USER_FLOW: + ret =3D 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 =3D hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec, + unused_tuple); + break; + case IPV6_USER_FLOW: + ret =3D hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec, + unused_tuple); + break; + case ETHER_FLOW: + if (hdev->fd_cfg.fd_mode !=3D + 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 =3D hclge_fd_check_ether_tuple(&fs->h_u.ether_spec, + unused_tuple); + break; + default: + dev_err(&hdev->pdev->dev, + "unsupported protocol type, protocol type =3D %#x\n", + flow_type); + return -EOPNOTSUPP; + } + + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to check flow union tuple, ret =3D %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] =3D + be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src); + rule->tuples_mask.src_ip[IPV4_INDEX] =3D + be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src); + + rule->tuples.dst_ip[IPV4_INDEX] =3D + be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst); + rule->tuples_mask.dst_ip[IPV4_INDEX] =3D + be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst); + + rule->tuples.src_port =3D be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc); + rule->tuples_mask.src_port =3D be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc); + + rule->tuples.dst_port =3D be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst); + rule->tuples_mask.dst_port =3D be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst); + + rule->tuples.ip_tos =3D fs->h_u.tcp_ip4_spec.tos; + rule->tuples_mask.ip_tos =3D fs->m_u.tcp_ip4_spec.tos; + + rule->tuples.ether_proto =3D ETH_P_IP; + rule->tuples_mask.ether_proto =3D 0xFFFF; + + rule->tuples.ip_proto =3D ip_proto; + rule->tuples_mask.ip_proto =3D 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] =3D + be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src); + rule->tuples_mask.src_ip[IPV4_INDEX] =3D + be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src); + + rule->tuples.dst_ip[IPV4_INDEX] =3D + be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst); + rule->tuples_mask.dst_ip[IPV4_INDEX] =3D + be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst); + + rule->tuples.ip_tos =3D fs->h_u.usr_ip4_spec.tos; + rule->tuples_mask.ip_tos =3D fs->m_u.usr_ip4_spec.tos; + + rule->tuples.ip_proto =3D fs->h_u.usr_ip4_spec.proto; + rule->tuples_mask.ip_proto =3D fs->m_u.usr_ip4_spec.proto; + + rule->tuples.ether_proto =3D ETH_P_IP; + rule->tuples_mask.ether_proto =3D 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 =3D be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc); + rule->tuples_mask.src_port =3D be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc); + + rule->tuples.dst_port =3D be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst); + rule->tuples_mask.dst_port =3D be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst); + + rule->tuples.ether_proto =3D ETH_P_IPV6; + rule->tuples_mask.ether_proto =3D 0xFFFF; + + rule->tuples.ip_tos =3D fs->h_u.tcp_ip6_spec.tclass; + rule->tuples_mask.ip_tos =3D fs->m_u.tcp_ip6_spec.tclass; + + rule->tuples.ip_proto =3D ip_proto; + rule->tuples_mask.ip_proto =3D 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 =3D fs->h_u.usr_ip6_spec.l4_proto; + rule->tuples_mask.ip_proto =3D fs->m_u.usr_ip6_spec.l4_proto; + + rule->tuples.ip_tos =3D fs->h_u.tcp_ip6_spec.tclass; + rule->tuples_mask.ip_tos =3D fs->m_u.tcp_ip6_spec.tclass; + + rule->tuples.ether_proto =3D ETH_P_IPV6; + rule->tuples_mask.ether_proto =3D 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 =3D be16_to_cpu(fs->h_u.ether_spec.h_proto); + rule->tuples_mask.ether_proto =3D be16_to_cpu(fs->m_u.ether_spec.h_proto); +} + +static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *inf= o, + struct hclge_fd_rule *rule) +{ + switch (info->layer) { + case HCLGE_FD_USER_DEF_L2: + rule->tuples.l2_user_def =3D info->data; + rule->tuples_mask.l2_user_def =3D info->data_mask; + break; + case HCLGE_FD_USER_DEF_L3: + rule->tuples.l3_user_def =3D info->data; + rule->tuples_mask.l3_user_def =3D info->data_mask; + break; + case HCLGE_FD_USER_DEF_L4: + rule->tuples.l4_user_def =3D (u32)info->data << 16; + rule->tuples_mask.l4_user_def =3D (u32)info->data_mask << 16; + break; + default: + break; + } + + rule->ep.user_def =3D *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 =3D 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 =3D be16_to_cpu(fs->h_ext.vlan_tci); + rule->tuples_mask.vlan_tag1 =3D 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 =3D 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 !=3D rule->rule_type && + (hdev->fd_active_type =3D=3D HCLGE_FD_TC_FLOWER_ACTIVE || + hdev->fd_active_type =3D=3D HCLGE_FD_EP_ACTIVE)) { + dev_err(&hdev->pdev->dev, + "mode conflict(new type %d, active type %d), please delete existent rul= es first\n", + rule->rule_type, hdev->fd_active_type); + spin_unlock_bh(&hdev->fd_rule_lock); + return -EINVAL; + } + + ret =3D hclge_fd_check_user_def_refcnt(hdev, rule); + if (ret) + goto out; + + ret =3D hclge_clear_arfs_rules(hdev); + if (ret) + goto out; + + ret =3D hclge_fd_config_rule(hdev, rule); + if (ret) + goto out; + + rule->state =3D HCLGE_FD_ACTIVE; + hdev->fd_active_type =3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + + return hdev->fd_active_type =3D=3D HCLGE_FD_TC_FLOWER_ACTIVE; +} + +static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_coo= kie, + u16 *vport_id, u8 *action, u16 *queue_id) +{ + struct hclge_vport *vport =3D hdev->vport; + + if (ring_cookie =3D=3D RX_CLS_FLOW_DISC) { + *action =3D HCLGE_FD_ACTION_DROP_PACKET; + } else { + u32 ring =3D ethtool_get_flow_spec_ring(ring_cookie); + u8 vf =3D 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 =3D vf ? hdev->vport[vf].vport_id : vport->vport_id; + tqps =3D hdev->vport[vf].nic.kinfo.num_tqps; + + if (ring >=3D tqps) { + dev_err(&hdev->pdev->dev, + "Error: queue id (%u) > max tqp num (%u)\n", + ring, tqps - 1U); + return -EINVAL; + } + + *action =3D HCLGE_FD_ACTION_SELECT_QUEUE; + *queue_id =3D ring; + } + + return 0; +} + +int hclge_add_fd_entry(struct hnae3_handle *handle, struct ethtool_rxnfc *= cmd) +{ + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + struct hclge_fd_user_def_info info; + u16 dst_vport_id =3D 0, q_index =3D 0; + struct ethtool_rx_flow_spec *fs; + struct hclge_fd_rule *rule; + u32 unused =3D 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 =3D (struct ethtool_rx_flow_spec *)&cmd->fs; + + ret =3D hclge_fd_check_spec(hdev, fs, &unused, &info); + if (ret) + return ret; + + ret =3D hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id, + &action, &q_index); + if (ret) + return ret; + + rule =3D kzalloc_obj(*rule); + if (!rule) + return -ENOMEM; + + ret =3D hclge_fd_get_tuple(fs, rule, &info); + if (ret) { + kfree(rule); + return ret; + } + + rule->flow_type =3D fs->flow_type; + rule->location =3D fs->location; + rule->unused_tuple =3D unused; + rule->vf_id =3D dst_vport_id; + rule->queue_id =3D q_index; + rule->action =3D action; + rule->rule_type =3D HCLGE_FD_EP_ACTIVE; + + ret =3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + struct ethtool_rx_flow_spec *fs; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + fs =3D (struct ethtool_rx_flow_spec *)&cmd->fs; + + if (fs->location >=3D hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) + return -EINVAL; + + spin_lock_bh(&hdev->fd_rule_lock); + if (hdev->fd_active_type =3D=3D 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 =3D 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 =3D HCLGE_FD_RULE_NONE; + hdev->hclge_fd_rule_num =3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D 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 =3D=3D HCLGE_FD_ACTIVE) + rule->state =3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || + hclge_is_cls_flower_active(handle)) + return -EOPNOTSUPP; + + cmd->rule_cnt =3D hdev->hclge_fd_rule_num; + cmd->data =3D 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 =3D cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); + spec_mask->ip4src =3D rule->unused_tuple & BIT(INNER_SRC_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); + + spec->ip4dst =3D cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); + spec_mask->ip4dst =3D rule->unused_tuple & BIT(INNER_DST_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); + + spec->psrc =3D cpu_to_be16(rule->tuples.src_port); + spec_mask->psrc =3D rule->unused_tuple & BIT(INNER_SRC_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.src_port); + + spec->pdst =3D cpu_to_be16(rule->tuples.dst_port); + spec_mask->pdst =3D rule->unused_tuple & BIT(INNER_DST_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.dst_port); + + spec->tos =3D rule->tuples.ip_tos; + spec_mask->tos =3D 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 =3D cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); + spec_mask->ip4src =3D rule->unused_tuple & BIT(INNER_SRC_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); + + spec->ip4dst =3D cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); + spec_mask->ip4dst =3D rule->unused_tuple & BIT(INNER_DST_IP) ? + 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); + + spec->tos =3D rule->tuples.ip_tos; + spec_mask->tos =3D rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; + + spec->proto =3D rule->tuples.ip_proto; + spec_mask->proto =3D rule->unused_tuple & BIT(INNER_IP_PROTO) ? + 0 : rule->tuples_mask.ip_proto; + + spec->ip_ver =3D 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 =3D rule->tuples.ip_tos; + spec_mask->tclass =3D rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; + + spec->psrc =3D cpu_to_be16(rule->tuples.src_port); + spec_mask->psrc =3D rule->unused_tuple & BIT(INNER_SRC_PORT) ? + 0 : cpu_to_be16(rule->tuples_mask.src_port); + + spec->pdst =3D cpu_to_be16(rule->tuples.dst_port); + spec_mask->pdst =3D 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 =3D rule->tuples.ip_tos; + spec_mask->tclass =3D rule->unused_tuple & BIT(INNER_IP_TOS) ? + 0 : rule->tuples_mask.ip_tos; + + spec->l4_proto =3D rule->tuples.ip_proto; + spec_mask->l4_proto =3D 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 =3D cpu_to_be16(rule->tuples.ether_proto); + spec_mask->h_proto =3D 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) =3D=3D + HCLGE_FD_TUPLE_USER_DEF_TUPLES) { + fs->h_ext.data[0] =3D 0; + fs->h_ext.data[1] =3D 0; + fs->m_ext.data[0] =3D 0; + fs->m_ext.data[1] =3D 0; + } else { + fs->h_ext.data[0] =3D cpu_to_be32(rule->ep.user_def.offset); + fs->h_ext.data[1] =3D cpu_to_be32(rule->ep.user_def.data); + fs->m_ext.data[0] =3D + cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK); + fs->m_ext.data[1] =3D 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 =3D cpu_to_be16(rule->tuples.vlan_tag1); + fs->m_ext.vlan_tci =3D + 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 =3D NULL; + struct hlist_node *node2; + + hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) { + if (rule->location =3D=3D 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 =3D=3D HCLGE_FD_ACTION_DROP_PACKET) { + fs->ring_cookie =3D RX_CLS_FLOW_DISC; + } else { + u64 vf_id; + + fs->ring_cookie =3D rule->queue_id; + vf_id =3D rule->vf_id; + vf_id <<=3D ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; + fs->ring_cookie |=3D vf_id; + } +} + +int hclge_get_fd_rule_info(struct hnae3_handle *handle, + struct ethtool_rxnfc *cmd) +{ + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_fd_rule *rule =3D NULL; + struct hclge_dev *hdev =3D vport->back; + struct ethtool_rx_flow_spec *fs; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + fs =3D (struct ethtool_rx_flow_spec *)&cmd->fs; + + spin_lock_bh(&hdev->fd_rule_lock); + + rule =3D hclge_get_fd_rule(hdev, fs->location); + if (!rule) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOENT; + } + + fs->flow_type =3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + struct hclge_fd_rule *rule; + struct hlist_node *node2; + u32 cnt =3D 0; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) + return -EOPNOTSUPP; + + cmd->data =3D 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 =3D=3D cmd->rule_cnt) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -EMSGSIZE; + } + + if (rule->state =3D=3D HCLGE_FD_TO_DEL) + continue; + + rule_locs[cnt] =3D rule->location; + cnt++; + } + + spin_unlock_bh(&hdev->fd_rule_lock); + + cmd->rule_cnt =3D 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 =3D be16_to_cpu(fkeys->basic.n_proto); + tuples->ip_proto =3D fkeys->basic.ip_proto; + tuples->dst_port =3D be16_to_cpu(fkeys->ports.dst); + + if (fkeys->basic.n_proto =3D=3D htons(ETH_P_IP)) { + tuples->src_ip[3] =3D be32_to_cpu(fkeys->addrs.v4addrs.src); + tuples->dst_ip[3] =3D be32_to_cpu(fkeys->addrs.v4addrs.dst); + } else { + int i; + + for (i =3D 0; i < IPV6_ADDR_WORDS; i++) { + tuples->src_ip[i] =3D be32_to_cpu(flow_ip6_src[i]); + tuples->dst_ip[i] =3D 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 =3D 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 *tu= ples, + struct hclge_fd_rule *rule) +{ + rule->unused_tuple =3D BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | + BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) | + BIT(INNER_SRC_PORT); + rule->action =3D 0; + rule->vf_id =3D 0; + rule->rule_type =3D HCLGE_FD_ARFS_ACTIVE; + rule->state =3D HCLGE_FD_TO_ADD; + if (tuples->ether_proto =3D=3D ETH_P_IP) { + if (tuples->ip_proto =3D=3D IPPROTO_TCP) + rule->flow_type =3D TCP_V4_FLOW; + else + rule->flow_type =3D UDP_V4_FLOW; + } else { + if (tuples->ip_proto =3D=3D IPPROTO_TCP) + rule->flow_type =3D TCP_V6_FLOW; + else + rule->flow_type =3D 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 =3D hclge_get_vport(handle); + struct hclge_fd_rule_tuples new_tuples =3D {}; + struct hclge_dev *hdev =3D 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 !=3D HCLGE_FD_ARFS_ACTIVE && + hdev->fd_active_type !=3D 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 =3D hclge_fd_search_flow_keys(hdev, &new_tuples); + if (!rule) { + bit_id =3D find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM); + if (bit_id >=3D hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOSPC; + } + + rule =3D kzalloc_obj(*rule, GFP_ATOMIC); + if (!rule) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -ENOMEM; + } + + rule->location =3D bit_id; + rule->arfs.flow_id =3D flow_id; + rule->queue_id =3D queue_id; + hclge_fd_build_arfs_rule(&new_tuples, rule); + hclge_update_fd_list(hdev, rule->state, rule->location, rule); + hdev->fd_active_type =3D HCLGE_FD_ARFS_ACTIVE; + } else if (rule->queue_id !=3D queue_id) { + rule->queue_id =3D queue_id; + rule->state =3D 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 =3D &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 !=3D 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 !=3D HCLGE_FD_ACTIVE) + continue; + if (rps_may_expire_flow(handle->netdev, rule->queue_id, + rule->arfs.flow_id, rule->location)) { + rule->state =3D 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 !=3D 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 =3D 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 =3D ntohs(match.key->n_proto); + ethtype_mask =3D ntohs(match.mask->n_proto); + + if (ethtype_key =3D=3D ETH_P_ALL) { + ethtype_key =3D 0; + ethtype_mask =3D 0; + } + rule->tuples.ether_proto =3D ethtype_key; + rule->tuples_mask.ether_proto =3D ethtype_mask; + rule->tuples.ip_proto =3D match.key->ip_proto; + rule->tuples_mask.ip_proto =3D match.mask->ip_proto; + } else { + rule->unused_tuple |=3D BIT(INNER_IP_PROTO); + rule->unused_tuple |=3D 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.mask->dst)) + rule->unused_tuple |=3D BIT(INNER_DST_MAC); + if (is_zero_ether_addr(match.mask->src)) + rule->unused_tuple |=3D BIT(INNER_SRC_MAC); + } else { + rule->unused_tuple |=3D BIT(INNER_DST_MAC); + rule->unused_tuple |=3D 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 =3D match.key->vlan_id | + (match.key->vlan_priority << VLAN_PRIO_SHIFT); + rule->tuples_mask.vlan_tag1 =3D match.mask->vlan_id | + (match.mask->vlan_priority << VLAN_PRIO_SHIFT); + } else { + rule->unused_tuple |=3D 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 =3D 0; + + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_match_control match; + + flow_rule_match_control(flow, &match); + addr_type =3D match.key->addr_type; + + if (flow_rule_has_control_flags(match.mask->flags, extack)) + return -EOPNOTSUPP; + } + + if (addr_type =3D=3D FLOW_DISSECTOR_KEY_IPV4_ADDRS) { + struct flow_match_ipv4_addrs match; + + flow_rule_match_ipv4_addrs(flow, &match); + rule->tuples.src_ip[IPV4_INDEX] =3D be32_to_cpu(match.key->src); + rule->tuples_mask.src_ip[IPV4_INDEX] =3D + be32_to_cpu(match.mask->src); + rule->tuples.dst_ip[IPV4_INDEX] =3D be32_to_cpu(match.key->dst); + rule->tuples_mask.dst_ip[IPV4_INDEX] =3D + be32_to_cpu(match.mask->dst); + if (!match.mask->src) + rule->unused_tuple |=3D BIT(INNER_SRC_IP); + if (!match.mask->dst) + rule->unused_tuple |=3D BIT(INNER_DST_IP); + } else if (addr_type =3D=3D 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(&match.mask->src)) + rule->unused_tuple |=3D BIT(INNER_SRC_IP); + if (ipv6_addr_any(&match.mask->dst)) + rule->unused_tuple |=3D BIT(INNER_DST_IP); + } else { + rule->unused_tuple |=3D BIT(INNER_SRC_IP); + rule->unused_tuple |=3D 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 =3D be16_to_cpu(match.key->src); + rule->tuples_mask.src_port =3D be16_to_cpu(match.mask->src); + rule->tuples.dst_port =3D be16_to_cpu(match.key->dst); + rule->tuples_mask.dst_port =3D be16_to_cpu(match.mask->dst); + } else { + rule->unused_tuple |=3D BIT(INNER_SRC_PORT); + rule->unused_tuple |=3D 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, + struct netlink_ext_ack *extack) +{ + 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 (be32_to_cpu(match.mask->keyid) !=3D + HCLGE_FD_VXLAN_VNI_UNMASK || + be32_to_cpu(match.key->keyid) >=3D VXLAN_N_VID || + !match.key->keyid) { + NL_SET_ERR_MSG_MOD(extack, "invalid enc_keyid"); + return -EINVAL; + } + + rule->tuples.outer_tun_vni =3D be32_to_cpu(match.key->keyid); + rule->tuples_mask.outer_tun_vni =3D + be32_to_cpu(match.mask->keyid); + } else { + rule->unused_tuple |=3D BIT(OUTER_TUN_VNI); + } + + return 0; +} + +static int hclge_get_cls_key_ip_tos(const struct flow_rule *flow, + struct hclge_fd_rule *rule, + struct netlink_ext_ack *extack) +{ + if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) { + struct flow_match_ip match; + + flow_rule_match_ip(flow, &match); + + if (match.mask->ttl) { + NL_SET_ERR_MSG_MOD(extack, "unsupported TTL"); + return -EOPNOTSUPP; + } + + rule->tuples.ip_tos =3D match.key->tos; + rule->tuples_mask.ip_tos =3D match.mask->tos; + if (!rule->tuples_mask.ip_tos) + rule->unused_tuple |=3D BIT(INNER_IP_TOS); + } else { + rule->unused_tuple |=3D BIT(INNER_IP_TOS); + } + + return 0; +} + +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 =3D flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack =3D cls_flower->common.extack; + struct hnae3_handle *handle =3D &hdev->vport[0].nic; + struct flow_action *action =3D &flow->action; + struct flow_action_entry *act; + int tc; + + if (!flow_action_has_entries(&flow->action)) { + tc =3D tc_classid_to_hwtc(handle->netdev, cls_flower->classid); + if (tc < 0 || tc > hdev->tc_max) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "invalid traffic class: %d", tc); + return -EINVAL; + } + + rule->action =3D HCLGE_FD_ACTION_SELECT_TC; + rule->cls_flower.tc =3D tc; + return 0; + } + + act =3D &action->entries[0]; + switch (act->id) { + case FLOW_ACTION_RX_QUEUE_MAPPING: + if (act->rx_queue >=3D handle->kinfo.num_tqps) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "queue id (%u) should be less than %u", + act->rx_queue, + handle->kinfo.num_tqps); + return -EINVAL; + } + + rule->queue_id =3D act->rx_queue; + rule->action =3D HCLGE_FD_ACTION_SELECT_QUEUE; + return 0; + case FLOW_ACTION_DROP: + rule->action =3D HCLGE_FD_ACTION_DROP_PACKET; + return 0; + default: + NL_SET_ERR_MSG_FMT_MOD(extack, + "unsupported action(%d)", 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 =3D flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack =3D cls_flower->common.extack; + int ret; + + /* not support any user def tuples */ + rule->unused_tuple |=3D 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 =3D hclge_get_cls_key_ip(flow, rule, extack); + if (ret) + return ret; + + hclge_get_cls_key_port(flow, rule); + ret =3D hclge_get_cls_key_ip_tos(flow, rule, extack); + if (ret) + return ret; + + return hclge_get_cls_enc_keyid(hdev, flow, rule, extack); +} + +static int hclge_check_cls_flower(struct hclge_dev *hdev, + struct flow_cls_offload *cls_flower) +{ + struct flow_rule *flow =3D flow_cls_offload_flow_rule(cls_flower); + struct netlink_ext_ack *extack =3D cls_flower->common.extack; + struct flow_dissector *dissector =3D flow->match.dissector; + u32 prio =3D cls_flower->common.prio; + u64 support_keys; + + if (prio =3D=3D 0 || + prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "prio %u should be in range[1, %u]", + prio, + hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); + return -EINVAL; + } + + if (test_bit(prio - 1, hdev->fd_bmap)) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "prio %u is already used", prio); + return -EINVAL; + } + + support_keys =3D BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + 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); + + if (hdev->fd_cfg.fd_mode =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) + support_keys |=3D BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID); + + if (dissector->used_keys & ~support_keys) { + NL_SET_ERR_MSG_FMT_MOD(extack, "unsupported key set: %#llx", + dissector->used_keys); + return -EOPNOTSUPP; + } + + /* driver will parses classid into an action */ + if (cls_flower->classid && flow_action_has_entries(&flow->action)) { + NL_SET_ERR_MSG_MOD(extack, + "cannot specify both classid and action"); + return -EOPNOTSUPP; + } + + if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) { + NL_SET_ERR_MSG_MOD(extack, + "must specify either classid or action"); + return -EINVAL; + } + + if (flow_action_has_entries(&flow->action) && + !flow_offload_has_one_action(&flow->action)) { + NL_SET_ERR_MSG_MOD(extack, "unsupported multiple actions"); + return -EOPNOTSUPP; + } + + return 0; +} + +int hclge_add_cls_flower(struct hnae3_handle *handle, + struct flow_cls_offload *cls_flower) +{ + struct netlink_ext_ack *extack =3D cls_flower->common.extack; + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + struct hclge_fd_rule *rule; + int ret; + + if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { + NL_SET_ERR_MSG_MOD(extack, "cls flower is not supported"); + return -EOPNOTSUPP; + } + + ret =3D hclge_check_cls_flower(hdev, cls_flower); + if (ret) + return ret; + + rule =3D kzalloc_obj(*rule); + if (!rule) + return -ENOMEM; + + ret =3D hclge_parse_cls_flower(hdev, cls_flower, rule); + if (ret) { + kfree(rule); + return ret; + } + + ret =3D hclge_get_tc_flower_action(hdev, cls_flower, rule); + if (ret) { + kfree(rule); + return ret; + } + + rule->location =3D cls_flower->common.prio - 1; + rule->vf_id =3D 0; + rule->cls_flower.cookie =3D cls_flower->cookie; + rule->rule_type =3D HCLGE_FD_TC_FLOWER_ACTIVE; + + ret =3D 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 =3D=3D cookie) + return rule; + } + + return NULL; +} + +int hclge_del_cls_flower(struct hnae3_handle *handle, + struct flow_cls_offload *cls_flower) +{ + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D 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 =3D hclge_find_cls_flower(hdev, cls_flower->cookie); + if (!rule) { + spin_unlock_bh(&hdev->fd_rule_lock); + return -EINVAL; + } + + ret =3D 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 =3D 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 =3D hclge_fd_config_rule(hdev, rule); + if (ret) + goto out; + rule->state =3D HCLGE_FD_ACTIVE; + break; + case HCLGE_FD_TO_DEL: + ret =3D 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 =3D hdev->fd_active_type =3D=3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; + + hdev->fd_en =3D 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/driver= s/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h new file mode 100644 index 000000000000..2f66cc9c3c65 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_fd.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2026 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/driv= ers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index b8d4858fe39a..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 =20 #include -#include #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 *h= dev); 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[] =3D { }, }; =20 -static const struct key_info meta_data_key_info[] =3D { - { 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[] =3D { - { 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,604 +5108,80 @@ static void hclge_request_update_promisc_mode(stru= ct hnae3_handle *handle) set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state); } =20 -static void hclge_sync_fd_state(struct hclge_dev *hdev) -{ - if (hlist_empty(&hdev->fd_rule_list)) - hdev->fd_active_type =3D 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 =3D old_rule->rule_node.next; - new_rule->rule_node.pprev =3D 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 =3D=3D HCLGE_FD_TO_ADD) { - hclge_fd_dec_rule_cnt(hdev, old_rule->location); - hclge_fd_free_node(hdev, old_rule); - return; - } - old_rule->state =3D 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 =3D=3D 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 =3D 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 =3D 0; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false); - - req =3D (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 =3D cpu_to_le16(data); - - data =3D 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 =3D cpu_to_le16(data); - - data =3D 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 =3D cpu_to_le16(data); - - ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, - "failed to set fd user def data, ret=3D %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 =3D 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 =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; =20 - 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); } =20 -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 =3D &hdev->fd_rule_list; - struct hclge_fd_rule *fd_rule, *parent =3D NULL; - struct hclge_fd_user_def_info *info, *old_info; - struct hclge_fd_user_def_cfg *cfg; - - if (!rule || rule->rule_type !=3D HCLGE_FD_EP_ACTIVE || - rule->ep.user_def.layer =3D=3D HCLGE_FD_USER_DEF_NONE) - return 0; - - /* for valid layer is start from 1, so need minus 1 to get the cfg */ - cfg =3D &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; - info =3D &rule->ep.user_def; - - if (!cfg->ref_cnt || cfg->offset =3D=3D info->offset) - return 0; - - if (cfg->ref_cnt > 1) - goto error; - - fd_rule =3D hclge_find_fd_rule(hlist, rule->location, &parent); - if (fd_rule) { - old_info =3D &fd_rule->ep.user_def; - if (info->layer =3D=3D old_info->layer) - return 0; - } + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; =20 -error: - dev_err(&hdev->pdev->dev, - "No available offset for layer%d fd rule, each layer only support one us= er def offset.\n", - info->layer + 1); - return -ENOSPC; + return test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state); } =20 -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 !=3D HCLGE_FD_EP_ACTIVE || - rule->ep.user_def.layer =3D=3D HCLGE_FD_USER_DEF_NONE) - return; + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; =20 - cfg =3D &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; - if (!cfg->ref_cnt) { - cfg->offset =3D 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); } =20 -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 !=3D HCLGE_FD_EP_ACTIVE || - rule->ep.user_def.layer =3D=3D HCLGE_FD_USER_DEF_NONE) - return; - - cfg =3D &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1]; - if (!cfg->ref_cnt) - return; + struct hclge_vport *vport =3D hclge_get_vport(handle); + struct hclge_dev *hdev =3D vport->back; =20 - cfg->ref_cnt--; - if (!cfg->ref_cnt) { - cfg->offset =3D 0; - set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); - } + return hdev->rst_stats.hw_reset_done_cnt; } =20 -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 =3D &hdev->fd_rule_list; - struct hclge_fd_rule *fd_rule, *parent =3D NULL; - - fd_rule =3D hclge_find_fd_rule(hlist, location, &parent); - if (fd_rule) { - hclge_fd_dec_user_def_refcnt(hdev, fd_rule); - if (state =3D=3D 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 =3D=3D HCLGE_FD_TO_DEL || state =3D=3D HCLGE_FD_DELETE= D)) { - 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 =3D=3D 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 =20 -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 =3D + (struct hclge_config_mac_mode_cmd *)desc.data; + u32 loop_en =3D 0; int ret; =20 - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true); - - req =3D (struct hclge_get_fd_mode_cmd *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false); =20 - ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) { - dev_err(&hdev->pdev->dev, "get fd mode fail, ret=3D%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); } =20 - *fd_mode =3D 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 =3D (struct hclge_get_fd_allocation_cmd *)desc.data; + req->txrx_pad_fcs_loop_en =3D cpu_to_le32(loop_en); =20 ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); if (ret) { - dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=3D%d\n", - ret); - return ret; - } - - *stage1_entry_num =3D le32_to_cpu(req->stage1_entry_num); - *stage2_entry_num =3D le32_to_cpu(req->stage2_entry_num); - *stage1_counter_num =3D le16_to_cpu(req->stage1_counter_num); - *stage2_counter_num =3D 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 =3D (struct hclge_set_fd_key_config_cmd *)desc.data; - stage =3D &hdev->fd_cfg.key_cfg[stage_num]; - req->stage =3D stage_num; - req->key_select =3D stage->key_sel; - req->inner_sipv6_word_en =3D stage->inner_sipv6_word_en; - req->inner_dipv6_word_en =3D stage->inner_dipv6_word_en; - req->outer_sipv6_word_en =3D stage->outer_sipv6_word_en; - req->outer_dipv6_word_en =3D stage->outer_dipv6_word_en; - req->tuple_mask =3D cpu_to_le32(~stage->tuple_active); - req->meta_data_mask =3D cpu_to_le32(~stage->meta_data_active); - - ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, "set fd key fail, ret=3D%d\n", ret); - - return ret; -} - -static void hclge_fd_disable_user_def(struct hclge_dev *hdev) -{ - struct hclge_fd_user_def_cfg *cfg =3D 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 =3D 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 =3D MAX_KEY_LENGTH; - break; - case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1: - hdev->fd_cfg.max_key_length =3D 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 =3D &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1]; - key_cfg->key_sel =3D HCLGE_FD_KEY_BASE_ON_TUPLE; - key_cfg->inner_sipv6_word_en =3D LOW_2_WORDS; - key_cfg->inner_dipv6_word_en =3D LOW_2_WORDS; - key_cfg->outer_sipv6_word_en =3D 0; - key_cfg->outer_dipv6_word_en =3D 0; - - key_cfg->tuple_active =3D 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 =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1= ) { - key_cfg->tuple_active |=3D BIT(INNER_DST_MAC) | - BIT(INNER_SRC_MAC) | - BIT(OUTER_TUN_VNI); - if (hdev->ae_dev->dev_version >=3D HNAE3_DEVICE_VERSION_V3) - key_cfg->tuple_active |=3D 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 =3D BIT(ROCE_TYPE) | BIT(DST_VPORT); - - ret =3D 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 |=3D cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); - hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false); - desc[1].flag |=3D cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); - hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false); - - req1 =3D (struct hclge_fd_tcam_config_1_cmd *)desc[0].data; - req2 =3D (struct hclge_fd_tcam_config_2_cmd *)desc[1].data; - req3 =3D (struct hclge_fd_tcam_config_3_cmd *)desc[2].data; - - req1->stage =3D stage; - req1->xy_sel =3D sel_x ? 1 : 0; - hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0); - req1->index =3D cpu_to_le32(loc); - req1->entry_vld =3D 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 =3D%d.\n", ret); + return; } =20 - ret =3D hclge_cmd_send(&hdev->hw, desc, 3); - if (ret) - dev_err(&hdev->pdev->dev, - "config tcam key fail, ret=3D%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 =3D pci_get_drvdata(hdev->pdev); - struct hclge_fd_ad_config_cmd *req; - struct hclge_desc desc; - u64 ad_data =3D 0; - int ret; - - hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false); - - req =3D (struct hclge_fd_ad_config_cmd *)desc.data; - req->index =3D cpu_to_le32(loc); - req->stage =3D 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 >=3D HCLGE_TQP_MAX_SIZE_DEV_V2 ? 1 : 0); - hnae3_set_bit(ad_data, HCLGE_FD_AD_COUNTER_NUM_H_B, - action->counter_id >=3D HCLGE_FD_COUNTER_MAX_SIZE_DEV_V2 ? - 1 : 0); - ad_data <<=3D 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 =3D cpu_to_le64(ad_data); - ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) - dev_err(&hdev->pdev->dev, "fd ad config fail, ret=3D%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 =3D (u8 *)rule; - __le32 le_x, le_y; - int i; - - if (rule->unused_tuple & BIT(tuple_bit)) - return true; - - key_opt =3D tuple_key_info[tuple_bit].key_opt; - offset =3D tuple_key_info[tuple_bit].offset; - moffset =3D 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 =3D cpu_to_le16(tmp_x_s); - *(__le16 *)key_y =3D 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 =3D cpu_to_le32(tmp_x_l); - *(__le32 *)key_y =3D cpu_to_le32(tmp_y_l); - - return true; - case KEY_OPT_MAC: - for (i =3D 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 =3D 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 =3D cpu_to_le32(tmp_x_l); - *(__le32 *)key_y =3D 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])); - le_x =3D cpu_to_le32(tmp_x_l); - le_y =3D cpu_to_le32(tmp_y_l); - memcpy(key_x, &le_x, HCLGE_VNI_LENGTH); - memcpy(key_y, &le_y, HCLGE_VNI_LENGTH); - - return true; - default: - return false; - } + if (!enable) + hclge_mac_link_status_wait(hdev, HCLGE_LINK_STATUS_DOWN, + HCLGE_LINK_STATUS_WAIT_CNT); } =20 -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 =3D 0; =20 @@ -5799,1996 +5200,6 @@ static u32 hclge_get_port_number(enum HLCGE_PORT_T= YPE port_type, u8 pf_id, return port_number; } =20 -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 =3D 0, tmp_x, tmp_y, port_number; - u8 cur_pos =3D 0, tuple_size, shift_bits; - unsigned int i; - - for (i =3D 0; i < MAX_META_DATA; i++) { - tuple_size =3D meta_data_key_info[i].key_length; - tuple_bit =3D 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 +=3D tuple_size; - break; - case BIT(DST_VPORT): - port_number =3D 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 +=3D tuple_size; - break; - default: - break; - } - } - - calc_x(tmp_x, meta_data, 0xFFFFFFFF); - calc_y(tmp_y, meta_data, 0xFFFFFFFF); - shift_bits =3D sizeof(meta_data) * 8 - cur_pos; - - *key_x =3D cpu_to_le32(tmp_x << shift_bits); - *key_y =3D 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 =3D &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 =3D key_x; - cur_key_y =3D key_y; - - for (i =3D 0; i < MAX_TUPLE; i++) { - bool tuple_valid; - - tuple_size =3D tuple_key_info[i].key_length / 8; - if (!(key_cfg->tuple_active & BIT(i))) - continue; - - tuple_valid =3D hclge_fd_convert_tuple(i, cur_key_x, - cur_key_y, rule); - if (tuple_valid) { - cur_key_x +=3D tuple_size; - cur_key_y +=3D tuple_size; - } - } - - meta_data_region =3D 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 =3D 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=3D%u, ret=3D%d\n", - rule->queue_id, ret); - return ret; - } - - ret =3D 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=3D%u, ret=3D%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 =3D hdev->vport; - struct hnae3_knic_private_info *kinfo =3D &vport->nic.kinfo; - struct hclge_fd_ad_data ad_data; - - memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data)); - ad_data.ad_id =3D rule->location; - - if (rule->action =3D=3D HCLGE_FD_ACTION_DROP_PACKET) { - ad_data.drop_packet =3D true; - } else if (rule->action =3D=3D HCLGE_FD_ACTION_SELECT_TC) { - ad_data.override_tc =3D true; - ad_data.queue_id =3D - kinfo->tc_info.tqp_offset[rule->cls_flower.tc]; - ad_data.tc_size =3D - ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]); - } else { - ad_data.forward_to_direct_queue =3D true; - ad_data.queue_id =3D rule->queue_id; - } - - if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) { - ad_data.use_counter =3D true; - ad_data.counter_id =3D rule->vf_id % - hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]; - } else { - ad_data.use_counter =3D false; - ad_data.counter_id =3D 0; - } - - ad_data.use_next_stage =3D false; - ad_data.next_input_key =3D 0; - - ad_data.write_rule_id_to_bd =3D true; - ad_data.rule_id =3D 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 |=3D BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC); - - if (!spec->ip4src) - *unused_tuple |=3D BIT(INNER_SRC_IP); - - if (!spec->ip4dst) - *unused_tuple |=3D BIT(INNER_DST_IP); - - if (!spec->psrc) - *unused_tuple |=3D BIT(INNER_SRC_PORT); - - if (!spec->pdst) - *unused_tuple |=3D BIT(INNER_DST_PORT); - - if (!spec->tos) - *unused_tuple |=3D 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 |=3D BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | - BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); - - if (!spec->ip4src) - *unused_tuple |=3D BIT(INNER_SRC_IP); - - if (!spec->ip4dst) - *unused_tuple |=3D BIT(INNER_DST_IP); - - if (!spec->tos) - *unused_tuple |=3D BIT(INNER_IP_TOS); - - if (!spec->proto) - *unused_tuple |=3D BIT(INNER_IP_PROTO); - - if (spec->l4_4_bytes) - return -EOPNOTSUPP; - - if (spec->ip_ver !=3D 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 |=3D 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 |=3D BIT(INNER_SRC_IP); - - if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) - *unused_tuple |=3D BIT(INNER_DST_IP); - - if (!spec->psrc) - *unused_tuple |=3D BIT(INNER_SRC_PORT); - - if (!spec->pdst) - *unused_tuple |=3D BIT(INNER_DST_PORT); - - if (!spec->tclass) - *unused_tuple |=3D 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 |=3D 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 |=3D BIT(INNER_SRC_IP); - - if (ipv6_addr_any((struct in6_addr *)spec->ip6dst)) - *unused_tuple |=3D BIT(INNER_DST_IP); - - if (!spec->l4_proto) - *unused_tuple |=3D BIT(INNER_IP_PROTO); - - if (!spec->tclass) - *unused_tuple |=3D 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_tup= le) -{ - if (!spec || !unused_tuple) - return -EINVAL; - - *unused_tuple |=3D 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 |=3D BIT(INNER_SRC_MAC); - - if (is_zero_ether_addr(spec->h_dest)) - *unused_tuple |=3D BIT(INNER_DST_MAC); - - if (!spec->h_proto) - *unused_tuple |=3D 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 |=3D BIT(INNER_VLAN_TAG_FST); - - if (fs->m_ext.vlan_tci && - be16_to_cpu(fs->h_ext.vlan_tci) >=3D 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 |=3D BIT(INNER_VLAN_TAG_FST); - } - - if (fs->flow_type & FLOW_MAC_EXT) { - if (hdev->fd_cfg.fd_mode !=3D - 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 |=3D BIT(INNER_DST_MAC); - else - *unused_tuple &=3D ~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 =3D HCLGE_FD_USER_DEF_L2; - *unused_tuple &=3D ~BIT(INNER_L2_RSV); - break; - case IP_USER_FLOW: - case IPV6_USER_FLOW: - info->layer =3D HCLGE_FD_USER_DEF_L3; - *unused_tuple &=3D ~BIT(INNER_L3_RSV); - break; - case TCP_V4_FLOW: - case UDP_V4_FLOW: - case TCP_V6_FLOW: - case UDP_V6_FLOW: - info->layer =3D HCLGE_FD_USER_DEF_L4; - *unused_tuple &=3D ~BIT(INNER_L4_RSV); - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *f= s) -{ - return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) =3D=3D 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 =3D hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active; - u32 flow_type =3D fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); - u16 data, offset, data_mask, offset_mask; - int ret; - - info->layer =3D HCLGE_FD_USER_DEF_NONE; - *unused_tuple |=3D 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 =3D be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; - data_mask =3D be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA; - offset =3D be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET; - offset_mask =3D 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 !=3D HCLGE_FD_USER_DEF_OFFSET_UNMASK) { - dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n"); - return -EINVAL; - } - - ret =3D 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 =3D %d\n", - ret); - return ret; - } - - info->data =3D data; - info->data_mask =3D data_mask; - info->offset =3D 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 >=3D 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 =3D hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info); - if (ret) - return ret; - - flow_type =3D fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT); - switch (flow_type) { - case SCTP_V4_FLOW: - case TCP_V4_FLOW: - case UDP_V4_FLOW: - ret =3D hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec, - unused_tuple); - break; - case IP_USER_FLOW: - ret =3D 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 =3D hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec, - unused_tuple); - break; - case IPV6_USER_FLOW: - ret =3D hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec, - unused_tuple); - break; - case ETHER_FLOW: - if (hdev->fd_cfg.fd_mode !=3D - 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 =3D hclge_fd_check_ether_tuple(&fs->h_u.ether_spec, - unused_tuple); - break; - default: - dev_err(&hdev->pdev->dev, - "unsupported protocol type, protocol type =3D %#x\n", - flow_type); - return -EOPNOTSUPP; - } - - if (ret) { - dev_err(&hdev->pdev->dev, - "failed to check flow union tuple, ret =3D %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] =3D - be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src); - rule->tuples_mask.src_ip[IPV4_INDEX] =3D - be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src); - - rule->tuples.dst_ip[IPV4_INDEX] =3D - be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst); - rule->tuples_mask.dst_ip[IPV4_INDEX] =3D - be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst); - - rule->tuples.src_port =3D be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc); - rule->tuples_mask.src_port =3D be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc); - - rule->tuples.dst_port =3D be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst); - rule->tuples_mask.dst_port =3D be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst); - - rule->tuples.ip_tos =3D fs->h_u.tcp_ip4_spec.tos; - rule->tuples_mask.ip_tos =3D fs->m_u.tcp_ip4_spec.tos; - - rule->tuples.ether_proto =3D ETH_P_IP; - rule->tuples_mask.ether_proto =3D 0xFFFF; - - rule->tuples.ip_proto =3D ip_proto; - rule->tuples_mask.ip_proto =3D 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] =3D - be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src); - rule->tuples_mask.src_ip[IPV4_INDEX] =3D - be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src); - - rule->tuples.dst_ip[IPV4_INDEX] =3D - be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst); - rule->tuples_mask.dst_ip[IPV4_INDEX] =3D - be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst); - - rule->tuples.ip_tos =3D fs->h_u.usr_ip4_spec.tos; - rule->tuples_mask.ip_tos =3D fs->m_u.usr_ip4_spec.tos; - - rule->tuples.ip_proto =3D fs->h_u.usr_ip4_spec.proto; - rule->tuples_mask.ip_proto =3D fs->m_u.usr_ip4_spec.proto; - - rule->tuples.ether_proto =3D ETH_P_IP; - rule->tuples_mask.ether_proto =3D 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 =3D be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc); - rule->tuples_mask.src_port =3D be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc); - - rule->tuples.dst_port =3D be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst); - rule->tuples_mask.dst_port =3D be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst); - - rule->tuples.ether_proto =3D ETH_P_IPV6; - rule->tuples_mask.ether_proto =3D 0xFFFF; - - rule->tuples.ip_tos =3D fs->h_u.tcp_ip6_spec.tclass; - rule->tuples_mask.ip_tos =3D fs->m_u.tcp_ip6_spec.tclass; - - rule->tuples.ip_proto =3D ip_proto; - rule->tuples_mask.ip_proto =3D 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 =3D fs->h_u.usr_ip6_spec.l4_proto; - rule->tuples_mask.ip_proto =3D fs->m_u.usr_ip6_spec.l4_proto; - - rule->tuples.ip_tos =3D fs->h_u.tcp_ip6_spec.tclass; - rule->tuples_mask.ip_tos =3D fs->m_u.tcp_ip6_spec.tclass; - - rule->tuples.ether_proto =3D ETH_P_IPV6; - rule->tuples_mask.ether_proto =3D 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 =3D be16_to_cpu(fs->h_u.ether_spec.h_proto); - rule->tuples_mask.ether_proto =3D be16_to_cpu(fs->m_u.ether_spec.h_proto); -} - -static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *inf= o, - struct hclge_fd_rule *rule) -{ - switch (info->layer) { - case HCLGE_FD_USER_DEF_L2: - rule->tuples.l2_user_def =3D info->data; - rule->tuples_mask.l2_user_def =3D info->data_mask; - break; - case HCLGE_FD_USER_DEF_L3: - rule->tuples.l3_user_def =3D info->data; - rule->tuples_mask.l3_user_def =3D info->data_mask; - break; - case HCLGE_FD_USER_DEF_L4: - rule->tuples.l4_user_def =3D (u32)info->data << 16; - rule->tuples_mask.l4_user_def =3D (u32)info->data_mask << 16; - break; - default: - break; - } - - rule->ep.user_def =3D *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 =3D 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 =3D be16_to_cpu(fs->h_ext.vlan_tci); - rule->tuples_mask.vlan_tag1 =3D 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 =3D 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 !=3D rule->rule_type && - (hdev->fd_active_type =3D=3D HCLGE_FD_TC_FLOWER_ACTIVE || - hdev->fd_active_type =3D=3D HCLGE_FD_EP_ACTIVE)) { - dev_err(&hdev->pdev->dev, - "mode conflict(new type %d, active type %d), please delete existent rul= es first\n", - rule->rule_type, hdev->fd_active_type); - spin_unlock_bh(&hdev->fd_rule_lock); - return -EINVAL; - } - - ret =3D hclge_fd_check_user_def_refcnt(hdev, rule); - if (ret) - goto out; - - ret =3D hclge_clear_arfs_rules(hdev); - if (ret) - goto out; - - ret =3D hclge_fd_config_rule(hdev, rule); - if (ret) - goto out; - - rule->state =3D HCLGE_FD_ACTIVE; - hdev->fd_active_type =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - - return hdev->fd_active_type =3D=3D HCLGE_FD_TC_FLOWER_ACTIVE; -} - -static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_coo= kie, - u16 *vport_id, u8 *action, u16 *queue_id) -{ - struct hclge_vport *vport =3D hdev->vport; - - if (ring_cookie =3D=3D RX_CLS_FLOW_DISC) { - *action =3D HCLGE_FD_ACTION_DROP_PACKET; - } else { - u32 ring =3D ethtool_get_flow_spec_ring(ring_cookie); - u8 vf =3D 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 =3D vf ? hdev->vport[vf].vport_id : vport->vport_id; - tqps =3D hdev->vport[vf].nic.kinfo.num_tqps; - - if (ring >=3D tqps) { - dev_err(&hdev->pdev->dev, - "Error: queue id (%u) > max tqp num (%u)\n", - ring, tqps - 1U); - return -EINVAL; - } - - *action =3D HCLGE_FD_ACTION_SELECT_QUEUE; - *queue_id =3D ring; - } - - return 0; -} - -static int hclge_add_fd_entry(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd) -{ - struct hclge_vport *vport =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - struct hclge_fd_user_def_info info; - u16 dst_vport_id =3D 0, q_index =3D 0; - struct ethtool_rx_flow_spec *fs; - struct hclge_fd_rule *rule; - u32 unused =3D 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 =3D (struct ethtool_rx_flow_spec *)&cmd->fs; - - ret =3D hclge_fd_check_spec(hdev, fs, &unused, &info); - if (ret) - return ret; - - ret =3D hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id, - &action, &q_index); - if (ret) - return ret; - - rule =3D kzalloc_obj(*rule); - if (!rule) - return -ENOMEM; - - ret =3D hclge_fd_get_tuple(fs, rule, &info); - if (ret) { - kfree(rule); - return ret; - } - - rule->flow_type =3D fs->flow_type; - rule->location =3D fs->location; - rule->unused_tuple =3D unused; - rule->vf_id =3D dst_vport_id; - rule->queue_id =3D q_index; - rule->action =3D action; - rule->rule_type =3D HCLGE_FD_EP_ACTIVE; - - ret =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - struct ethtool_rx_flow_spec *fs; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - fs =3D (struct ethtool_rx_flow_spec *)&cmd->fs; - - if (fs->location >=3D hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) - return -EINVAL; - - spin_lock_bh(&hdev->fd_rule_lock); - if (hdev->fd_active_type =3D=3D 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 =3D 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 =3D HCLGE_FD_RULE_NONE; - hdev->hclge_fd_rule_num =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D 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 =3D=3D HCLGE_FD_ACTIVE) - rule->state =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || hclge_is_cls_flower_activ= e(handle)) - return -EOPNOTSUPP; - - cmd->rule_cnt =3D hdev->hclge_fd_rule_num; - cmd->data =3D 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 =3D cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); - spec_mask->ip4src =3D rule->unused_tuple & BIT(INNER_SRC_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); - - spec->ip4dst =3D cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); - spec_mask->ip4dst =3D rule->unused_tuple & BIT(INNER_DST_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); - - spec->psrc =3D cpu_to_be16(rule->tuples.src_port); - spec_mask->psrc =3D rule->unused_tuple & BIT(INNER_SRC_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.src_port); - - spec->pdst =3D cpu_to_be16(rule->tuples.dst_port); - spec_mask->pdst =3D rule->unused_tuple & BIT(INNER_DST_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.dst_port); - - spec->tos =3D rule->tuples.ip_tos; - spec_mask->tos =3D 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 =3D cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]); - spec_mask->ip4src =3D rule->unused_tuple & BIT(INNER_SRC_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]); - - spec->ip4dst =3D cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]); - spec_mask->ip4dst =3D rule->unused_tuple & BIT(INNER_DST_IP) ? - 0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]); - - spec->tos =3D rule->tuples.ip_tos; - spec_mask->tos =3D rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; - - spec->proto =3D rule->tuples.ip_proto; - spec_mask->proto =3D rule->unused_tuple & BIT(INNER_IP_PROTO) ? - 0 : rule->tuples_mask.ip_proto; - - spec->ip_ver =3D 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 =3D rule->tuples.ip_tos; - spec_mask->tclass =3D rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; - - spec->psrc =3D cpu_to_be16(rule->tuples.src_port); - spec_mask->psrc =3D rule->unused_tuple & BIT(INNER_SRC_PORT) ? - 0 : cpu_to_be16(rule->tuples_mask.src_port); - - spec->pdst =3D cpu_to_be16(rule->tuples.dst_port); - spec_mask->pdst =3D 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 =3D rule->tuples.ip_tos; - spec_mask->tclass =3D rule->unused_tuple & BIT(INNER_IP_TOS) ? - 0 : rule->tuples_mask.ip_tos; - - spec->l4_proto =3D rule->tuples.ip_proto; - spec_mask->l4_proto =3D 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 =3D cpu_to_be16(rule->tuples.ether_proto); - spec_mask->h_proto =3D 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) =3D=3D - HCLGE_FD_TUPLE_USER_DEF_TUPLES) { - fs->h_ext.data[0] =3D 0; - fs->h_ext.data[1] =3D 0; - fs->m_ext.data[0] =3D 0; - fs->m_ext.data[1] =3D 0; - } else { - fs->h_ext.data[0] =3D cpu_to_be32(rule->ep.user_def.offset); - fs->h_ext.data[1] =3D cpu_to_be32(rule->ep.user_def.data); - fs->m_ext.data[0] =3D - cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK); - fs->m_ext.data[1] =3D 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 =3D cpu_to_be16(rule->tuples.vlan_tag1); - fs->m_ext.vlan_tci =3D - 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 =3D NULL; - struct hlist_node *node2; - - hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) { - if (rule->location =3D=3D 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 =3D=3D HCLGE_FD_ACTION_DROP_PACKET) { - fs->ring_cookie =3D RX_CLS_FLOW_DISC; - } else { - u64 vf_id; - - fs->ring_cookie =3D rule->queue_id; - vf_id =3D rule->vf_id; - vf_id <<=3D ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; - fs->ring_cookie |=3D vf_id; - } -} - -static int hclge_get_fd_rule_info(struct hnae3_handle *handle, - struct ethtool_rxnfc *cmd) -{ - struct hclge_vport *vport =3D hclge_get_vport(handle); - struct hclge_fd_rule *rule =3D NULL; - struct hclge_dev *hdev =3D vport->back; - struct ethtool_rx_flow_spec *fs; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - fs =3D (struct ethtool_rx_flow_spec *)&cmd->fs; - - spin_lock_bh(&hdev->fd_rule_lock); - - rule =3D hclge_get_fd_rule(hdev, fs->location); - if (!rule) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOENT; - } - - fs->flow_type =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - struct hclge_fd_rule *rule; - struct hlist_node *node2; - u32 cnt =3D 0; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) - return -EOPNOTSUPP; - - cmd->data =3D 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 =3D=3D cmd->rule_cnt) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -EMSGSIZE; - } - - if (rule->state =3D=3D HCLGE_FD_TO_DEL) - continue; - - rule_locs[cnt] =3D rule->location; - cnt++; - } - - spin_unlock_bh(&hdev->fd_rule_lock); - - cmd->rule_cnt =3D 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 =3D be16_to_cpu(fkeys->basic.n_proto); - tuples->ip_proto =3D fkeys->basic.ip_proto; - tuples->dst_port =3D be16_to_cpu(fkeys->ports.dst); - - if (fkeys->basic.n_proto =3D=3D htons(ETH_P_IP)) { - tuples->src_ip[3] =3D be32_to_cpu(fkeys->addrs.v4addrs.src); - tuples->dst_ip[3] =3D be32_to_cpu(fkeys->addrs.v4addrs.dst); - } else { - int i; - - for (i =3D 0; i < IPV6_ADDR_WORDS; i++) { - tuples->src_ip[i] =3D be32_to_cpu(flow_ip6_src[i]); - tuples->dst_ip[i] =3D 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 =3D 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 *tu= ples, - struct hclge_fd_rule *rule) -{ - rule->unused_tuple =3D BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) | - BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) | - BIT(INNER_SRC_PORT); - rule->action =3D 0; - rule->vf_id =3D 0; - rule->rule_type =3D HCLGE_FD_ARFS_ACTIVE; - rule->state =3D HCLGE_FD_TO_ADD; - if (tuples->ether_proto =3D=3D ETH_P_IP) { - if (tuples->ip_proto =3D=3D IPPROTO_TCP) - rule->flow_type =3D TCP_V4_FLOW; - else - rule->flow_type =3D UDP_V4_FLOW; - } else { - if (tuples->ip_proto =3D=3D IPPROTO_TCP) - rule->flow_type =3D TCP_V6_FLOW; - else - rule->flow_type =3D 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 que= ue_id, - u16 flow_id, struct flow_keys *fkeys) -{ - struct hclge_vport *vport =3D hclge_get_vport(handle); - struct hclge_fd_rule_tuples new_tuples =3D {}; - struct hclge_dev *hdev =3D 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 !=3D HCLGE_FD_ARFS_ACTIVE && - hdev->fd_active_type !=3D 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 =3D hclge_fd_search_flow_keys(hdev, &new_tuples); - if (!rule) { - bit_id =3D find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM); - if (bit_id >=3D hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOSPC; - } - - rule =3D kzalloc_obj(*rule, GFP_ATOMIC); - if (!rule) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -ENOMEM; - } - - rule->location =3D bit_id; - rule->arfs.flow_id =3D flow_id; - rule->queue_id =3D queue_id; - hclge_fd_build_arfs_rule(&new_tuples, rule); - hclge_update_fd_list(hdev, rule->state, rule->location, rule); - hdev->fd_active_type =3D HCLGE_FD_ARFS_ACTIVE; - } else if (rule->queue_id !=3D queue_id) { - rule->queue_id =3D queue_id; - rule->state =3D 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 =3D &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 !=3D 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 !=3D HCLGE_FD_ACTIVE) - continue; - if (rps_may_expire_flow(handle->netdev, rule->queue_id, - rule->arfs.flow_id, rule->location)) { - rule->state =3D 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 !=3D 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 =3D 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 =3D ntohs(match.key->n_proto); - ethtype_mask =3D ntohs(match.mask->n_proto); - - if (ethtype_key =3D=3D ETH_P_ALL) { - ethtype_key =3D 0; - ethtype_mask =3D 0; - } - rule->tuples.ether_proto =3D ethtype_key; - rule->tuples_mask.ether_proto =3D ethtype_mask; - rule->tuples.ip_proto =3D match.key->ip_proto; - rule->tuples_mask.ip_proto =3D match.mask->ip_proto; - } else { - rule->unused_tuple |=3D BIT(INNER_IP_PROTO); - rule->unused_tuple |=3D 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.mask->dst)) - rule->unused_tuple |=3D BIT(INNER_DST_MAC); - if (is_zero_ether_addr(match.mask->src)) - rule->unused_tuple |=3D BIT(INNER_SRC_MAC); - } else { - rule->unused_tuple |=3D BIT(INNER_DST_MAC); - rule->unused_tuple |=3D 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 =3D match.key->vlan_id | - (match.key->vlan_priority << VLAN_PRIO_SHIFT); - rule->tuples_mask.vlan_tag1 =3D match.mask->vlan_id | - (match.mask->vlan_priority << VLAN_PRIO_SHIFT); - } else { - rule->unused_tuple |=3D 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 =3D 0; - - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) { - struct flow_match_control match; - - flow_rule_match_control(flow, &match); - addr_type =3D match.key->addr_type; - - if (flow_rule_has_control_flags(match.mask->flags, extack)) - return -EOPNOTSUPP; - } - - if (addr_type =3D=3D FLOW_DISSECTOR_KEY_IPV4_ADDRS) { - struct flow_match_ipv4_addrs match; - - flow_rule_match_ipv4_addrs(flow, &match); - rule->tuples.src_ip[IPV4_INDEX] =3D be32_to_cpu(match.key->src); - rule->tuples_mask.src_ip[IPV4_INDEX] =3D - be32_to_cpu(match.mask->src); - rule->tuples.dst_ip[IPV4_INDEX] =3D be32_to_cpu(match.key->dst); - rule->tuples_mask.dst_ip[IPV4_INDEX] =3D - be32_to_cpu(match.mask->dst); - if (!match.mask->src) - rule->unused_tuple |=3D BIT(INNER_SRC_IP); - if (!match.mask->dst) - rule->unused_tuple |=3D BIT(INNER_DST_IP); - } else if (addr_type =3D=3D 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(&match.mask->src)) - rule->unused_tuple |=3D BIT(INNER_SRC_IP); - if (ipv6_addr_any(&match.mask->dst)) - rule->unused_tuple |=3D BIT(INNER_DST_IP); - } else { - rule->unused_tuple |=3D BIT(INNER_SRC_IP); - rule->unused_tuple |=3D 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 =3D be16_to_cpu(match.key->src); - rule->tuples_mask.src_port =3D be16_to_cpu(match.mask->src); - rule->tuples.dst_port =3D be16_to_cpu(match.key->dst); - rule->tuples_mask.dst_port =3D be16_to_cpu(match.mask->dst); - } else { - rule->unused_tuple |=3D BIT(INNER_SRC_PORT); - rule->unused_tuple |=3D 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, - struct netlink_ext_ack *extack) -{ - 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 (be32_to_cpu(match.mask->keyid) !=3D - HCLGE_FD_VXLAN_VNI_UNMASK || - be32_to_cpu(match.key->keyid) >=3D VXLAN_N_VID || - !match.key->keyid) { - NL_SET_ERR_MSG_MOD(extack, "invalid enc_keyid"); - return -EINVAL; - } - - rule->tuples.outer_tun_vni =3D be32_to_cpu(match.key->keyid); - rule->tuples_mask.outer_tun_vni =3D - be32_to_cpu(match.mask->keyid); - } else { - rule->unused_tuple |=3D BIT(OUTER_TUN_VNI); - } - - return 0; -} - -static int hclge_get_cls_key_ip_tos(const struct flow_rule *flow, - struct hclge_fd_rule *rule, - struct netlink_ext_ack *extack) -{ - if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_IP)) { - struct flow_match_ip match; - - flow_rule_match_ip(flow, &match); - - if (match.mask->ttl) { - NL_SET_ERR_MSG_MOD(extack, "unsupported TTL"); - return -EOPNOTSUPP; - } - - rule->tuples.ip_tos =3D match.key->tos; - rule->tuples_mask.ip_tos =3D match.mask->tos; - if (!rule->tuples_mask.ip_tos) - rule->unused_tuple |=3D BIT(INNER_IP_TOS); - } else { - rule->unused_tuple |=3D BIT(INNER_IP_TOS); - } - - return 0; -} - -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 =3D flow_cls_offload_flow_rule(cls_flower); - struct netlink_ext_ack *extack =3D cls_flower->common.extack; - struct hnae3_handle *handle =3D &hdev->vport[0].nic; - struct flow_action *action =3D &flow->action; - struct flow_action_entry *act; - int tc; - - if (!flow_action_has_entries(&flow->action)) { - tc =3D tc_classid_to_hwtc(handle->netdev, cls_flower->classid); - if (tc < 0 || tc > hdev->tc_max) { - NL_SET_ERR_MSG_FMT_MOD(extack, - "invalid traffic class: %d", - tc); - return -EINVAL; - } - - rule->action =3D HCLGE_FD_ACTION_SELECT_TC; - rule->cls_flower.tc =3D tc; - return 0; - } - - act =3D &action->entries[0]; - switch (act->id) { - case FLOW_ACTION_RX_QUEUE_MAPPING: - if (act->rx_queue >=3D handle->kinfo.num_tqps) { - NL_SET_ERR_MSG_FMT_MOD(extack, - "queue id (%u) should be less than %u", - act->rx_queue, - handle->kinfo.num_tqps); - return -EINVAL; - } - - rule->queue_id =3D act->rx_queue; - rule->action =3D HCLGE_FD_ACTION_SELECT_QUEUE; - return 0; - case FLOW_ACTION_DROP: - rule->action =3D HCLGE_FD_ACTION_DROP_PACKET; - return 0; - default: - NL_SET_ERR_MSG_FMT_MOD(extack, - "unsupported action(%d)", 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 =3D flow_cls_offload_flow_rule(cls_flower); - struct netlink_ext_ack *extack =3D cls_flower->common.extack; - int ret; - - /* not support any user def tuples */ - rule->unused_tuple |=3D 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 =3D hclge_get_cls_key_ip(flow, rule, extack); - if (ret) - return ret; - - hclge_get_cls_key_port(flow, rule); - ret =3D hclge_get_cls_key_ip_tos(flow, rule, extack); - if (ret) - return ret; - - return hclge_get_cls_enc_keyid(hdev, flow, rule, extack); -} - -static int hclge_check_cls_flower(struct hclge_dev *hdev, - struct flow_cls_offload *cls_flower) -{ - struct flow_rule *flow =3D flow_cls_offload_flow_rule(cls_flower); - struct netlink_ext_ack *extack =3D cls_flower->common.extack; - struct flow_dissector *dissector =3D flow->match.dissector; - u32 prio =3D cls_flower->common.prio; - u64 support_keys; - - if (prio =3D=3D 0 || - prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) { - NL_SET_ERR_MSG_FMT_MOD(extack, - "prio %u should be in range[1, %u]", - prio, - hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]); - return -EINVAL; - } - - if (test_bit(prio - 1, hdev->fd_bmap)) { - NL_SET_ERR_MSG_FMT_MOD(extack, - "prio %u is already used", prio); - return -EINVAL; - } - - support_keys =3D BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | - BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | - 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); - - if (hdev->fd_cfg.fd_mode =3D=3D HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) - support_keys |=3D BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID); - - if (dissector->used_keys & ~support_keys) { - NL_SET_ERR_MSG_FMT_MOD(extack, "unsupported key set: %#llx", - dissector->used_keys); - return -EOPNOTSUPP; - } - - /* driver will parses classid into an action */ - if (cls_flower->classid && flow_action_has_entries(&flow->action)) { - NL_SET_ERR_MSG_MOD(extack, - "cannot specify both classid and action"); - return -EOPNOTSUPP; - } - - if (!flow_action_has_entries(&flow->action) && !cls_flower->classid) { - NL_SET_ERR_MSG_MOD(extack, - "must specify either classid or action"); - return -EINVAL; - } - - if (flow_action_has_entries(&flow->action) && - !flow_offload_has_one_action(&flow->action)) { - NL_SET_ERR_MSG_MOD(extack, "unsupported multiple actions"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int hclge_add_cls_flower(struct hnae3_handle *handle, - struct flow_cls_offload *cls_flower) -{ - struct netlink_ext_ack *extack =3D cls_flower->common.extack; - struct hclge_vport *vport =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - struct hclge_fd_rule *rule; - int ret; - - if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) { - NL_SET_ERR_MSG_MOD(extack, "cls flower is not supported"); - return -EOPNOTSUPP; - } - - ret =3D hclge_check_cls_flower(hdev, cls_flower); - if (ret) - return ret; - - rule =3D kzalloc_obj(*rule); - if (!rule) - return -ENOMEM; - - ret =3D hclge_parse_cls_flower(hdev, cls_flower, rule); - if (ret) { - kfree(rule); - return ret; - } - - ret =3D hclge_get_tc_flower_action(hdev, cls_flower, rule); - if (ret) { - kfree(rule); - return ret; - } - - rule->location =3D cls_flower->common.prio - 1; - rule->vf_id =3D 0; - rule->cls_flower.cookie =3D cls_flower->cookie; - rule->rule_type =3D HCLGE_FD_TC_FLOWER_ACTIVE; - - ret =3D 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 =3D=3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D 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 =3D hclge_find_cls_flower(hdev, cls_flower->cookie); - if (!rule) { - spin_unlock_bh(&hdev->fd_rule_lock); - return -EINVAL; - } - - ret =3D 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 =3D 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 =3D hclge_fd_config_rule(hdev, rule); - if (ret) - goto out; - rule->state =3D HCLGE_FD_ACTIVE; - break; - case HCLGE_FD_TO_DEL: - ret =3D 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 =3D hdev->fd_active_type =3D=3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D 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 =3D hclge_get_vport(handle); - struct hclge_dev *hdev =3D vport->back; - - hdev->fd_en =3D 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 =3D - (struct hclge_config_mac_mode_cmd *)desc.data; - u32 loop_en =3D 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 =3D cpu_to_le32(loop_en); - - ret =3D hclge_cmd_send(&hdev->hw, &desc, 1); - if (ret) { - dev_err(&hdev->pdev->dev, - "mac enable fail, ret =3D%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/driv= ers/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 --=20 2.33.0