[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260109100146.63569-10-illusion.wang@nebula-matrix.com>
Date: Fri, 9 Jan 2026 18:01:27 +0800
From: "illusion.wang" <illusion.wang@...ula-matrix.com>
To: dimon.zhao@...ula-matrix.com,
illusion.wang@...ula-matrix.com,
alvin.wang@...ula-matrix.com,
sam.chen@...ula-matrix.com,
netdev@...r.kernel.org
Cc: andrew+netdev@...n.ch,
corbet@....net,
kuba@...nel.org,
linux-doc@...r.kernel.org,
lorenzo@...nel.org,
pabeni@...hat.com,
horms@...nel.org,
vadim.fedorenko@...ux.dev,
lukas.bulwahn@...hat.com,
edumazet@...gle.com,
linux-kernel@...r.kernel.org (open list)
Subject: [PATCH v2 net-next 09/15] net/nebula-matrix: add flow resource definitions and implementation
flow resource management functions include:
Flow Configuration: Setting the actions and key-value pairs for flows.
Flow Management: Allocating/releasing flow IDs, TCAM IDs, MCC IDs, etc.
Multicast Control: Managing multicast control groups.
Hash Table Management: Enabling rapid lookup of flow entries.
LLDP/LACP Flow Management: Managing flows related to link-layer
protocols.
Multicast Flow Management: Managing multicast flows.
MTU Management: Managing the MTU of Virtual Switching Instances (VSIs).
Initialization and Cleanup: Initializing/cleaning up the flow management
module.
Signed-off-by: illusion.wang <illusion.wang@...ula-matrix.com>
---
.../net/ethernet/nebula-matrix/nbl/Makefile | 1 +
.../nbl_hw/nbl_hw_leonis/nbl_flow_leonis.c | 2268 +++++++++++++++++
.../nbl_hw/nbl_hw_leonis/nbl_flow_leonis.h | 204 ++
.../nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c | 519 ++++
.../nbl_hw_leonis/nbl_resource_leonis.c | 10 +
.../nbl_hw_leonis/nbl_resource_leonis.h | 3 +
.../nbl/nbl_include/nbl_def_common.h | 87 +
.../nbl/nbl_include/nbl_def_hw.h | 18 +
8 files changed, 3110 insertions(+)
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.c
create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.h
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
index e611110ac369..16d751e01b8e 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
+++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_NBL_CORE) := nbl_core.o
nbl_core-objs += nbl_common/nbl_common.o \
nbl_channel/nbl_channel.o \
nbl_hw/nbl_hw_leonis/nbl_hw_leonis.o \
+ nbl_hw/nbl_hw_leonis/nbl_flow_leonis.o \
nbl_hw/nbl_hw_leonis/nbl_queue_leonis.o \
nbl_hw/nbl_hw_leonis/nbl_resource_leonis.o \
nbl_hw/nbl_hw_leonis/nbl_hw_leonis_regs.o \
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.c
new file mode 100644
index 000000000000..62681d64c3e0
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.c
@@ -0,0 +1,2268 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Nebula Matrix Limited.
+ * Author:
+ */
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+
+#include "nbl_flow_leonis.h"
+#include "nbl_resource_leonis.h"
+
+#define NBL_ACT_SET_AUX_FIELD 1
+#define NBL_ACT_SET_DPORT 9
+#define NBL_ACT_SET_MCC 13
+#define NBL_FLOW_LEONIS_VSI_NUM_PER_ETH 256
+
+static u32 nbl_flow_cfg_action_set_dport(u16 upcall_flag, u16 port_type,
+ u16 vsi, u16 next_stg_sel)
+{
+ union nbl_action_data set_dport = { .data = 0 };
+
+ set_dport.dport.up.upcall_flag = upcall_flag;
+ set_dport.dport.up.port_type = port_type;
+ set_dport.dport.up.port_id = vsi;
+ set_dport.dport.up.next_stg_sel = next_stg_sel;
+
+ return set_dport.data + (NBL_ACT_SET_DPORT << 16);
+}
+
+static u16 nbl_flow_cfg_action_set_dport_mcc_eth(u8 eth)
+{
+ union nbl_action_data set_dport = { .data = 0 };
+
+ set_dport.dport.down.upcall_flag = AUX_FWD_TYPE_NML_FWD;
+ set_dport.dport.down.port_type = SET_DPORT_TYPE_ETH_LAG;
+ set_dport.dport.down.next_stg_sel = NEXT_STG_SEL_EPRO;
+ set_dport.dport.down.lag_vld = 0;
+ set_dport.dport.down.eth_vld = 1;
+ set_dport.dport.down.eth_id = eth;
+
+ return set_dport.data;
+}
+
+static u16 nbl_flow_cfg_action_set_dport_mcc_vsi(u16 vsi)
+{
+ union nbl_action_data set_dport = { .data = 0 };
+
+ set_dport.dport.up.upcall_flag = AUX_FWD_TYPE_NML_FWD;
+ set_dport.dport.up.port_type = SET_DPORT_TYPE_VSI_HOST;
+ set_dport.dport.up.port_id = vsi;
+ set_dport.dport.up.next_stg_sel = NEXT_STG_SEL_ACL_S0;
+
+ return set_dport.data;
+}
+
+static u32 nbl_flow_cfg_action_set_dport_mcc_bmc(void)
+{
+ union nbl_action_data set_dport = { .data = 0 };
+
+ set_dport.dport.up.upcall_flag = AUX_FWD_TYPE_NML_FWD;
+ set_dport.dport.up.port_type = SET_DPORT_TYPE_SP_PORT;
+ set_dport.dport.up.port_id = NBL_FLOW_MCC_BMC_DPORT;
+ set_dport.dport.up.next_stg_sel = NEXT_STG_SEL_EPRO;
+
+ return set_dport.data + (NBL_ACT_SET_DPORT << 16);
+}
+
+static int nbl_flow_cfg_action_mcc(u16 mcc_id, u32 *action0, u32 *action1)
+{
+ union nbl_action_data mcc_idx_act = { .data = 0 },
+ set_aux_act = { .data = 0 };
+
+ mcc_idx_act.mcc_idx.mcc_id = mcc_id;
+ *action0 = (u32)mcc_idx_act.data + (NBL_ACT_SET_MCC << 16);
+
+ set_aux_act.set_aux.sub_id = NBL_SET_AUX_SET_AUX;
+ set_aux_act.set_aux.nstg_vld = 1;
+ set_aux_act.set_aux.nstg_val = NBL_NEXT_STG_MCC;
+ *action1 = (u32)set_aux_act.data + (NBL_ACT_SET_AUX_FIELD << 16);
+
+ return 0;
+}
+
+static int nbl_flow_cfg_action_up_tnl(struct nbl_flow_param param, u32 *action0,
+ u32 *action1)
+{
+ *action1 = 0;
+ if (param.mcc_id == NBL_MCC_ID_INVALID)
+ *action0 =
+ nbl_flow_cfg_action_set_dport(AUX_FWD_TYPE_NML_FWD,
+ SET_DPORT_TYPE_VSI_HOST,
+ param.vsi,
+ NEXT_STG_SEL_ACL_S0);
+ else
+ nbl_flow_cfg_action_mcc(param.mcc_id, action0, action1);
+
+ return 0;
+}
+
+static int nbl_flow_cfg_action_lldp_lacp_up(struct nbl_flow_param param,
+ u32 *action0, u32 *action1)
+{
+ *action1 = 0;
+ *action0 = nbl_flow_cfg_action_set_dport(AUX_FWD_TYPE_NML_FWD,
+ SET_DPORT_TYPE_VSI_HOST,
+ param.vsi,
+ NEXT_STG_SEL_ACL_S0);
+
+ return 0;
+}
+
+static int nbl_flow_cfg_action_up(struct nbl_flow_param param, u32 *action0,
+ u32 *action1)
+{
+ *action1 = 0;
+ if (param.mcc_id == NBL_MCC_ID_INVALID)
+ *action0 =
+ nbl_flow_cfg_action_set_dport(AUX_FWD_TYPE_NML_FWD,
+ SET_DPORT_TYPE_VSI_HOST,
+ param.vsi,
+ NEXT_STG_SEL_NONE);
+ else
+ nbl_flow_cfg_action_mcc(param.mcc_id, action0, action1);
+
+ return 0;
+}
+
+static int nbl_flow_cfg_action_down(struct nbl_flow_param param, u32 *action0,
+ u32 *action1)
+{
+ *action1 = 0;
+ if (param.mcc_id == NBL_MCC_ID_INVALID)
+ *action0 =
+ nbl_flow_cfg_action_set_dport(AUX_FWD_TYPE_NML_FWD,
+ SET_DPORT_TYPE_VSI_HOST,
+ param.vsi,
+ NEXT_STG_SEL_ACL_S0);
+ else
+ nbl_flow_cfg_action_mcc(param.mcc_id, action0, action1);
+
+ return 0;
+}
+
+static int nbl_flow_cfg_up_tnl_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param,
+ u8 eth_mode)
+{
+ union nbl_l2_hw_up_data_u *kt_data = (union nbl_l2_hw_up_data_u *)data;
+ u64 dst_mac = 0;
+ u8 sport;
+ u8 reverse_mac[ETH_ALEN];
+
+ nbl_convert_mac(param.mac, reverse_mac);
+
+ memset(kt_data->hash_key, 0x0, sizeof(kt_data->hash_key));
+ ether_addr_copy((u8 *)&dst_mac, reverse_mac);
+
+ kt_data->info.dst_mac = dst_mac;
+ kt_data->info.svlan_id = param.vid;
+ kt_data->info.template = NBL_EM0_PT_HW_UP_TUNNEL_L2;
+ kt_data->info.padding = 0;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+static int nbl_flow_cfg_lldp_lacp_up_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param,
+ u8 eth_mode)
+{
+ union nbl_l2_hw_lldp_lacp_data_u *kt_data =
+ (union nbl_l2_hw_lldp_lacp_data_u *)data;
+ u8 sport;
+
+ kt_data->info.template = NBL_EM0_PT_HW_UP_LLDP_LACP;
+
+ kt_data->info.ether_type = param.ether_type;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+static int nbl_flow_cfg_up_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param, u8 eth_mode)
+{
+ union nbl_l2_hw_up_data_u *kt_data = (union nbl_l2_hw_up_data_u *)data;
+ u64 dst_mac = 0;
+ u8 sport;
+ u8 reverse_mac[ETH_ALEN];
+
+ nbl_convert_mac(param.mac, reverse_mac);
+
+ memset(kt_data->hash_key, 0x0, sizeof(kt_data->hash_key));
+ ether_addr_copy((u8 *)&dst_mac, reverse_mac);
+
+ kt_data->info.dst_mac = dst_mac;
+ kt_data->info.svlan_id = param.vid;
+ kt_data->info.template = NBL_EM0_PT_HW_UP_L2;
+ kt_data->info.padding = 0;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+static int nbl_flow_cfg_down_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param, u8 eth_mode)
+{
+ union nbl_l2_hw_down_data_u *kt_data =
+ (union nbl_l2_hw_down_data_u *)data;
+ u64 dst_mac = 0;
+ u8 sport;
+ u8 reverse_mac[ETH_ALEN];
+
+ nbl_convert_mac(param.mac, reverse_mac);
+
+ memset(kt_data->hash_key, 0x0, sizeof(kt_data->hash_key));
+ ether_addr_copy((u8 *)&dst_mac, reverse_mac);
+
+ kt_data->info.dst_mac = dst_mac;
+ kt_data->info.svlan_id = param.vid;
+ kt_data->info.template = NBL_EM0_PT_HW_DOWN_L2;
+ kt_data->info.padding = 0;
+
+ sport = param.vsi >> 8;
+ if (eth_mode == NBL_TWO_ETHERNET_PORT)
+ sport &= 0xFE;
+ if (eth_mode == NBL_ONE_ETHERNET_PORT)
+ sport = 0;
+ kt_data->info.sport = sport;
+
+ return 0;
+}
+
+static void nbl_flow_cfg_kt_action_up_tnl(union nbl_common_data_u *data,
+ u32 action0, u32 action1)
+{
+ union nbl_l2_hw_up_data_u *kt_data = (union nbl_l2_hw_up_data_u *)data;
+
+ kt_data->info.act0 = action0;
+ kt_data->info.act1 = action1;
+}
+
+static void nbl_flow_cfg_kt_action_lldp_lacp_up(union nbl_common_data_u *data,
+ u32 action0, u32 action1)
+{
+ union nbl_l2_hw_lldp_lacp_data_u *kt_data =
+ (union nbl_l2_hw_lldp_lacp_data_u *)data;
+
+ kt_data->info.act0 = action0;
+}
+
+static void nbl_flow_cfg_kt_action_up(union nbl_common_data_u *data,
+ u32 action0, u32 action1)
+{
+ union nbl_l2_hw_up_data_u *kt_data = (union nbl_l2_hw_up_data_u *)data;
+
+ kt_data->info.act0 = action0;
+ kt_data->info.act1 = action1;
+}
+
+static void nbl_flow_cfg_kt_action_down(union nbl_common_data_u *data,
+ u32 action0, u32 action1)
+{
+ union nbl_l2_hw_down_data_u *kt_data =
+ (union nbl_l2_hw_down_data_u *)data;
+
+ kt_data->info.act0 = action0;
+ kt_data->info.act1 = action1;
+}
+
+static int nbl_flow_cfg_action_multi_mcast(struct nbl_flow_param param,
+ u32 *action0, u32 *action1)
+{
+ return nbl_flow_cfg_action_mcc(param.mcc_id, action0, action1);
+}
+
+static int
+nbl_flow_cfg_l2up_multi_mcast_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param,
+ u8 eth_mode)
+{
+ union nbl_l2_hw_up_multi_mcast_data_u *kt_data =
+ (union nbl_l2_hw_up_multi_mcast_data_u *)data;
+ u8 sport;
+
+ kt_data->info.template = NBL_EM0_PT_HW_L2_UP_MULTI_MCAST;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+static void
+nbl_flow_cfg_kt_action_l2up_multi_mcast(union nbl_common_data_u *data,
+ u32 action0, u32 action1)
+{
+ union nbl_l2_hw_up_multi_mcast_data_u *kt_data =
+ (union nbl_l2_hw_up_multi_mcast_data_u *)data;
+
+ kt_data->info.act0 = action0;
+}
+
+static int
+nbl_flow_cfg_l3up_multi_mcast_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param,
+ u8 eth_mode)
+{
+ union nbl_l2_hw_up_multi_mcast_data_u *kt_data =
+ (union nbl_l2_hw_up_multi_mcast_data_u *)data;
+ u8 sport;
+
+ kt_data->info.template = NBL_EM0_PT_HW_L3_UP_MULTI_MCAST;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+static int
+nbl_flow_cfg_l2down_multi_mcast_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param,
+ u8 eth_mode)
+{
+ union nbl_l2_hw_down_multi_mcast_data_u *kt_data =
+ (union nbl_l2_hw_down_multi_mcast_data_u *)data;
+ u8 sport;
+
+ kt_data->info.template = NBL_EM0_PT_HW_L2_DOWN_MULTI_MCAST;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+static void
+nbl_flow_cfg_kt_action_l2down_multi_mcast(union nbl_common_data_u *data,
+ u32 action0, u32 action1)
+{
+ union nbl_l2_hw_down_multi_mcast_data_u *kt_data =
+ (union nbl_l2_hw_down_multi_mcast_data_u *)data;
+
+ kt_data->info.act0 = action0;
+}
+
+static int
+nbl_flow_cfg_l3down_multi_mcast_key_value(union nbl_common_data_u *data,
+ struct nbl_flow_param param,
+ u8 eth_mode)
+{
+ union nbl_l2_hw_down_multi_mcast_data_u *kt_data =
+ (union nbl_l2_hw_down_multi_mcast_data_u *)data;
+ u8 sport;
+
+ kt_data->info.template = NBL_EM0_PT_HW_L3_DOWN_MULTI_MCAST;
+
+ sport = param.eth;
+ kt_data->info.sport = sport + NBL_SPORT_ETH_OFFSET;
+
+ return 0;
+}
+
+#define NBL_FLOW_OPS_ARR_ENTRY(type, action_func, kt_func, kt_action_func) \
+ [type] = {.cfg_action = action_func, .cfg_key = kt_func, \
+ .cfg_kt_action = kt_action_func}
+static const struct nbl_flow_rule_cfg_ops cfg_ops[] = {
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_UP_TNL,
+ nbl_flow_cfg_action_up_tnl,
+ nbl_flow_cfg_up_tnl_key_value,
+ nbl_flow_cfg_kt_action_up_tnl),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_UP,
+ nbl_flow_cfg_action_up,
+ nbl_flow_cfg_up_key_value,
+ nbl_flow_cfg_kt_action_up),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_DOWN,
+ nbl_flow_cfg_action_down,
+ nbl_flow_cfg_down_key_value,
+ nbl_flow_cfg_kt_action_down),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_LLDP_LACP_UP,
+ nbl_flow_cfg_action_lldp_lacp_up,
+ nbl_flow_cfg_lldp_lacp_up_key_value,
+ nbl_flow_cfg_kt_action_lldp_lacp_up),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_L2_UP_MULTI_MCAST,
+ nbl_flow_cfg_action_multi_mcast,
+ nbl_flow_cfg_l2up_multi_mcast_key_value,
+ nbl_flow_cfg_kt_action_l2up_multi_mcast),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_L3_UP_MULTI_MCAST,
+ nbl_flow_cfg_action_multi_mcast,
+ nbl_flow_cfg_l3up_multi_mcast_key_value,
+ nbl_flow_cfg_kt_action_l2up_multi_mcast),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_L2_DOWN_MULTI_MCAST,
+ nbl_flow_cfg_action_multi_mcast,
+ nbl_flow_cfg_l2down_multi_mcast_key_value,
+ nbl_flow_cfg_kt_action_l2down_multi_mcast),
+ NBL_FLOW_OPS_ARR_ENTRY(NBL_FLOW_L3_DOWN_MULTI_MCAST,
+ nbl_flow_cfg_action_multi_mcast,
+ nbl_flow_cfg_l3down_multi_mcast_key_value,
+ nbl_flow_cfg_kt_action_l2down_multi_mcast),
+};
+
+static int nbl_flow_alloc_flow_id(struct nbl_flow_mgt *flow_mgt,
+ struct nbl_flow_fem_entry *flow)
+{
+ u32 flow_id;
+
+ if (flow->flow_type == NBL_KT_HALF_MODE) {
+ flow_id = find_first_zero_bit(flow_mgt->flow_id_bitmap,
+ NBL_MACVLAN_TABLE_LEN);
+ if (flow_id == NBL_MACVLAN_TABLE_LEN)
+ return -ENOSPC;
+ set_bit(flow_id, flow_mgt->flow_id_bitmap);
+ flow_mgt->flow_id_cnt--;
+ } else {
+ flow_id = nbl_common_find_free_idx(flow_mgt->flow_id_bitmap,
+ NBL_MACVLAN_TABLE_LEN,
+ 2, 2);
+ if (flow_id == NBL_MACVLAN_TABLE_LEN)
+ return -ENOSPC;
+ set_bit(flow_id, flow_mgt->flow_id_bitmap);
+ set_bit(flow_id + 1, flow_mgt->flow_id_bitmap);
+ flow_mgt->flow_id_cnt -= 2;
+ }
+
+ flow->flow_id = flow_id;
+ return 0;
+}
+
+static void nbl_flow_free_flow_id(struct nbl_flow_mgt *flow_mgt,
+ struct nbl_flow_fem_entry *flow)
+{
+ if (flow->flow_id == U16_MAX)
+ return;
+
+ if (flow->flow_type == NBL_KT_HALF_MODE) {
+ clear_bit(flow->flow_id, flow_mgt->flow_id_bitmap);
+ flow->flow_id = 0xFFFF;
+ flow_mgt->flow_id_cnt++;
+ } else {
+ clear_bit(flow->flow_id, flow_mgt->flow_id_bitmap);
+ clear_bit(flow->flow_id + 1, flow_mgt->flow_id_bitmap);
+ flow->flow_id = 0xFFFF;
+ flow_mgt->flow_id_cnt += 2;
+ }
+}
+
+static int nbl_flow_alloc_tcam_id(struct nbl_flow_mgt *flow_mgt,
+ struct nbl_tcam_item *tcam_item)
+{
+ u32 tcam_id;
+
+ tcam_id = find_first_zero_bit(flow_mgt->tcam_id, NBL_TCAM_TABLE_LEN);
+ if (tcam_id == NBL_TCAM_TABLE_LEN)
+ return -ENOSPC;
+
+ set_bit(tcam_id, flow_mgt->tcam_id);
+ tcam_item->tcam_index = tcam_id;
+
+ return 0;
+}
+
+static void nbl_flow_free_tcam_id(struct nbl_flow_mgt *flow_mgt,
+ struct nbl_tcam_item *tcam_item)
+{
+ clear_bit(tcam_item->tcam_index, flow_mgt->tcam_id);
+ tcam_item->tcam_index = 0;
+}
+
+static int nbl_flow_alloc_mcc_id(struct nbl_flow_mgt *flow_mgt)
+{
+ u32 mcc_id;
+
+ mcc_id = find_first_zero_bit(flow_mgt->mcc_id_bitmap,
+ NBL_FLOW_MCC_INDEX_SIZE);
+ if (mcc_id == NBL_FLOW_MCC_INDEX_SIZE)
+ return -ENOSPC;
+
+ set_bit(mcc_id, flow_mgt->mcc_id_bitmap);
+
+ return mcc_id + NBL_FLOW_MCC_INDEX_START;
+}
+
+static void nbl_flow_free_mcc_id(struct nbl_flow_mgt *flow_mgt, u32 mcc_id)
+{
+ if (mcc_id >= NBL_FLOW_MCC_INDEX_START)
+ clear_bit(mcc_id - NBL_FLOW_MCC_INDEX_START,
+ flow_mgt->mcc_id_bitmap);
+}
+
+static void nbl_flow_set_mt_input(struct nbl_mt_input *mt_input,
+ union nbl_common_data_u *kt_data, u8 type,
+ u16 flow_id)
+{
+ int i;
+ u16 key_len;
+
+ key_len = ((type) == NBL_KT_HALF_MODE ? NBL_KT_BYTE_HALF_LEN :
+ NBL_KT_BYTE_LEN);
+ for (i = 0; i < key_len; i++)
+ mt_input->key[i] = kt_data->hash_key[key_len - 1 - i];
+
+ mt_input->tbl_id = flow_id + NBL_EM_HW_KT_OFFSET;
+ mt_input->depth = 0;
+ mt_input->power = NBL_PP0_POWER;
+}
+
+static void nbl_flow_key_hash(struct nbl_flow_fem_entry *flow,
+ struct nbl_mt_input *mt_input)
+{
+ u16 ht0_hash = 0;
+ u16 ht1_hash = 0;
+
+ ht0_hash = NBL_CRC16_CCITT(mt_input->key, NBL_KT_BYTE_LEN);
+ ht1_hash = NBL_CRC16_IBM(mt_input->key, NBL_KT_BYTE_LEN);
+ flow->ht0_hash =
+ nbl_hash_transfer(ht0_hash, mt_input->power, mt_input->depth);
+ flow->ht1_hash =
+ nbl_hash_transfer(ht1_hash, mt_input->power, mt_input->depth);
+}
+
+static bool nbl_pp_ht0_ht1_search(struct nbl_flow_ht_mng *pp_ht0_mng,
+ u16 ht0_hash,
+ struct nbl_flow_ht_mng *pp_ht1_mng,
+ u16 ht1_hash, struct nbl_common_info *common)
+{
+ struct nbl_flow_ht_tbl *node0 = NULL;
+ struct nbl_flow_ht_tbl *node1 = NULL;
+ u16 i = 0;
+ bool is_find = false;
+
+ node0 = pp_ht0_mng->hash_map[ht0_hash];
+ if (node0)
+ for (i = 0; i < NBL_HASH_CFT_MAX; i++)
+ if (node0->key[i].vid &&
+ node0->key[i].ht_other_index == ht1_hash) {
+ is_find = true;
+ nbl_debug(common,
+ "Conflicted ht on vid %d and kt_index %u\n",
+ node0->key[i].vid,
+ node0->key[i].kt_index);
+ return is_find;
+ }
+
+ node1 = pp_ht1_mng->hash_map[ht1_hash];
+ if (node1)
+ for (i = 0; i < NBL_HASH_CFT_MAX; i++)
+ if (node1->key[i].vid &&
+ node1->key[i].ht_other_index == ht0_hash) {
+ is_find = true;
+ nbl_debug(common,
+ "Conflicted ht on vid %d and kt_index %u\n",
+ node1->key[i].vid,
+ node1->key[i].kt_index);
+ return is_find;
+ }
+
+ return is_find;
+}
+
+static bool nbl_flow_check_ht_conflict(struct nbl_flow_ht_mng *pp_ht0_mng,
+ struct nbl_flow_ht_mng *pp_ht1_mng,
+ u16 ht0_hash, u16 ht1_hash,
+ struct nbl_common_info *common)
+{
+ return nbl_pp_ht0_ht1_search(pp_ht0_mng, ht0_hash, pp_ht1_mng, ht1_hash,
+ common);
+}
+
+static int nbl_flow_find_ht_avail_table(struct nbl_flow_ht_mng *pp_ht0_mng,
+ struct nbl_flow_ht_mng *pp_ht1_mng,
+ u16 ht0_hash, u16 ht1_hash)
+{
+ struct nbl_flow_ht_tbl *pp_ht0_node = NULL;
+ struct nbl_flow_ht_tbl *pp_ht1_node = NULL;
+
+ pp_ht0_node = pp_ht0_mng->hash_map[ht0_hash];
+ pp_ht1_node = pp_ht1_mng->hash_map[ht1_hash];
+
+ if (!pp_ht0_node && !pp_ht1_node) {
+ return 0;
+ } else if (pp_ht0_node && !pp_ht1_node) {
+ if (pp_ht0_node->ref_cnt >= NBL_HASH_CFT_AVL)
+ return 1;
+ else
+ return 0;
+ } else if (!pp_ht0_node && pp_ht1_node) {
+ if (pp_ht1_node->ref_cnt >= NBL_HASH_CFT_AVL)
+ return 0;
+ else
+ return 1;
+ } else {
+ if ((pp_ht0_node->ref_cnt <= NBL_HASH_CFT_AVL ||
+ (pp_ht0_node->ref_cnt > NBL_HASH_CFT_AVL &&
+ pp_ht0_node->ref_cnt < NBL_HASH_CFT_MAX &&
+ pp_ht1_node->ref_cnt > NBL_HASH_CFT_AVL)))
+ return 0;
+ else if (((pp_ht0_node->ref_cnt > NBL_HASH_CFT_AVL &&
+ pp_ht1_node->ref_cnt <= NBL_HASH_CFT_AVL) ||
+ (pp_ht0_node->ref_cnt == NBL_HASH_CFT_MAX &&
+ pp_ht1_node->ref_cnt > NBL_HASH_CFT_AVL &&
+ pp_ht1_node->ref_cnt < NBL_HASH_CFT_MAX)))
+ return 1;
+ else
+ return -1;
+ }
+}
+
+static int nbl_flow_insert_pp_ht(struct nbl_flow_ht_mng *pp_ht_mng, u16 hash,
+ u16 hash_other, u32 key_index)
+{
+ struct nbl_flow_ht_tbl *node;
+ int i;
+
+ node = pp_ht_mng->hash_map[hash];
+ if (!node) {
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOSPC;
+ pp_ht_mng->hash_map[hash] = node;
+ }
+
+ for (i = 0; i < NBL_HASH_CFT_MAX; i++) {
+ if (node->key[i].vid == 0) {
+ node->key[i].vid = 1;
+ node->key[i].ht_other_index = hash_other;
+ node->key[i].kt_index = key_index;
+ node->ref_cnt++;
+ break;
+ }
+ }
+
+ return i;
+}
+
+static void nbl_flow_add_ht(struct nbl_ht_item *ht_item,
+ struct nbl_flow_fem_entry *flow, u32 key_index,
+ struct nbl_flow_ht_mng *pp_ht_mng, u8 ht_table)
+{
+ u16 ht_hash;
+ u16 ht_other_hash;
+
+ ht_hash = ht_table == NBL_HT0 ? flow->ht0_hash : flow->ht1_hash;
+ ht_other_hash = ht_table == NBL_HT0 ? flow->ht1_hash : flow->ht0_hash;
+
+ ht_item->hash_bucket = nbl_flow_insert_pp_ht(pp_ht_mng, ht_hash,
+ ht_other_hash, key_index);
+ if (ht_item->hash_bucket < 0)
+ return;
+
+ ht_item->ht_table = ht_table;
+ ht_item->key_index = key_index;
+ ht_item->ht0_hash = flow->ht0_hash;
+ ht_item->ht1_hash = flow->ht1_hash;
+
+ flow->hash_bucket = ht_item->hash_bucket;
+ flow->hash_table = ht_item->ht_table;
+}
+
+static void nbl_flow_del_ht(struct nbl_ht_item *ht_item,
+ struct nbl_flow_fem_entry *flow,
+ struct nbl_flow_ht_mng *pp_ht_mng)
+{
+ struct nbl_flow_ht_tbl *pp_ht_node = NULL;
+ u16 ht_hash;
+ u16 ht_other_hash;
+ int i;
+
+ ht_hash = ht_item->ht_table == NBL_HT0 ? flow->ht0_hash :
+ flow->ht1_hash;
+ ht_other_hash = ht_item->ht_table == NBL_HT0 ? flow->ht1_hash :
+ flow->ht0_hash;
+
+ pp_ht_node = pp_ht_mng->hash_map[ht_hash];
+ if (!pp_ht_node)
+ return;
+
+ for (i = 0; i < NBL_HASH_CFT_MAX; i++) {
+ if (pp_ht_node->key[i].vid == 1 &&
+ pp_ht_node->key[i].ht_other_index == ht_other_hash) {
+ memset(&pp_ht_node->key[i], 0,
+ sizeof(pp_ht_node->key[i]));
+ pp_ht_node->ref_cnt--;
+ break;
+ }
+ }
+
+ if (!pp_ht_node->ref_cnt) {
+ kfree(pp_ht_node);
+ pp_ht_mng->hash_map[ht_hash] = NULL;
+ }
+}
+
+static int nbl_flow_send_2hw(struct nbl_resource_mgt *res_mgt,
+ struct nbl_ht_item ht_item,
+ struct nbl_kt_item kt_item, u8 key_type)
+{
+ struct nbl_hw_ops *hw_ops;
+ u16 hash, hash_other;
+ int ret = 0;
+
+ hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+
+ ret = hw_ops->set_kt(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ kt_item.kt_data.hash_key, ht_item.key_index,
+ key_type);
+ if (ret)
+ goto set_kt_fail;
+
+ hash = ht_item.ht_table == NBL_HT0 ? ht_item.ht0_hash :
+ ht_item.ht1_hash;
+ hash_other = ht_item.ht_table == NBL_HT0 ? ht_item.ht1_hash :
+ ht_item.ht0_hash;
+ ret = hw_ops->set_ht(NBL_RES_MGT_TO_HW_PRIV(res_mgt), hash, hash_other,
+ ht_item.ht_table, ht_item.hash_bucket,
+ ht_item.key_index, 1);
+ if (ret)
+ goto set_ht_fail;
+
+ ret = hw_ops->search_key(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ kt_item.kt_data.hash_key, key_type);
+ if (ret)
+ goto search_fail;
+
+ return 0;
+
+search_fail:
+ ret = hw_ops->set_ht(NBL_RES_MGT_TO_HW_PRIV(res_mgt), hash, 0,
+ ht_item.ht_table, ht_item.hash_bucket, 0, 0);
+set_ht_fail:
+ memset(kt_item.kt_data.hash_key, 0, sizeof(kt_item.kt_data.hash_key));
+ hw_ops->set_kt(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ kt_item.kt_data.hash_key, ht_item.key_index, key_type);
+set_kt_fail:
+ return ret;
+}
+
+static int nbl_flow_del_2hw(struct nbl_resource_mgt *res_mgt,
+ struct nbl_ht_item ht_item,
+ struct nbl_kt_item kt_item, u8 key_type)
+{
+ struct nbl_hw_ops *hw_ops;
+ u16 hash;
+
+ hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+
+ hash = ht_item.ht_table == NBL_HT0 ? ht_item.ht0_hash :
+ ht_item.ht1_hash;
+ hw_ops->set_ht(NBL_RES_MGT_TO_HW_PRIV(res_mgt), hash, 0,
+ ht_item.ht_table, ht_item.hash_bucket, 0, 0);
+
+ return 0;
+}
+
+static void nbl_flow_cfg_tcam(struct nbl_tcam_item *tcam_item,
+ struct nbl_ht_item *ht_item,
+ struct nbl_kt_item *kt_item, u32 action0,
+ u32 action1)
+{
+ tcam_item->key_mode = NBL_KT_HALF_MODE;
+ tcam_item->pp_type = NBL_PT_PP0;
+ tcam_item->tcam_action[0] = action0;
+ tcam_item->tcam_action[1] = action1;
+ memcpy(&tcam_item->ht_item, ht_item, sizeof(struct nbl_ht_item));
+ memcpy(&tcam_item->kt_item, kt_item, sizeof(struct nbl_kt_item));
+}
+
+static int nbl_flow_add_tcam(struct nbl_resource_mgt *res_mgt,
+ struct nbl_tcam_item tcam_item)
+{
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+
+ return hw_ops->add_tcam(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ tcam_item.tcam_index,
+ tcam_item.kt_item.kt_data.hash_key,
+ tcam_item.tcam_action, tcam_item.key_mode,
+ NBL_PT_PP0);
+}
+
+static void nbl_flow_del_tcam(struct nbl_resource_mgt *res_mgt,
+ struct nbl_tcam_item tcam_item)
+{
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+
+ hw_ops->del_tcam(NBL_RES_MGT_TO_HW_PRIV(res_mgt), tcam_item.tcam_index,
+ tcam_item.key_mode, NBL_PT_PP0);
+}
+
+static int nbl_flow_add_flow(struct nbl_resource_mgt *res_mgt,
+ struct nbl_flow_param param, s32 type,
+ struct nbl_flow_fem_entry *flow)
+{
+ struct nbl_flow_mgt *flow_mgt;
+ struct nbl_common_info *common;
+ struct nbl_mt_input mt_input;
+ struct nbl_ht_item ht_item;
+ struct nbl_kt_item kt_item;
+ struct nbl_tcam_item *tcam_item = NULL;
+ struct nbl_flow_ht_mng *pp_ht_mng = NULL;
+ u32 action0, action1;
+ int ht_table;
+ int ret = 0;
+
+ memset(&mt_input, 0, sizeof(mt_input));
+ memset(&ht_item, 0, sizeof(ht_item));
+ memset(&kt_item, 0, sizeof(kt_item));
+
+ tcam_item = kzalloc(sizeof(*tcam_item), GFP_ATOMIC);
+ if (!tcam_item)
+ return -ENOMEM;
+
+ flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ common = NBL_RES_MGT_TO_COMMON(res_mgt);
+
+ flow->flow_type = param.type;
+ flow->type = type;
+ flow->flow_id = 0xFFFF;
+
+ ret = nbl_flow_alloc_flow_id(flow_mgt, flow);
+ if (ret)
+ goto free_mem;
+
+ ret = cfg_ops[type].cfg_action(param, &action0, &action1);
+ if (ret)
+ goto free_mem;
+
+ ret = cfg_ops[type].cfg_key(&kt_item.kt_data, param,
+ NBL_COMMON_TO_ETH_MODE(common));
+ if (ret)
+ goto free_mem;
+
+ nbl_flow_set_mt_input(&mt_input, &kt_item.kt_data, param.type,
+ flow->flow_id);
+ nbl_flow_key_hash(flow, &mt_input);
+
+ if (nbl_flow_check_ht_conflict(&flow_mgt->pp0_ht0_mng,
+ &flow_mgt->pp0_ht1_mng, flow->ht0_hash,
+ flow->ht1_hash, common))
+ flow->tcam_flag = true;
+
+ ht_table = nbl_flow_find_ht_avail_table(&flow_mgt->pp0_ht0_mng,
+ &flow_mgt->pp0_ht1_mng,
+ flow->ht0_hash, flow->ht1_hash);
+ if (ht_table < 0)
+ flow->tcam_flag = true;
+
+ if (!flow->tcam_flag) {
+ pp_ht_mng = ht_table == NBL_HT0 ? &flow_mgt->pp0_ht0_mng :
+ &flow_mgt->pp0_ht1_mng;
+ nbl_flow_add_ht(&ht_item, flow, mt_input.tbl_id, pp_ht_mng,
+ ht_table);
+
+ cfg_ops[type].cfg_kt_action(&kt_item.kt_data, action0, action1);
+ ret = nbl_flow_send_2hw(res_mgt, ht_item, kt_item, param.type);
+ } else {
+ ret = nbl_flow_alloc_tcam_id(flow_mgt, tcam_item);
+ if (ret)
+ goto out;
+
+ nbl_flow_cfg_tcam(tcam_item, &ht_item, &kt_item, action0,
+ action1);
+ flow->tcam_index = tcam_item->tcam_index;
+
+ ret = nbl_flow_add_tcam(res_mgt, *tcam_item);
+ }
+
+out:
+ if (ret) {
+ if (flow->tcam_flag)
+ nbl_flow_free_tcam_id(flow_mgt, tcam_item);
+ else
+ nbl_flow_del_ht(&ht_item, flow, pp_ht_mng);
+
+ nbl_flow_free_flow_id(flow_mgt, flow);
+ }
+
+free_mem:
+ kfree(tcam_item);
+
+ return ret;
+}
+
+static void nbl_flow_del_flow(struct nbl_resource_mgt *res_mgt,
+ struct nbl_flow_fem_entry *flow)
+{
+ struct nbl_flow_mgt *flow_mgt;
+ struct nbl_ht_item ht_item;
+ struct nbl_kt_item kt_item;
+ struct nbl_tcam_item tcam_item;
+ struct nbl_flow_ht_mng *pp_ht_mng = NULL;
+
+ if (flow->flow_id == 0xFFFF)
+ return;
+
+ memset(&ht_item, 0, sizeof(ht_item));
+ memset(&kt_item, 0, sizeof(kt_item));
+ memset(&tcam_item, 0, sizeof(tcam_item));
+
+ flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+
+ if (!flow->tcam_flag) {
+ ht_item.ht_table = flow->hash_table;
+ ht_item.ht0_hash = flow->ht0_hash;
+ ht_item.ht1_hash = flow->ht1_hash;
+ ht_item.hash_bucket = flow->hash_bucket;
+
+ pp_ht_mng = flow->hash_table == NBL_HT0 ?
+ &flow_mgt->pp0_ht0_mng :
+ &flow_mgt->pp0_ht1_mng;
+
+ nbl_flow_del_ht(&ht_item, flow, pp_ht_mng);
+ nbl_flow_del_2hw(res_mgt, ht_item, kt_item, flow->flow_type);
+ } else {
+ tcam_item.tcam_index = flow->tcam_index;
+ nbl_flow_del_tcam(res_mgt, tcam_item);
+ nbl_flow_free_tcam_id(flow_mgt, &tcam_item);
+ }
+
+ nbl_flow_free_flow_id(flow_mgt, flow);
+}
+
+static struct nbl_flow_mcc_node *
+nbl_flow_alloc_mcc_node(struct nbl_flow_mgt *flow_mgt, u8 type, u16 data,
+ u16 head)
+{
+ struct nbl_flow_mcc_node *node;
+ int mcc_id;
+ u16 mcc_action;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return NULL;
+
+ mcc_id = nbl_flow_alloc_mcc_id(flow_mgt);
+ if (mcc_id < 0) {
+ kfree(node);
+ return NULL;
+ }
+
+ switch (type) {
+ case NBL_MCC_INDEX_ETH:
+ mcc_action = nbl_flow_cfg_action_set_dport_mcc_eth((u8)data);
+ break;
+ case NBL_MCC_INDEX_VSI:
+ mcc_action = nbl_flow_cfg_action_set_dport_mcc_vsi(data);
+ break;
+ case NBL_MCC_INDEX_BMC:
+ mcc_action = nbl_flow_cfg_action_set_dport_mcc_bmc();
+ break;
+ default:
+ nbl_flow_free_mcc_id(flow_mgt, mcc_id);
+ kfree(node);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&node->node);
+ node->mcc_id = mcc_id;
+ node->mcc_head = head;
+ node->type = type;
+ node->data = data;
+ node->mcc_action = mcc_action;
+
+ return node;
+}
+
+static void nbl_flow_free_mcc_node(struct nbl_flow_mgt *flow_mgt,
+ struct nbl_flow_mcc_node *node)
+{
+ nbl_flow_free_mcc_id(flow_mgt, node->mcc_id);
+ kfree(node);
+}
+
+/* not consider multicast node first change, need modify all macvlan mcc */
+static int nbl_flow_add_mcc_node(struct nbl_resource_mgt *res_mgt,
+ struct nbl_flow_mcc_node *mcc_node,
+ struct list_head *head, struct list_head *list,
+ struct list_head *suffix)
+{
+ struct nbl_flow_mcc_node *mcc_head = NULL;
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+ u16 prev_mcc_id, next_mcc_id = NBL_MCC_ID_INVALID;
+ int ret = 0;
+
+ /* mcc_head must init before mcc_list */
+ if (mcc_node->mcc_head) {
+ list_add_tail(&mcc_node->node, head);
+ prev_mcc_id = NBL_MCC_ID_INVALID;
+
+ WARN_ON(!list_empty(list));
+ ret = hw_ops->add_mcc(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ mcc_node->mcc_id, prev_mcc_id,
+ NBL_MCC_ID_INVALID, mcc_node->mcc_action);
+ goto check_ret;
+ }
+
+ list_add_tail(&mcc_node->node, list);
+
+ if (list_is_first(&mcc_node->node, list))
+ prev_mcc_id = NBL_MCC_ID_INVALID;
+ else
+ prev_mcc_id = list_prev_entry(mcc_node, node)->mcc_id;
+
+ /* not head, next mcc may point suffix */
+ if (suffix && !list_empty(suffix))
+ next_mcc_id =
+ list_first_entry(suffix, struct nbl_flow_mcc_node, node)
+ ->mcc_id;
+ else
+ next_mcc_id = NBL_MCC_ID_INVALID;
+
+ /* first add mcc_list */
+ if (prev_mcc_id == NBL_MCC_ID_INVALID && !list_empty(head)) {
+ list_for_each_entry(mcc_head, head, node) {
+ prev_mcc_id = mcc_head->mcc_id;
+ ret |= hw_ops->add_mcc(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ mcc_node->mcc_id, prev_mcc_id,
+ next_mcc_id,
+ mcc_node->mcc_action);
+ }
+ goto check_ret;
+ }
+
+ ret = hw_ops->add_mcc(NBL_RES_MGT_TO_HW_PRIV(res_mgt), mcc_node->mcc_id,
+ prev_mcc_id, next_mcc_id, mcc_node->mcc_action);
+check_ret:
+ if (ret) {
+ list_del(&mcc_node->node);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* not consider multicast node first change, need modify all macvlan mcc */
+static void nbl_flow_del_mcc_node(struct nbl_resource_mgt *res_mgt,
+ struct nbl_flow_mcc_node *mcc_node,
+ struct list_head *head,
+ struct list_head *list,
+ struct list_head *suffix)
+{
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+ struct nbl_flow_mcc_node *mcc_head = NULL;
+ u16 prev_mcc_id, next_mcc_id;
+
+ if (list_entry_is_head(mcc_node, head, node) ||
+ list_entry_is_head(mcc_node, list, node))
+ return;
+
+ if (mcc_node->mcc_head) {
+ WARN_ON(!list_empty(list));
+ prev_mcc_id = NBL_MCC_ID_INVALID;
+ next_mcc_id = NBL_MCC_ID_INVALID;
+ hw_ops->del_mcc(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ mcc_node->mcc_id, prev_mcc_id, next_mcc_id);
+ goto free_node;
+ }
+
+ if (list_is_first(&mcc_node->node, list))
+ prev_mcc_id = NBL_MCC_ID_INVALID;
+ else
+ prev_mcc_id = list_prev_entry(mcc_node, node)->mcc_id;
+
+ if (list_is_last(&mcc_node->node, list))
+ next_mcc_id = NBL_MCC_ID_INVALID;
+ else
+ next_mcc_id = list_next_entry(mcc_node, node)->mcc_id;
+
+ /* not head, next mcc may point suffix */
+ if (next_mcc_id == NBL_MCC_ID_INVALID && suffix && !list_empty(suffix))
+ next_mcc_id =
+ list_first_entry(suffix, struct nbl_flow_mcc_node, node)
+ ->mcc_id;
+
+ if (prev_mcc_id == NBL_MCC_ID_INVALID && !list_empty(head)) {
+ list_for_each_entry(mcc_head, head, node) {
+ prev_mcc_id = mcc_head->mcc_id;
+ hw_ops->del_mcc(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ mcc_node->mcc_id, prev_mcc_id,
+ next_mcc_id);
+ }
+ goto free_node;
+ }
+
+ hw_ops->del_mcc(NBL_RES_MGT_TO_HW_PRIV(res_mgt), mcc_node->mcc_id,
+ prev_mcc_id, next_mcc_id);
+free_node:
+ list_del(&mcc_node->node);
+}
+
+static struct nbl_flow_mcc_group *
+nbl_flow_alloc_mcc_group(struct nbl_resource_mgt *res_mgt,
+ unsigned long *vsi_bitmap, u16 eth_id, bool multi,
+ u16 vsi_num)
+{
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *res = &flow_mgt->switch_res[eth_id];
+ struct nbl_flow_mcc_group *group;
+ struct nbl_flow_mcc_node *mcc_node, *mcc_node_safe;
+ int ret;
+ int bit;
+
+ /* The structure for mc macvlan list is:
+ *
+ * macvlan up
+ * |
+ * |
+ * BMC -> |
+ * VSI 0 -> VSI 1 -> -> allmulti list
+ * ETH -> |
+ * |
+ * |
+ * macvlan down
+ *
+ * So that the up mc pkts will be send to BMC, not need broadcast to
+ *eth, but the down mc pkts will send to eth, not send to BMC.
+ * Per mac flow entry has independent bmc/eth mcc nodes.
+ * All mac flow entry share all allmuti vsi nodes.
+ */
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return NULL;
+
+ group->vsi_base = eth_id * NBL_FLOW_LEONIS_VSI_NUM_PER_ETH;
+ group->multi = multi;
+ group->nbits = flow_mgt->vsi_max_per_switch;
+ group->ref_cnt = 1;
+ group->vsi_num = vsi_num;
+
+ INIT_LIST_HEAD(&group->group_node);
+ INIT_LIST_HEAD(&group->mcc_node);
+ INIT_LIST_HEAD(&group->mcc_head);
+
+ group->vsi_bitmap = kcalloc(BITS_TO_LONGS(flow_mgt->vsi_max_per_switch),
+ sizeof(long), GFP_KERNEL);
+ if (!group->vsi_bitmap)
+ goto alloc_vsi_bitmap_failed;
+
+ bitmap_copy(group->vsi_bitmap, vsi_bitmap,
+ flow_mgt->vsi_max_per_switch);
+ if (!multi)
+ goto add_mcc_node;
+
+ mcc_node =
+ nbl_flow_alloc_mcc_node(flow_mgt, NBL_MCC_INDEX_ETH, eth_id, 1);
+ if (!mcc_node)
+ goto free_nodes;
+
+ ret = nbl_flow_add_mcc_node(res_mgt, mcc_node, &group->mcc_head,
+ &group->mcc_node, NULL);
+ if (ret) {
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ goto free_nodes;
+ }
+
+ group->down_mcc_id = mcc_node->mcc_id;
+ mcc_node = nbl_flow_alloc_mcc_node(flow_mgt, NBL_MCC_INDEX_BMC,
+ NBL_FLOW_MCC_BMC_DPORT, 1);
+ if (!mcc_node)
+ goto free_nodes;
+
+ ret = nbl_flow_add_mcc_node(res_mgt, mcc_node, &group->mcc_head,
+ &group->mcc_node, NULL);
+ if (ret) {
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ goto free_nodes;
+ }
+ group->up_mcc_id = mcc_node->mcc_id;
+
+add_mcc_node:
+ for_each_set_bit(bit, vsi_bitmap, flow_mgt->vsi_max_per_switch) {
+ mcc_node = nbl_flow_alloc_mcc_node(flow_mgt, NBL_MCC_INDEX_VSI,
+ bit + group->vsi_base, 0);
+ if (!mcc_node)
+ goto free_nodes;
+
+ if (multi)
+ ret = nbl_flow_add_mcc_node(res_mgt, mcc_node,
+ &group->mcc_head,
+ &group->mcc_node,
+ &res->allmulti_list);
+ else
+ ret = nbl_flow_add_mcc_node(res_mgt, mcc_node,
+ &group->mcc_head,
+ &group->mcc_node, NULL);
+
+ if (ret) {
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ goto free_nodes;
+ }
+ }
+
+ if (list_empty(&group->mcc_head)) {
+ group->down_mcc_id = list_first_entry(&group->mcc_node,
+ struct nbl_flow_mcc_node,
+ node)
+ ->mcc_id;
+ group->up_mcc_id = list_first_entry(&group->mcc_node,
+ struct nbl_flow_mcc_node,
+ node)
+ ->mcc_id;
+ }
+ list_add_tail(&group->group_node, &res->mcc_group_head);
+
+ return group;
+
+free_nodes:
+ list_for_each_entry_safe(mcc_node, mcc_node_safe, &group->mcc_node,
+ node) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node, &group->mcc_head,
+ &group->mcc_node, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ }
+
+ list_for_each_entry_safe(mcc_node, mcc_node_safe, &group->mcc_head,
+ node) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node, &group->mcc_head,
+ &group->mcc_node, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ }
+ kfree(group->vsi_bitmap);
+alloc_vsi_bitmap_failed:
+ kfree(group);
+
+ return NULL;
+}
+
+static void nbl_flow_free_mcc_group(struct nbl_resource_mgt *res_mgt,
+ struct nbl_flow_mcc_group *group)
+{
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_mcc_node *mcc_node, *mcc_node_safe;
+
+ group->ref_cnt--;
+ if (group->ref_cnt)
+ return;
+
+ list_del(&group->group_node);
+ list_for_each_entry_safe(mcc_node, mcc_node_safe, &group->mcc_node,
+ node) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node, &group->mcc_head,
+ &group->mcc_node, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ }
+
+ list_for_each_entry_safe(mcc_node, mcc_node_safe, &group->mcc_head,
+ node) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node, &group->mcc_head,
+ &group->mcc_node, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ }
+
+ kfree(group->vsi_bitmap);
+ kfree(group);
+}
+
+static struct nbl_flow_mcc_group *
+nbl_find_same_mcc_group(struct nbl_flow_switch_res *res,
+ unsigned long *vsi_bitmap, bool multi)
+{
+ struct nbl_flow_mcc_group *group = NULL;
+
+ list_for_each_entry(group, &res->mcc_group_head, group_node)
+ if (group->multi == multi &&
+ __bitmap_equal(group->vsi_bitmap, vsi_bitmap,
+ group->nbits)) {
+ group->ref_cnt++;
+ return group;
+ }
+
+ return NULL;
+}
+
+static void nbl_flow_macvlan_node_del_action_func(void *priv, void *x_key,
+ void *y_key, void *data)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_l2_data *rule_data = (struct nbl_flow_l2_data *)data;
+ int i;
+
+ for (i = 0; i < NBL_FLOW_MACVLAN_MAX; i++) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+ nbl_flow_del_flow(res_mgt, &rule_data->entry[i]);
+ }
+
+ /* delete mcc */
+ if (rule_data->mcast_flow)
+ nbl_flow_free_mcc_group(res_mgt, rule_data->mcc_group);
+}
+
+static u32 nbl_flow_get_reserve_macvlan_cnt(struct nbl_resource_mgt *res_mgt)
+{
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_eth_info *eth_info = NBL_RES_MGT_TO_ETH_INFO(res_mgt);
+ struct nbl_flow_switch_res *res;
+ int i;
+ u32 reserve_cnt = 0;
+
+ for_each_set_bit(i, eth_info->eth_bitmap, NBL_MAX_ETHERNET) {
+ res = &flow_mgt->switch_res[i];
+ if (res->num_vfs)
+ reserve_cnt += (res->num_vfs - res->active_vfs) * 3;
+ }
+
+ return reserve_cnt;
+}
+
+static int nbl_flow_macvlan_node_vsi_match_func(void *condition, void *x_key,
+ void *y_key, void *data)
+{
+ u16 vsi = *(u16 *)condition;
+ struct nbl_flow_l2_data *rule_data = (struct nbl_flow_l2_data *)data;
+
+ if (!rule_data->mcast_flow)
+ return rule_data->vsi == vsi ? 0 : -1;
+ else
+ return !test_bit(vsi - rule_data->mcc_group->vsi_base,
+ rule_data->mcc_group->vsi_bitmap);
+}
+
+static void nbl_flow_macvlan_node_found_vsi_action(void *priv, void *x_key,
+ void *y_key, void *data)
+{
+ bool *match = (bool *)(priv);
+
+ *match = 1;
+}
+
+static int nbl_flow_add_macvlan(void *priv, u8 *mac, u16 vlan, u16 vsi)
+{
+ struct nbl_hash_xy_tbl_scan_key scan_key;
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *res;
+ struct nbl_flow_l2_data *rule_data;
+ struct nbl_flow_mcc_group *mcc_group = NULL, *pend_group = NULL;
+ unsigned long *vsi_bitmap;
+ struct nbl_flow_param param = { 0 };
+ void *tbl;
+ int i;
+ int ret = 0;
+ int pf_id, vf_id;
+ u32 reserve_cnt;
+ u16 eth_id;
+ u16 vsi_base;
+ u16 vsi_num = 0;
+ u16 func_id;
+ bool alloc_rule = 0;
+ bool need_mcast = 0;
+ bool vsi_match = 0;
+
+ eth_id = nbl_res_vsi_id_to_eth_id(res_mgt, vsi);
+ res = &flow_mgt->switch_res[eth_id];
+
+ func_id = nbl_res_vsi_id_to_func_id(res_mgt, vsi);
+ nbl_res_func_id_to_pfvfid(res_mgt, func_id, &pf_id, &vf_id);
+ reserve_cnt = nbl_flow_get_reserve_macvlan_cnt(res_mgt);
+
+ if (flow_mgt->flow_id_cnt <= reserve_cnt &&
+ (vf_id == U32_MAX || test_bit(vf_id, res->vf_bitmap)))
+ return -ENOSPC;
+
+ vsi_bitmap = kcalloc(BITS_TO_LONGS(flow_mgt->vsi_max_per_switch),
+ sizeof(long), GFP_KERNEL);
+ if (!vsi_bitmap)
+ return -ENOMEM;
+
+ NBL_HASH_XY_TBL_SCAN_KEY_INIT(&scan_key,
+ NBL_HASH_TBL_OP_SHOW,
+ NBL_HASH_TBL_ALL_SCAN,
+ false, NULL, NULL, &vsi,
+ &nbl_flow_macvlan_node_vsi_match_func,
+ &vsi_match,
+ &nbl_flow_macvlan_node_found_vsi_action);
+
+ param.mac = mac;
+ param.vid = vlan;
+ param.eth = eth_id;
+ param.vsi = vsi;
+ param.mcc_id = NBL_MCC_ID_INVALID;
+
+ vsi_base = eth_id * NBL_FLOW_LEONIS_VSI_NUM_PER_ETH;
+ tbl = res->mac_hash_tbl;
+ rule_data =
+ (struct nbl_flow_l2_data *)nbl_common_get_hash_xy_node(tbl,
+ mac,
+ &vlan);
+ if (rule_data) {
+ if (rule_data->mcast_flow &&
+ test_bit(vsi - rule_data->mcc_group->vsi_base,
+ rule_data->mcc_group->vsi_bitmap))
+ goto success;
+ else if (!rule_data->mcast_flow && rule_data->vsi == vsi)
+ goto success;
+
+ if (!rule_data->mcast_flow) {
+ vsi_num = 1;
+ set_bit(rule_data->vsi - vsi_base, vsi_bitmap);
+ } else {
+ vsi_num = rule_data->mcc_group->vsi_num;
+ bitmap_copy(vsi_bitmap,
+ rule_data->mcc_group->vsi_bitmap,
+ flow_mgt->vsi_max_per_switch);
+ }
+ need_mcast = 1;
+
+ } else {
+ rule_data = kzalloc(sizeof(*rule_data), GFP_KERNEL);
+ if (!rule_data) {
+ ret = -ENOMEM;
+ goto alloc_rule_failed;
+ }
+ alloc_rule = 1;
+ rule_data->multi = is_multicast_ether_addr(mac);
+ rule_data->mcast_flow = 0;
+ }
+
+ if (rule_data->multi)
+ need_mcast = 1;
+
+ if (need_mcast) {
+ set_bit(vsi - vsi_base, vsi_bitmap);
+ vsi_num++;
+ mcc_group = nbl_find_same_mcc_group(res, vsi_bitmap,
+ rule_data->multi);
+ if (!mcc_group) {
+ mcc_group = nbl_flow_alloc_mcc_group(res_mgt,
+ vsi_bitmap, eth_id,
+ rule_data->multi,
+ vsi_num);
+ if (!mcc_group) {
+ ret = -ENOMEM;
+ goto alloc_mcc_group_failed;
+ }
+ }
+ if (rule_data->mcast_flow)
+ pend_group = rule_data->mcc_group;
+ } else {
+ rule_data->vsi = vsi;
+ }
+
+ if (!alloc_rule) {
+ for (i = 0; i < NBL_FLOW_MACVLAN_MAX; i++) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+
+ nbl_flow_del_flow(res_mgt, &rule_data->entry[i]);
+ }
+
+ if (pend_group)
+ nbl_flow_free_mcc_group(res_mgt, pend_group);
+ }
+
+ for (i = 0; i < NBL_FLOW_MACVLAN_MAX; i++) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+ if (mcc_group) {
+ if (i <= NBL_FLOW_UP)
+ param.mcc_id = mcc_group->up_mcc_id;
+ else
+ param.mcc_id = mcc_group->down_mcc_id;
+ }
+ ret = nbl_flow_add_flow(res_mgt, param, i,
+ &rule_data->entry[i]);
+ if (ret)
+ goto add_flow_failed;
+ }
+
+ if (mcc_group) {
+ rule_data->mcast_flow = 1;
+ rule_data->mcc_group = mcc_group;
+ } else {
+ rule_data->mcast_flow = 0;
+ rule_data->vsi = vsi;
+ }
+
+ if (alloc_rule) {
+ ret = nbl_common_alloc_hash_xy_node(res->mac_hash_tbl, mac,
+ &vlan, rule_data);
+ if (ret)
+ goto add_flow_failed;
+ }
+
+ if (alloc_rule)
+ kfree(rule_data);
+success:
+ kfree(vsi_bitmap);
+
+ if (vf_id != U32_MAX && !test_bit(vf_id, res->vf_bitmap)) {
+ set_bit(vf_id, res->vf_bitmap);
+ res->active_vfs++;
+ }
+
+ return 0;
+
+add_flow_failed:
+ while (--i + 1) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+ nbl_flow_del_flow(res_mgt, &rule_data->entry[i]);
+ }
+ if (!alloc_rule)
+ nbl_common_free_hash_xy_node(res->mac_hash_tbl, mac, &vlan);
+ if (mcc_group)
+ nbl_flow_free_mcc_group(res_mgt, mcc_group);
+alloc_mcc_group_failed:
+ if (alloc_rule)
+ kfree(rule_data);
+alloc_rule_failed:
+ kfree(vsi_bitmap);
+
+ nbl_common_scan_hash_xy_node(res->mac_hash_tbl, &scan_key);
+ if (vf_id != U32_MAX && test_bit(vf_id, res->vf_bitmap) && !vsi_match) {
+ clear_bit(vf_id, res->vf_bitmap);
+ res->active_vfs--;
+ }
+
+ return ret;
+}
+
+static void nbl_flow_del_macvlan(void *priv, u8 *mac, u16 vlan, u16 vsi)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_mcc_group *mcc_group = NULL, *pend_group = NULL;
+ unsigned long *vsi_bitmap;
+ struct nbl_flow_switch_res *res;
+ struct nbl_flow_l2_data *rule_data;
+ struct nbl_flow_param param = { 0 };
+ struct nbl_hash_xy_tbl_scan_key scan_key;
+ int i;
+ int ret;
+ int pf_id, vf_id;
+ u32 vsi_num;
+ u16 vsi_base = 0;
+ u16 eth_id;
+ u16 func_id;
+ bool need_mcast = false;
+ bool add_flow = false;
+ bool vsi_match = 0;
+
+ eth_id = nbl_res_vsi_id_to_eth_id(res_mgt, vsi);
+ res = &flow_mgt->switch_res[eth_id];
+
+ rule_data = nbl_common_get_hash_xy_node(res->mac_hash_tbl, mac, &vlan);
+ if (!rule_data)
+ return;
+ if (!rule_data->mcast_flow && rule_data->vsi != vsi)
+ return;
+ else if (rule_data->mcast_flow &&
+ !test_bit(vsi - rule_data->mcc_group->vsi_base,
+ rule_data->mcc_group->vsi_bitmap))
+ return;
+
+ vsi_bitmap = kcalloc(BITS_TO_LONGS(flow_mgt->vsi_max_per_switch),
+ sizeof(long), GFP_KERNEL);
+ if (!vsi_bitmap)
+ return;
+
+ func_id = nbl_res_vsi_id_to_func_id(res_mgt, vsi);
+ nbl_res_func_id_to_pfvfid(res_mgt, func_id, &pf_id, &vf_id);
+ NBL_HASH_XY_TBL_SCAN_KEY_INIT(&scan_key, NBL_HASH_TBL_OP_SHOW,
+ NBL_HASH_TBL_ALL_SCAN, false, NULL, NULL,
+ &vsi,
+ &nbl_flow_macvlan_node_vsi_match_func,
+ &vsi_match,
+ &nbl_flow_macvlan_node_found_vsi_action);
+
+ if (rule_data->mcast_flow) {
+ bitmap_copy(vsi_bitmap, rule_data->mcc_group->vsi_bitmap,
+ flow_mgt->vsi_max_per_switch);
+ vsi_num = rule_data->mcc_group->vsi_num;
+ clear_bit(vsi - rule_data->mcc_group->vsi_base, vsi_bitmap);
+ vsi_num--;
+ vsi_base = (u16)rule_data->mcc_group->vsi_base;
+
+ if (rule_data->mcc_group->vsi_num > 1)
+ add_flow = true;
+
+ if ((rule_data->multi && rule_data->mcc_group->vsi_num > 1) ||
+ (!rule_data->multi && rule_data->mcc_group->vsi_num > 2))
+ need_mcast = 1;
+ pend_group = rule_data->mcc_group;
+ }
+
+ if (need_mcast) {
+ mcc_group = nbl_find_same_mcc_group(res, vsi_bitmap,
+ rule_data->multi);
+ if (!mcc_group) {
+ mcc_group = nbl_flow_alloc_mcc_group(res_mgt,
+ vsi_bitmap, eth_id,
+ rule_data->multi,
+ vsi_num);
+ if (!mcc_group)
+ goto alloc_mcc_group_failed;
+ }
+ }
+
+ for (i = 0; i < NBL_FLOW_MACVLAN_MAX; i++) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+
+ nbl_flow_del_flow(res_mgt, &rule_data->entry[i]);
+ }
+
+ if (pend_group)
+ nbl_flow_free_mcc_group(res_mgt, pend_group);
+
+ if (add_flow) {
+ param.mac = mac;
+ param.vid = vlan;
+ param.eth = eth_id;
+ param.mcc_id = NBL_MCC_ID_INVALID;
+ param.vsi = (u16)find_first_bit(vsi_bitmap,
+ flow_mgt->vsi_max_per_switch) +
+ vsi_base;
+
+ for (i = 0; i < NBL_FLOW_MACVLAN_MAX; i++) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+ if (mcc_group) {
+ if (i <= NBL_FLOW_UP)
+ param.mcc_id = mcc_group->up_mcc_id;
+ else
+ param.mcc_id = mcc_group->down_mcc_id;
+ }
+ ret = nbl_flow_add_flow(res_mgt, param, i,
+ &rule_data->entry[i]);
+ if (ret)
+ goto add_flow_failed;
+ }
+
+ if (mcc_group) {
+ rule_data->mcast_flow = 1;
+ rule_data->mcc_group = mcc_group;
+ } else {
+ rule_data->mcast_flow = 0;
+ rule_data->vsi = param.vsi;
+ }
+ }
+
+ if (!add_flow)
+ nbl_common_free_hash_xy_node(res->mac_hash_tbl, mac, &vlan);
+
+alloc_mcc_group_failed:
+ kfree(vsi_bitmap);
+
+ nbl_common_scan_hash_xy_node(res->mac_hash_tbl, &scan_key);
+ if (vf_id != U32_MAX && test_bit(vf_id, res->vf_bitmap) && !vsi_match) {
+ clear_bit(vf_id, res->vf_bitmap);
+ res->active_vfs--;
+ }
+
+ return;
+
+add_flow_failed:
+ while (--i + 1) {
+ if (i == NBL_FLOW_UP_TNL && rule_data->multi)
+ continue;
+ nbl_flow_del_flow(res_mgt, &rule_data->entry[i]);
+ }
+ if (mcc_group)
+ nbl_flow_free_mcc_group(res_mgt, mcc_group);
+ nbl_common_free_hash_xy_node(res->mac_hash_tbl, mac, &vlan);
+ kfree(vsi_bitmap);
+ nbl_common_scan_hash_xy_node(res->mac_hash_tbl, &scan_key);
+ if (vf_id != U32_MAX && test_bit(vf_id, res->vf_bitmap) && !vsi_match) {
+ clear_bit(vf_id, res->vf_bitmap);
+ res->active_vfs--;
+ }
+}
+
+static int nbl_flow_add_lldp(void *priv, u16 vsi)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_common_info *common = NBL_RES_MGT_TO_COMMON(res_mgt);
+ struct nbl_flow_lldp_rule *rule;
+ struct nbl_flow_param param = { 0 };
+
+ list_for_each_entry(rule, &flow_mgt->lldp_list, node)
+ if (rule->vsi == vsi)
+ return 0;
+
+ rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+ if (!rule)
+ return -ENOMEM;
+
+ param.eth = nbl_res_vsi_id_to_eth_id(res_mgt, vsi);
+ param.vsi = vsi;
+ param.ether_type = ETH_P_LLDP;
+
+ if (nbl_flow_add_flow(res_mgt, param, NBL_FLOW_LLDP_LACP_UP,
+ &rule->entry)) {
+ nbl_err(common, "Fail to add lldp flow for vsi %d", vsi);
+ kfree(rule);
+ return -EFAULT;
+ }
+
+ rule->vsi = vsi;
+ list_add_tail(&rule->node, &flow_mgt->lldp_list);
+
+ return 0;
+}
+
+static void nbl_flow_del_lldp(void *priv, u16 vsi)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt;
+ struct nbl_flow_lldp_rule *rule;
+
+ flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+
+ list_for_each_entry(rule, &flow_mgt->lldp_list, node)
+ if (rule->vsi == vsi)
+ break;
+
+ if (list_entry_is_head(rule, &flow_mgt->lldp_list, node))
+ return;
+
+ nbl_flow_del_flow(res_mgt, &rule->entry);
+
+ list_del(&rule->node);
+ kfree(rule);
+}
+
+static int nbl_flow_change_mcc_group_chain(struct nbl_resource_mgt *res_mgt,
+ u8 eth, u16 current_mcc_id)
+{
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *switch_res = &flow_mgt->switch_res[eth];
+ struct nbl_flow_mcc_group *group;
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+ void *p = NBL_RES_MGT_TO_HW_PRIV(res_mgt);
+ u16 node_mcc;
+
+ list_for_each_entry(group, &switch_res->mcc_group_head, group_node)
+ if (group->multi && !list_empty(&group->mcc_node)) {
+ node_mcc = list_last_entry(&group->mcc_node,
+ struct nbl_flow_mcc_node,
+ node)
+ ->mcc_id;
+ hw_ops->update_mcc_next_node(p, node_mcc,
+ current_mcc_id);
+ }
+ switch_res->allmulti_first_mcc = current_mcc_id;
+ return 0;
+}
+
+static int nbl_flow_add_multi_mcast(void *priv, u16 vsi)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *switch_res;
+ struct nbl_flow_mcc_node *node;
+ int ret;
+ u16 current_mcc_id;
+ u8 eth = nbl_res_vsi_id_to_eth_id(res_mgt, vsi);
+
+ switch_res = &flow_mgt->switch_res[eth];
+ list_for_each_entry(node, &switch_res->allmulti_list, node)
+ if (node->data == vsi && node->type == NBL_MCC_INDEX_VSI)
+ return 0;
+
+ node = nbl_flow_alloc_mcc_node(flow_mgt, NBL_MCC_INDEX_VSI, vsi, 0);
+ if (!node)
+ return -ENOSPC;
+
+ switch_res = &flow_mgt->switch_res[eth];
+ ret = nbl_flow_add_mcc_node(res_mgt, node, &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+ if (ret) {
+ nbl_flow_free_mcc_node(flow_mgt, node);
+ return ret;
+ }
+
+ if (list_empty(&switch_res->allmulti_list))
+ current_mcc_id = NBL_MCC_ID_INVALID;
+ else
+ current_mcc_id = list_first_entry(&switch_res->allmulti_list,
+ struct nbl_flow_mcc_node,
+ node)
+ ->mcc_id;
+
+ if (current_mcc_id != switch_res->allmulti_first_mcc)
+ nbl_flow_change_mcc_group_chain(res_mgt, eth, current_mcc_id);
+
+ return 0;
+}
+
+static void nbl_flow_del_multi_mcast(void *priv, u16 vsi)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *switch_res;
+ struct nbl_flow_mcc_node *mcc_node;
+ u16 current_mcc_id;
+ u8 eth = nbl_res_vsi_id_to_eth_id(res_mgt, vsi);
+
+ switch_res = &flow_mgt->switch_res[eth];
+ list_for_each_entry(mcc_node, &switch_res->allmulti_list, node)
+ if (mcc_node->data == vsi &&
+ mcc_node->type == NBL_MCC_INDEX_VSI) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node,
+ &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ break;
+ }
+
+ if (list_empty(&switch_res->allmulti_list))
+ current_mcc_id = NBL_MCC_ID_INVALID;
+ else
+ current_mcc_id = list_first_entry(&switch_res->allmulti_list,
+ struct nbl_flow_mcc_node,
+ node)
+ ->mcc_id;
+
+ if (current_mcc_id != switch_res->allmulti_first_mcc)
+ nbl_flow_change_mcc_group_chain(res_mgt, eth, current_mcc_id);
+}
+
+static int nbl_flow_add_multi_group(struct nbl_resource_mgt *res_mgt, u8 eth)
+{
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *switch_res = &flow_mgt->switch_res[eth];
+ struct nbl_flow_param param_up = {0};
+ struct nbl_flow_mcc_node *up_node;
+ struct nbl_flow_param param_down = {0};
+ struct nbl_flow_mcc_node *down_node;
+ int i, ret;
+
+ down_node =
+ nbl_flow_alloc_mcc_node(flow_mgt, NBL_MCC_INDEX_ETH, eth, 1);
+ if (!down_node)
+ return -ENOSPC;
+
+ ret = nbl_flow_add_mcc_node(res_mgt, down_node,
+ &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+ if (ret)
+ goto add_eth_mcc_node_failed;
+
+ param_down.mcc_id = down_node->mcc_id;
+ param_down.eth = eth;
+ for (i = 0;
+ i < NBL_FLOW_DOWN_MULTI_MCAST_END - NBL_FLOW_L2_DOWN_MULTI_MCAST;
+ i++) {
+ ret = nbl_flow_add_flow(res_mgt, param_down,
+ i + NBL_FLOW_L2_DOWN_MULTI_MCAST,
+ &switch_res->allmulti_down[i]);
+ if (ret)
+ goto add_down_flow_failed;
+ }
+
+ up_node = nbl_flow_alloc_mcc_node(flow_mgt, NBL_MCC_INDEX_BMC,
+ NBL_FLOW_MCC_BMC_DPORT, 1);
+ if (!up_node) {
+ ret = -ENOSPC;
+ goto alloc_bmc_node_failed;
+ }
+
+ ret = nbl_flow_add_mcc_node(res_mgt, up_node,
+ &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+ if (ret)
+ goto add_bmc_mcc_node_failed;
+
+ param_up.mcc_id = up_node->mcc_id;
+ param_up.eth = eth;
+ for (i = 0;
+ i < NBL_FLOW_UP_MULTI_MCAST_END - NBL_FLOW_L2_UP_MULTI_MCAST;
+ i++) {
+ ret = nbl_flow_add_flow(res_mgt, param_up,
+ i + NBL_FLOW_L2_UP_MULTI_MCAST,
+ &switch_res->allmulti_up[i]);
+ if (ret)
+ goto add_up_flow_failed;
+ }
+
+ switch_res->ether_id = eth;
+ switch_res->allmulti_first_mcc = NBL_MCC_ID_INVALID;
+ switch_res->vld = 1;
+
+ return 0;
+
+add_up_flow_failed:
+ while (--i >= 0)
+ nbl_flow_del_flow(res_mgt, &switch_res->allmulti_up[i]);
+ nbl_flow_del_mcc_node(res_mgt, up_node, &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+add_bmc_mcc_node_failed:
+ nbl_flow_free_mcc_node(flow_mgt, up_node);
+alloc_bmc_node_failed:
+add_down_flow_failed:
+ while (--i >= 0)
+ nbl_flow_del_flow(res_mgt, &switch_res->allmulti_down[i]);
+ nbl_flow_del_mcc_node(res_mgt, down_node, &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+add_eth_mcc_node_failed:
+ nbl_flow_free_mcc_node(flow_mgt, down_node);
+ return ret;
+}
+
+static void nbl_flow_del_multi_group(struct nbl_resource_mgt *res_mgt, u8 eth)
+{
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ struct nbl_flow_switch_res *switch_res = &flow_mgt->switch_res[eth];
+ struct nbl_flow_mcc_node *mcc_node, *mcc_node_safe;
+
+ if (!switch_res->vld)
+ return;
+
+ nbl_flow_del_flow(res_mgt, &switch_res->allmulti_up[0]);
+ nbl_flow_del_flow(res_mgt, &switch_res->allmulti_up[1]);
+ nbl_flow_del_flow(res_mgt, &switch_res->allmulti_down[0]);
+ nbl_flow_del_flow(res_mgt, &switch_res->allmulti_down[1]);
+
+ list_for_each_entry_safe(mcc_node, mcc_node_safe,
+ &switch_res->allmulti_list, node) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node,
+ &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ }
+
+ list_for_each_entry_safe(mcc_node, mcc_node_safe,
+ &switch_res->allmulti_head, node) {
+ nbl_flow_del_mcc_node(res_mgt, mcc_node,
+ &switch_res->allmulti_head,
+ &switch_res->allmulti_list, NULL);
+ nbl_flow_free_mcc_node(flow_mgt, mcc_node);
+ }
+
+ INIT_LIST_HEAD(&switch_res->allmulti_list);
+ INIT_LIST_HEAD(&switch_res->allmulti_head);
+ switch_res->vld = 0;
+ switch_res->allmulti_first_mcc = NBL_MCC_ID_INVALID;
+}
+
+static void nbl_flow_remove_multi_group(void *priv)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_eth_info *eth_info = NBL_RES_MGT_TO_ETH_INFO(res_mgt);
+ int i;
+
+ for_each_set_bit(i, eth_info->eth_bitmap, NBL_MAX_ETHERNET)
+ nbl_flow_del_multi_group(res_mgt, i);
+}
+
+static int nbl_flow_setup_multi_group(void *priv)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_eth_info *eth_info = NBL_RES_MGT_TO_ETH_INFO(res_mgt);
+ int i, ret = 0;
+
+ for_each_set_bit(i, eth_info->eth_bitmap, NBL_MAX_ETHERNET) {
+ ret = nbl_flow_add_multi_group(res_mgt, i);
+ if (ret)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ nbl_flow_remove_multi_group(res_mgt);
+ return ret;
+}
+
+static u16 nbl_vsi_mtu_index(struct nbl_resource_mgt *res_mgt, u16 vsi_id)
+{
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+ u16 index;
+
+ index = hw_ops->get_mtu_index(NBL_RES_MGT_TO_HW_PRIV(res_mgt), vsi_id);
+ return index - 1;
+}
+
+static void nbl_clear_mtu_entry(struct nbl_resource_mgt *res_mgt, u16 vsi_id)
+{
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+ u16 mtu_index;
+
+ mtu_index = nbl_vsi_mtu_index(res_mgt, vsi_id);
+ if (mtu_index < NBL_MAX_MTU_NUM) {
+ res_mgt->resource_info->mtu_list[mtu_index].ref_count--;
+ hw_ops->set_vsi_mtu(NBL_RES_MGT_TO_HW_PRIV(res_mgt), vsi_id, 0);
+ if (res_mgt->resource_info->mtu_list[mtu_index].ref_count ==
+ 0) {
+ hw_ops->set_mtu(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ mtu_index + 1, 0);
+ res_mgt->resource_info->mtu_list[mtu_index].mtu_value =
+ 0;
+ }
+ }
+}
+
+static void nbl_flow_clear_flow(void *priv, u16 vsi_id)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ void *mac_hash_tbl;
+ struct nbl_hash_xy_tbl_scan_key scan_key;
+ u8 eth_id;
+
+ eth_id = nbl_res_vsi_id_to_eth_id(res_mgt, vsi_id);
+ mac_hash_tbl = flow_mgt->switch_res[eth_id].mac_hash_tbl;
+
+ nbl_clear_mtu_entry(res_mgt, vsi_id);
+ NBL_HASH_XY_TBL_SCAN_KEY_INIT(&scan_key, NBL_HASH_TBL_OP_DELETE,
+ NBL_HASH_TBL_ALL_SCAN, false, NULL, NULL,
+ &vsi_id,
+ &nbl_flow_macvlan_node_vsi_match_func,
+ res_mgt,
+ &nbl_flow_macvlan_node_del_action_func);
+ nbl_common_scan_hash_xy_node(mac_hash_tbl, &scan_key);
+ nbl_flow_del_multi_mcast(res_mgt, vsi_id);
+}
+
+static void nbl_res_flr_clear_flow(void *priv, u16 vf_id)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ u16 func_id = vf_id + NBL_MAX_PF;
+ u16 vsi_id = nbl_res_func_id_to_vsi_id(res_mgt, func_id,
+ NBL_VSI_SERV_VF_DATA_TYPE);
+
+ if (nbl_res_vf_is_active(priv, func_id))
+ nbl_flow_clear_flow(priv, vsi_id);
+}
+
+static int nbl_res_flow_check_flow_table_spec(void *priv, u16 vlan_cnt,
+ u16 unicast_cnt,
+ u16 multicast_cnt)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_flow_mgt *flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ u32 reserve_cnt = nbl_flow_get_reserve_macvlan_cnt(res_mgt);
+ u32 need = vlan_cnt * (3 * unicast_cnt + 2 * multicast_cnt);
+
+ if (reserve_cnt + need > flow_mgt->flow_id_cnt)
+ return -ENOSPC;
+
+ return 0;
+}
+
+static int nbl_res_set_mtu(void *priv, u16 vsi_id, u16 mtu)
+{
+ struct nbl_resource_mgt *res_mgt = (struct nbl_resource_mgt *)priv;
+ struct nbl_hw_ops *hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+ struct nbl_mtu_entry *mtu_list = &res_mgt->resource_info->mtu_list[0];
+ int i, found_idx = -1, first_zero_idx = -1;
+ u16 real_mtu = mtu + ETH_HLEN + 2 * VLAN_HLEN;
+
+ nbl_clear_mtu_entry(res_mgt, vsi_id);
+ if (mtu == 0)
+ return 0;
+
+ for (i = 0; i < NBL_MAX_MTU_NUM; i++) {
+ if (mtu_list[i].mtu_value == real_mtu) {
+ found_idx = i;
+ break;
+ }
+
+ if (!mtu_list[i].mtu_value)
+ first_zero_idx = i;
+ }
+
+ if (first_zero_idx == -1 && found_idx == -1)
+ return 0;
+
+ if (found_idx != -1) {
+ mtu_list[found_idx].ref_count++;
+ hw_ops->set_vsi_mtu(NBL_RES_MGT_TO_HW_PRIV(res_mgt), vsi_id,
+ found_idx + 1);
+ return 0;
+ }
+
+ if (first_zero_idx != -1) {
+ mtu_list[first_zero_idx].ref_count++;
+ mtu_list[first_zero_idx].mtu_value = real_mtu;
+ hw_ops->set_vsi_mtu(NBL_RES_MGT_TO_HW_PRIV(res_mgt), vsi_id,
+ first_zero_idx + 1);
+ hw_ops->set_mtu(NBL_RES_MGT_TO_HW_PRIV(res_mgt),
+ first_zero_idx + 1, real_mtu);
+ }
+
+ return 0;
+}
+
+/* NBL_FLOW_SET_OPS(ops_name, func)
+ *
+ * Use X Macros to reduce setup and remove codes.
+ */
+#define NBL_FLOW_OPS_TBL \
+do { \
+ NBL_FLOW_SET_OPS(add_macvlan, nbl_flow_add_macvlan); \
+ NBL_FLOW_SET_OPS(del_macvlan, nbl_flow_del_macvlan); \
+ NBL_FLOW_SET_OPS(add_lldp_flow, nbl_flow_add_lldp); \
+ NBL_FLOW_SET_OPS(del_lldp_flow, nbl_flow_del_lldp); \
+ NBL_FLOW_SET_OPS(add_multi_mcast, nbl_flow_add_multi_mcast); \
+ NBL_FLOW_SET_OPS(del_multi_mcast, nbl_flow_del_multi_mcast); \
+ NBL_FLOW_SET_OPS(setup_multi_group, nbl_flow_setup_multi_group); \
+ NBL_FLOW_SET_OPS(remove_multi_group, nbl_flow_remove_multi_group); \
+ NBL_FLOW_SET_OPS(clear_flow, nbl_flow_clear_flow); \
+ NBL_FLOW_SET_OPS(flr_clear_flows, nbl_res_flr_clear_flow); \
+ NBL_FLOW_SET_OPS(set_mtu, nbl_res_set_mtu); \
+ NBL_FLOW_SET_OPS(check_flow_table_spec, \
+ nbl_res_flow_check_flow_table_spec); \
+} while (0)
+
+static void nbl_flow_remove_mgt(struct device *dev,
+ struct nbl_resource_mgt *res_mgt)
+{
+ struct nbl_flow_mgt *fl_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ int i;
+ struct nbl_hash_xy_tbl_del_key del_key;
+
+ NBL_HASH_XY_TBL_DEL_KEY_INIT(&del_key, res_mgt,
+ &nbl_flow_macvlan_node_del_action_func);
+ for (i = 0; i < NBL_MAX_ETHERNET; i++) {
+ nbl_common_rm_hash_xy_table(fl_mgt->switch_res[i].mac_hash_tbl,
+ &del_key);
+ if (fl_mgt->switch_res[i].vf_bitmap)
+ devm_kfree(dev, fl_mgt->switch_res[i].vf_bitmap);
+ }
+
+ if (fl_mgt->flow_id_bitmap)
+ devm_kfree(dev, fl_mgt->flow_id_bitmap);
+ if (fl_mgt->mcc_id_bitmap)
+ devm_kfree(dev, fl_mgt->mcc_id_bitmap);
+ fl_mgt->flow_id_cnt = 0;
+ devm_kfree(dev, fl_mgt);
+ NBL_RES_MGT_TO_FLOW_MGT(res_mgt) = NULL;
+}
+
+static int nbl_flow_setup_mgt(struct device *dev,
+ struct nbl_resource_mgt *res_mgt)
+{
+ struct nbl_hash_xy_tbl_key macvlan_tbl_key;
+ struct nbl_flow_mgt *flow_mgt;
+ struct nbl_eth_info *eth_info;
+ int i;
+ int vf_num = -1;
+ u16 pf_id;
+
+ flow_mgt = devm_kzalloc(dev, sizeof(struct nbl_flow_mgt), GFP_KERNEL);
+ if (!flow_mgt)
+ return -ENOMEM;
+
+ NBL_RES_MGT_TO_FLOW_MGT(res_mgt) = flow_mgt;
+ eth_info = NBL_RES_MGT_TO_ETH_INFO(res_mgt);
+
+ flow_mgt->flow_id_bitmap =
+ devm_kcalloc(dev, BITS_TO_LONGS(NBL_MACVLAN_TABLE_LEN),
+ sizeof(long), GFP_KERNEL);
+ if (!flow_mgt->flow_id_bitmap)
+ goto setup_mgt_failed;
+ flow_mgt->flow_id_cnt = NBL_MACVLAN_TABLE_LEN;
+
+ flow_mgt->mcc_id_bitmap =
+ devm_kcalloc(dev, BITS_TO_LONGS(NBL_FLOW_MCC_INDEX_SIZE),
+ sizeof(long), GFP_KERNEL);
+ if (!flow_mgt->mcc_id_bitmap)
+ goto setup_mgt_failed;
+
+ NBL_HASH_XY_TBL_KEY_INIT(&macvlan_tbl_key, dev, ETH_ALEN, sizeof(u16),
+ sizeof(struct nbl_flow_l2_data),
+ NBL_MACVLAN_TBL_BUCKET_SIZE,
+ NBL_MACVLAN_X_AXIS_BUCKET_SIZE,
+ NBL_MACVLAN_Y_AXIS_BUCKET_SIZE, false);
+ for (i = 0; i < NBL_MAX_ETHERNET; i++) {
+ INIT_LIST_HEAD(&flow_mgt->switch_res[i].allmulti_head);
+ INIT_LIST_HEAD(&flow_mgt->switch_res[i].allmulti_list);
+ INIT_LIST_HEAD(&flow_mgt->switch_res[i].mcc_group_head);
+
+ flow_mgt->switch_res[i].mac_hash_tbl =
+ nbl_common_init_hash_xy_table(&macvlan_tbl_key);
+ if (!flow_mgt->switch_res[i].mac_hash_tbl)
+ goto setup_mgt_failed;
+ pf_id = find_first_bit((unsigned long *)ð_info->pf_bitmap[i],
+ 8);
+ if (pf_id != 8)
+ vf_num = nbl_res_get_pf_vf_num(res_mgt, pf_id);
+
+ if (vf_num != -1) {
+ flow_mgt->switch_res[i].num_vfs = vf_num;
+ flow_mgt->switch_res[i].vf_bitmap =
+ devm_kcalloc(dev, BITS_TO_LONGS(vf_num),
+ sizeof(long), GFP_KERNEL);
+ if (!flow_mgt->switch_res[i].vf_bitmap)
+ goto setup_mgt_failed;
+ } else {
+ flow_mgt->switch_res[i].num_vfs = 0;
+ flow_mgt->switch_res[i].vf_bitmap = NULL;
+ }
+ flow_mgt->switch_res[i].active_vfs = 0;
+ }
+
+ INIT_LIST_HEAD(&flow_mgt->lldp_list);
+ INIT_LIST_HEAD(&flow_mgt->lacp_list);
+ INIT_LIST_HEAD(&flow_mgt->ul4s_head);
+ INIT_LIST_HEAD(&flow_mgt->dprbac_head);
+
+ flow_mgt->vsi_max_per_switch = NBL_VSI_MAX_ID / eth_info->eth_num;
+
+ return 0;
+
+setup_mgt_failed:
+ nbl_flow_remove_mgt(dev, res_mgt);
+ return -1;
+}
+
+int nbl_flow_mgt_start_leonis(struct nbl_resource_mgt *res_mgt)
+{
+ struct nbl_hw_ops *hw_ops;
+ struct device *dev;
+ int ret = 0;
+
+ dev = NBL_RES_MGT_TO_DEV(res_mgt);
+ hw_ops = NBL_RES_MGT_TO_HW_OPS(res_mgt);
+
+ ret = nbl_flow_setup_mgt(dev, res_mgt);
+ if (ret)
+ goto setup_mgt_fail;
+
+ ret = hw_ops->init_fem(NBL_RES_MGT_TO_HW_PRIV(res_mgt));
+ if (ret)
+ goto init_fem_fail;
+
+ return 0;
+
+init_fem_fail:
+ nbl_flow_remove_mgt(dev, res_mgt);
+setup_mgt_fail:
+ return -1;
+}
+
+void nbl_flow_mgt_stop_leonis(struct nbl_resource_mgt *res_mgt)
+{
+ struct device *dev;
+ struct nbl_flow_mgt *flow_mgt;
+
+ dev = NBL_RES_MGT_TO_DEV(res_mgt);
+ flow_mgt = NBL_RES_MGT_TO_FLOW_MGT(res_mgt);
+ if (!flow_mgt)
+ return;
+
+ nbl_flow_remove_mgt(dev, res_mgt);
+}
+
+int nbl_flow_setup_ops_leonis(struct nbl_resource_ops *res_ops)
+{
+#define NBL_FLOW_SET_OPS(name, func) \
+ do { \
+ res_ops->NBL_NAME(name) = func; \
+ ; \
+ } while (0)
+ NBL_FLOW_OPS_TBL;
+#undef NBL_FLOW_SET_OPS
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.h
new file mode 100644
index 000000000000..b513eb2afd87
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_flow_leonis.h
@@ -0,0 +1,204 @@
+/* SPDX-License-Identifier: GPL-2.0*/
+/*
+ * Copyright (c) 2025 Nebula Matrix Limited.
+ * Author:
+ */
+#ifndef _NBL_FLOW_LEONIS_H_
+#define _NBL_FLOW_LEONIS_H_
+
+#include "nbl_core.h"
+#include "nbl_hw.h"
+#include "nbl_resource.h"
+
+#define NBL_EM_HW_KT_OFFSET (0x1E000)
+
+#define NBL_TOTAL_MACVLAN_NUM 4096
+#define NBL_MAX_ACTION_NUM 16
+
+#define NBL_FLOW_MCC_PXE_SIZE 8
+#define NBL_FLOW_MCC_INDEX_SIZE (4096 - NBL_FLOW_MCC_PXE_SIZE)
+#define NBL_FLOW_MCC_INDEX_START (4 * 1024)
+#define NBL_FLOW_MCC_BMC_DPORT 0x30D
+
+#define NBL_MACVLAN_TBL_BUCKET_SIZE 64
+#define NBL_MACVLAN_X_AXIS_BUCKET_SIZE 64
+#define NBL_MACVLAN_Y_AXIS_BUCKET_SIZE 16
+
+#define NBL_PP0_POWER 11
+
+enum nbl_flow_mcc_index_type {
+ NBL_MCC_INDEX_ETH,
+ NBL_MCC_INDEX_VSI,
+ NBL_MCC_INDEX_BOND,
+ NBL_MCC_INDEX_BMC,
+};
+
+#pragma pack(1)
+
+#define NBL_DUPPKT_PTYPE_NA 135
+#define NBL_DUPPKT_PTYPE_NS 136
+
+struct nbl_flow_l2_data {
+ struct nbl_flow_fem_entry entry[NBL_FLOW_MACVLAN_MAX];
+ union {
+ struct nbl_flow_mcc_group *mcc_group;
+ u16 vsi;
+ };
+ bool multi;
+ bool mcast_flow;
+};
+
+union nbl_l2_hw_up_data_u {
+ struct nbl_l2_hw_up_data {
+ u32 act0:22;
+ u32 act1:22;
+ u64 rsv1:40;
+ u32 padding:4;
+ u32 sport:4;
+ u32 svlan_id:16;
+ u64 dst_mac:48;
+ u32 template:4;
+ u32 rsv[5];
+ } __packed info;
+#define NBL_L2_HW_UP_DATA_TAB_WIDTH \
+ (sizeof(struct nbl_l2_hw_up_data) / sizeof(u32))
+ u32 data[NBL_L2_HW_UP_DATA_TAB_WIDTH];
+ u8 hash_key[sizeof(struct nbl_l2_hw_up_data)];
+};
+
+union nbl_l2_hw_lldp_lacp_data_u {
+ struct nbl_l2_hw_lldp_lacp_data {
+ u32 act0:22;
+ u32 rsv1:2;
+ u8 padding[14];
+ u32 sport:4;
+ u32 ether_type:16;
+ u32 template:4;
+ u32 rsv[5];
+ } __packed info;
+#define NBL_L2_HW_LLDP_LACP_DATA_TAB_WIDTH \
+ (sizeof(struct nbl_l2_hw_lldp_lacp_data) / sizeof(u32))
+ u32 data[NBL_L2_HW_LLDP_LACP_DATA_TAB_WIDTH];
+ u8 hash_key[sizeof(struct nbl_l2_hw_lldp_lacp_data)];
+};
+
+union nbl_l2_hw_up_multi_mcast_data_u {
+ struct nbl_l2_hw_up_multi_mcast_data {
+ u32 act0:22;
+ u32 rsv1:2;
+ u8 padding[16];
+ u32 sport:4;
+ u32 template:4;
+ u32 rsv[5];
+ } __packed info;
+#define NBL_L2_HW_UP_MULTI_MCAST_DATA_TAB_WIDTH \
+ (sizeof(struct nbl_l2_hw_up_multi_mcast_data) / sizeof(u32))
+ u32 data[NBL_L2_HW_UP_MULTI_MCAST_DATA_TAB_WIDTH];
+ u8 hash_key[sizeof(struct nbl_l2_hw_up_multi_mcast_data)];
+};
+
+union nbl_l2_hw_down_multi_mcast_data_u {
+ struct nbl_l2_hw_down_multi_mcast_data {
+ u32 act0:22;
+ u32 rsv1:2;
+ u8 rsv2[16];
+ u32 padding:2;
+ u32 sport:2;
+ u32 template:4;
+ u32 rsv[5];
+ } __packed info;
+#define NBL_L2_HW_DOWN_MULTI_MCAST_DATA_TAB_WIDTH \
+ (sizeof(struct nbl_l2_hw_down_multi_mcast_data) / sizeof(u32))
+ u32 data[NBL_L2_HW_DOWN_MULTI_MCAST_DATA_TAB_WIDTH];
+ u8 hash_key[sizeof(struct nbl_l2_hw_down_multi_mcast_data)];
+};
+
+union nbl_l2_hw_down_data_u {
+ struct nbl_l2_hw_down_data {
+ u32 act0:22;
+ u32 act1:22;
+ u64 rsv2:40;
+ u32 padding:6;
+ u32 sport:2;
+ u32 svlan_id:16;
+ u64 dst_mac:48;
+ u32 template:4;
+ u32 rsv[5];
+ } __packed info;
+#define NBL_L2_HW_DOWN_DATA_TAB_WIDTH \
+ (sizeof(struct nbl_l2_hw_down_data) / sizeof(u32))
+ u32 data[NBL_L2_HW_DOWN_DATA_TAB_WIDTH];
+ u8 hash_key[sizeof(struct nbl_l2_hw_down_data)];
+};
+
+union nbl_common_data_u {
+ struct nbl_common_data {
+ u32 rsv[10];
+ } __packed info;
+#define NBL_COMMON_DATA_TAB_WIDTH (sizeof(struct nbl_common_data) / sizeof(u32))
+ u32 data[NBL_COMMON_DATA_TAB_WIDTH];
+ u8 hash_key[sizeof(struct nbl_common_data)];
+};
+
+#pragma pack()
+
+struct nbl_flow_param {
+ u8 *mac;
+ u8 type;
+ u8 eth;
+ u16 ether_type;
+ u16 vid;
+ u16 vsi;
+ u16 mcc_id;
+ u32 index;
+ u32 *data;
+ u32 priv_data;
+ bool for_pmd;
+};
+
+struct nbl_mt_input {
+ u8 key[NBL_KT_BYTE_LEN];
+ u8 at_num;
+ u8 kt_left_num;
+ u32 tbl_id;
+ u16 depth;
+ u16 power;
+};
+
+struct nbl_ht_item {
+ u16 ht0_hash;
+ u16 ht1_hash;
+ u16 hash_bucket;
+ u32 key_index;
+ u8 ht_table;
+};
+
+struct nbl_kt_item {
+ union nbl_common_data_u kt_data;
+};
+
+struct nbl_tcam_item {
+ struct nbl_ht_item ht_item;
+ struct nbl_kt_item kt_item;
+ u32 tcam_action[NBL_MAX_ACTION_NUM];
+ bool tcam_flag;
+ u8 key_mode;
+ u8 pp_type;
+ u32 *pp_tcam_count;
+ u16 tcam_index;
+};
+
+struct nbl_tcam_ad_item {
+ u32 action[NBL_MAX_ACTION_NUM];
+};
+
+struct nbl_flow_rule_cfg_ops {
+ int (*cfg_action)(struct nbl_flow_param param, u32 *action0,
+ u32 *action1);
+ int (*cfg_key)(union nbl_common_data_u *data,
+ struct nbl_flow_param param, u8 eth_mode);
+ void (*cfg_kt_action)(union nbl_common_data_u *data, u32 action0,
+ u32 action1);
+};
+
+#endif
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
index 4ee35f46c785..0b15d6365513 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_hw_leonis.c
@@ -25,6 +25,467 @@ static u32 nbl_hw_get_quirks(void *priv)
return quirks;
}
+static int nbl_send_kt_data(struct nbl_hw_mgt *hw_mgt,
+ union nbl_fem_kt_acc_ctrl_u *kt_ctrl, u8 *data,
+ struct nbl_common_info *common)
+{
+ union nbl_fem_kt_acc_ack_u kt_ack = { .info = { 0 } };
+ u32 times = 3;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_KT_ACC_DATA, data, NBL_KT_HW_L2_DW_LEN);
+ nbl_debug(common, "Set kt = %08x-%08x-%08x-%08x-%08x", ((u32 *)data)[0],
+ ((u32 *)data)[1], ((u32 *)data)[2], ((u32 *)data)[3],
+ ((u32 *)data)[4]);
+
+ kt_ctrl->info.rw = NBL_ACC_MODE_WRITE;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_KT_ACC_CTRL, kt_ctrl->data,
+ NBL_FEM_KT_ACC_CTRL_TBL_WIDTH);
+
+ times = 3;
+ do {
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_KT_ACC_ACK, kt_ack.data,
+ NBL_FEM_KT_ACC_ACK_TBL_WIDTH);
+ if (!kt_ack.info.done) {
+ times--;
+ usleep_range(100, 200);
+ } else {
+ break;
+ }
+ } while (times);
+
+ if (!times) {
+ nbl_err(common, "Config kt flowtale failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int nbl_send_ht_data(struct nbl_hw_mgt *hw_mgt,
+ union nbl_fem_ht_acc_ctrl_u *ht_ctrl, u8 *data,
+ struct nbl_common_info *common)
+{
+ union nbl_fem_ht_acc_ack_u ht_ack = { .info = { 0 } };
+ u32 times = 3;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_HT_ACC_DATA, data,
+ NBL_FEM_HT_ACC_DATA_TBL_WIDTH);
+ nbl_debug(common, "Set ht data = %x", *(u32 *)data);
+
+ ht_ctrl->info.rw = NBL_ACC_MODE_WRITE;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_HT_ACC_CTRL, ht_ctrl->data,
+ NBL_FEM_HT_ACC_CTRL_TBL_WIDTH);
+
+ times = 3;
+ do {
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_HT_ACC_ACK, ht_ack.data,
+ NBL_FEM_HT_ACC_ACK_TBL_WIDTH);
+ if (!ht_ack.info.done) {
+ times--;
+ usleep_range(100, 200);
+ } else {
+ break;
+ }
+ } while (times);
+
+ if (!times) {
+ nbl_err(common, "Config ht flowtale failed");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void nbl_check_kt_data(struct nbl_hw_mgt *hw_mgt,
+ union nbl_fem_kt_acc_ctrl_u *kt_ctrl,
+ struct nbl_common_info *common)
+{
+ union nbl_fem_kt_acc_ack_u ack = { .info = { 0 } };
+ u32 data[10] = { 0 };
+
+ kt_ctrl->info.rw = NBL_ACC_MODE_READ;
+ kt_ctrl->info.access_size = NBL_ACC_SIZE_320B;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_KT_ACC_CTRL, kt_ctrl->data,
+ NBL_FEM_KT_ACC_CTRL_TBL_WIDTH);
+
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_KT_ACC_ACK, ack.data,
+ NBL_FEM_KT_ACC_ACK_TBL_WIDTH);
+ nbl_debug(common, "Check kt done:%u status:%u.", ack.info.done,
+ ack.info.status);
+ if (ack.info.done) {
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_KT_ACC_DATA, (u8 *)data,
+ NBL_KT_HW_L2_DW_LEN);
+ nbl_debug(common,
+ "Check kt data:0x%x-%x-%x-%x-%x-%x-%x-%x-%x-%x.",
+ data[9], data[8], data[7], data[6], data[5], data[4],
+ data[3], data[2], data[1], data[0]);
+ }
+}
+
+static void nbl_check_ht_data(struct nbl_hw_mgt *hw_mgt,
+ union nbl_fem_ht_acc_ctrl_u *ht_ctrl,
+ struct nbl_common_info *common)
+{
+ union nbl_fem_ht_acc_ack_u ack = { .info = { 0 } };
+ u32 data[4] = { 0 };
+
+ ht_ctrl->info.rw = NBL_ACC_MODE_READ;
+ ht_ctrl->info.access_size = NBL_ACC_SIZE_128B;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_HT_ACC_CTRL, ht_ctrl->data,
+ NBL_FEM_HT_ACC_CTRL_TBL_WIDTH);
+
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_HT_ACC_ACK, ack.data,
+ NBL_FEM_HT_ACC_ACK_TBL_WIDTH);
+ nbl_debug(common, "Check ht done:%u status:%u.", ack.info.done,
+ ack.info.status);
+ if (ack.info.done) {
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_HT_ACC_DATA, (u8 *)data,
+ NBL_FEM_HT_ACC_DATA_TBL_WIDTH);
+ nbl_debug(common, "Check ht data:0x%x-%x-%x-%x.", data[0],
+ data[1], data[2], data[3]);
+ }
+}
+
+static void nbl_hw_fem_set_bank(struct nbl_hw_mgt *hw_mgt)
+{
+ u32 bank_sel = 0;
+
+ /* HT bank sel */
+ bank_sel = HT_PORT0_BANK_SEL | HT_PORT1_BANK_SEL << NBL_8BIT |
+ HT_PORT2_BANK_SEL << NBL_16BIT;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_HT_BANK_SEL_BITMAP, (u8 *)&bank_sel,
+ sizeof(bank_sel));
+
+ /* KT bank sel */
+ bank_sel = KT_PORT0_BANK_SEL | KT_PORT1_BANK_SEL << NBL_8BIT |
+ KT_PORT2_BANK_SEL << NBL_16BIT;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_KT_BANK_SEL_BITMAP, (u8 *)&bank_sel,
+ sizeof(bank_sel));
+
+ /* AT bank sel */
+ bank_sel = AT_PORT0_BANK_SEL | AT_PORT1_BANK_SEL << NBL_16BIT;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_AT_BANK_SEL_BITMAP, (u8 *)&bank_sel,
+ sizeof(bank_sel));
+ bank_sel = AT_PORT2_BANK_SEL;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_AT_BANK_SEL_BITMAP2, (u8 *)&bank_sel,
+ sizeof(bank_sel));
+}
+
+static void nbl_hw_fem_clear_tcam_ad(struct nbl_hw_mgt *hw_mgt)
+{
+ union fem_em_ad_table_u ad_table = { .info = { 0 } };
+ union fem_em_tcam_table_u tcam_table;
+ int i, j;
+
+ memset(&tcam_table, 0, sizeof(tcam_table));
+
+ for (i = 0; i < NBL_PT_LEN; i++) {
+ for (j = 0; j < NBL_TCAM_TABLE_LEN; j++) {
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_EM_TCAM_TABLE_REG(i, j),
+ tcam_table.hash_key, sizeof(tcam_table));
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_EM_AD_TABLE_REG(i, j),
+ ad_table.hash_key, sizeof(ad_table));
+ nbl_hw_rd32(hw_mgt, NBL_FEM_EM_TCAM_TABLE_REG(i, 1));
+ }
+ }
+}
+
+static int nbl_hw_set_ht(void *priv, u16 hash, u16 hash_other, u8 ht_table,
+ u8 bucket, u32 key_index, u8 valid)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ union nbl_fem_ht_acc_data_u ht = { .info = { 0 } };
+ union nbl_fem_ht_acc_ctrl_u ht_ctrl = { .info = { 0 } };
+ struct nbl_common_info *common;
+
+ common = NBL_HW_MGT_TO_COMMON(hw_mgt);
+
+ ht.info.vld = valid;
+ ht.info.hash = hash_other;
+ ht.info.kt_index = key_index;
+
+ ht_ctrl.info.ht_id = ht_table == NBL_HT0 ? NBL_ACC_HT0 : NBL_ACC_HT1;
+ ht_ctrl.info.entry_id = hash;
+ ht_ctrl.info.bucket_id = bucket;
+ ht_ctrl.info.port = NBL_PT_PP0;
+ ht_ctrl.info.access_size = NBL_ACC_SIZE_32B;
+ ht_ctrl.info.start = 1;
+
+ if (nbl_send_ht_data(hw_mgt, &ht_ctrl, ht.data, common))
+ return -EIO;
+
+ nbl_check_ht_data(hw_mgt, &ht_ctrl, common);
+ return 0;
+}
+
+static int nbl_hw_set_kt(void *priv, u8 *key, u32 key_index, u8 key_type)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ union nbl_fem_kt_acc_ctrl_u kt_ctrl = { .info = { 0 } };
+ struct nbl_common_info *common;
+
+ common = NBL_HW_MGT_TO_COMMON(hw_mgt);
+
+ kt_ctrl.info.addr = key_index;
+ kt_ctrl.info.access_size = key_type == NBL_KT_HALF_MODE ?
+ NBL_ACC_SIZE_160B :
+ NBL_ACC_SIZE_320B;
+ kt_ctrl.info.start = 1;
+
+ if (nbl_send_kt_data(hw_mgt, &kt_ctrl, key, common))
+ return -EIO;
+
+ nbl_check_kt_data(hw_mgt, &kt_ctrl, common);
+ return 0;
+}
+
+static int nbl_hw_search_key(void *priv, u8 *key, u8 key_type)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_common_info *common;
+ union nbl_search_ctrl_u s_ctrl = { .info = { 0 } };
+ union nbl_search_ack_u s_ack = { .info = { 0 } };
+ u8 key_data[NBL_KT_BYTE_LEN] = { 0 };
+ u8 search_key[NBL_FEM_SEARCH_KEY_LEN] = { 0 };
+ u8 data[NBL_FEM_SEARCH_KEY_LEN] = { 0 };
+ u8 times = 3;
+
+ common = NBL_HW_MGT_TO_COMMON(hw_mgt);
+
+ if (key_type == NBL_KT_HALF_MODE)
+ memcpy(key_data, key, NBL_KT_BYTE_HALF_LEN);
+ else
+ memcpy(key_data, key, NBL_KT_BYTE_LEN);
+
+ key_data[0] &= KT_MASK_LEN32_ACTION_INFO;
+ key_data[1] &= KT_MASK_LEN12_ACTION_INFO;
+ if (key_type == NBL_KT_HALF_MODE)
+ memcpy(&search_key[20], key_data, NBL_KT_BYTE_HALF_LEN);
+ else
+ memcpy(search_key, key_data, NBL_KT_BYTE_LEN);
+
+ nbl_debug(common, "Search key:0x%x-%x-%x-%x-%x-%x-%x-%x-%x-%x",
+ ((u32 *)search_key)[9], ((u32 *)search_key)[8],
+ ((u32 *)search_key)[7], ((u32 *)search_key)[6],
+ ((u32 *)search_key)[5], ((u32 *)search_key)[4],
+ ((u32 *)search_key)[3], ((u32 *)search_key)[2],
+ ((u32 *)search_key)[1], ((u32 *)search_key)[0]);
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_INSERT_SEARCH0_DATA, search_key,
+ NBL_FEM_SEARCH_KEY_LEN);
+
+ s_ctrl.info.start = 1;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_INSERT_SEARCH0_CTRL, (u8 *)&s_ctrl,
+ NBL_SEARCH_CTRL_WIDTH);
+
+ do {
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_INSERT_SEARCH0_ACK, s_ack.data,
+ NBL_SEARCH_ACK_WIDTH);
+ nbl_debug(common, "Search key ack:done:%u status:%u.",
+ s_ack.info.done, s_ack.info.status);
+
+ if (!s_ack.info.done) {
+ times--;
+ usleep_range(100, 200);
+ } else {
+ nbl_hw_rd_regs(hw_mgt, NBL_FEM_INSERT_SEARCH0_DATA,
+ data, NBL_FEM_SEARCH_KEY_LEN);
+ nbl_debug(common,
+ "Search key data:0x%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x.",
+ ((u32 *)data)[10], ((u32 *)data)[9],
+ ((u32 *)data)[8], ((u32 *)data)[7],
+ ((u32 *)data)[6], ((u32 *)data)[5],
+ ((u32 *)data)[4], ((u32 *)data)[3],
+ ((u32 *)data)[2], ((u32 *)data)[1],
+ ((u32 *)data)[0]);
+ break;
+ }
+ } while (times);
+
+ if (!times) {
+ nbl_err(common, "Search ht/kt failed.");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int nbl_hw_add_tcam(void *priv, u32 index, u8 *key, u32 *action,
+ u8 key_type, u8 pp_type)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ union fem_em_tcam_table_u tcam_table;
+ union fem_em_tcam_table_u tcam_table_second;
+ union fem_em_ad_table_u ad_table;
+
+ memset(&tcam_table, 0, sizeof(tcam_table));
+ memset(&tcam_table_second, 0, sizeof(tcam_table_second));
+ memset(&ad_table, 0, sizeof(ad_table));
+
+ memcpy(tcam_table.info.key, key, NBL_KT_BYTE_HALF_LEN);
+ tcam_table.info.key_vld = 1;
+
+ if (key_type == NBL_KT_FULL_MODE) {
+ tcam_table.info.key_size = 1;
+ memcpy(tcam_table_second.info.key, &key[5],
+ NBL_KT_BYTE_HALF_LEN);
+ tcam_table_second.info.key_vld = 1;
+ tcam_table_second.info.key_size = 1;
+
+ nbl_hw_wr_regs(hw_mgt,
+ NBL_FEM_EM_TCAM_TABLE_REG(pp_type, index + 1),
+ tcam_table_second.hash_key,
+ NBL_FLOW_TCAM_TOTAL_LEN);
+ }
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_EM_TCAM_TABLE_REG(pp_type, index),
+ tcam_table.hash_key, NBL_FLOW_TCAM_TOTAL_LEN);
+
+ ad_table.info.action0 = action[0];
+ ad_table.info.action1 = action[1];
+ ad_table.info.action2 = action[2];
+ ad_table.info.action3 = action[3];
+ ad_table.info.action4 = action[4];
+ ad_table.info.action5 = action[5];
+ ad_table.info.action6 = action[6];
+ ad_table.info.action7 = action[7];
+ ad_table.info.action8 = action[8];
+ ad_table.info.action9 = action[9];
+ ad_table.info.action10 = action[10];
+ ad_table.info.action11 = action[11];
+ ad_table.info.action12 = action[12];
+ ad_table.info.action13 = action[13];
+ ad_table.info.action14 = action[14];
+ ad_table.info.action15 = action[15];
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_EM_AD_TABLE_REG(pp_type, index),
+ ad_table.hash_key, NBL_FLOW_AD_TOTAL_LEN);
+
+ return 0;
+}
+
+static void nbl_hw_del_tcam(void *priv, u32 index, u8 key_type, u8 pp_type)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ union fem_em_tcam_table_u tcam_table;
+ union fem_em_tcam_table_u tcam_table_second;
+ union fem_em_ad_table_u ad_table;
+
+ memset(&tcam_table, 0, sizeof(tcam_table));
+ memset(&tcam_table_second, 0, sizeof(tcam_table_second));
+ memset(&ad_table, 0, sizeof(ad_table));
+ if (key_type == NBL_KT_FULL_MODE)
+ nbl_hw_wr_regs(hw_mgt,
+ NBL_FEM_EM_TCAM_TABLE_REG(pp_type, index + 1),
+ tcam_table_second.hash_key,
+ NBL_FLOW_TCAM_TOTAL_LEN);
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_EM_TCAM_TABLE_REG(pp_type, index),
+ tcam_table.hash_key, NBL_FLOW_TCAM_TOTAL_LEN);
+
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_EM_AD_TABLE_REG(pp_type, index),
+ ad_table.hash_key, NBL_FLOW_AD_TOTAL_LEN);
+}
+
+static int nbl_hw_add_mcc(void *priv, u16 mcc_id, u16 prev_mcc_id,
+ u16 next_mcc_id, u16 action)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_mcc_tbl node = { 0 };
+
+ node.vld = 1;
+ if (next_mcc_id == NBL_MCC_ID_INVALID) {
+ node.next_pntr = 0;
+ node.tail = 1;
+ } else {
+ node.next_pntr = next_mcc_id;
+ node.tail = 0;
+ }
+
+ node.stateid_filter = 1;
+ node.flowid_filter = 1;
+ node.dport_act = action;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(mcc_id), (u8 *)&node,
+ sizeof(node));
+ if (prev_mcc_id != NBL_MCC_ID_INVALID) {
+ nbl_hw_rd_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(prev_mcc_id),
+ (u8 *)&node, sizeof(node));
+ node.next_pntr = mcc_id;
+ node.tail = 0;
+ nbl_hw_wr_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(prev_mcc_id),
+ (u8 *)&node, sizeof(node));
+ }
+
+ return 0;
+}
+
+static void nbl_hw_del_mcc(void *priv, u16 mcc_id, u16 prev_mcc_id,
+ u16 next_mcc_id)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_mcc_tbl node = { 0 };
+
+ if (prev_mcc_id != NBL_MCC_ID_INVALID) {
+ nbl_hw_rd_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(prev_mcc_id),
+ (u8 *)&node, sizeof(node));
+
+ if (next_mcc_id != NBL_MCC_ID_INVALID) {
+ node.next_pntr = next_mcc_id;
+ } else {
+ node.next_pntr = 0;
+ node.tail = 1;
+ }
+
+ nbl_hw_wr_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(prev_mcc_id),
+ (u8 *)&node, sizeof(node));
+ }
+
+ memset(&node, 0, sizeof(node));
+ nbl_hw_wr_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(mcc_id), (u8 *)&node,
+ sizeof(node));
+}
+
+static void nbl_hw_update_mcc_next_node(void *priv, u16 mcc_id, u16 next_mcc_id)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_mcc_tbl node = { 0 };
+
+ nbl_hw_rd_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(mcc_id), (u8 *)&node,
+ sizeof(node));
+ if (next_mcc_id != NBL_MCC_ID_INVALID) {
+ node.next_pntr = next_mcc_id;
+ node.tail = 0;
+ } else {
+ node.next_pntr = 0;
+ node.tail = 1;
+ }
+
+ nbl_hw_wr_regs(hw_mgt, NBL_MCC_LEAF_NODE_TABLE(mcc_id), (u8 *)&node,
+ sizeof(node));
+}
+
+static int nbl_hw_init_fem(void *priv)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ union nbl_fem_ht_size_table_u ht_size = { .info = { 0 } };
+ u32 fem_start = NBL_FEM_INIT_START_KERN;
+ int ret = 0;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_INIT_START, (u8 *)&fem_start,
+ sizeof(fem_start));
+
+ nbl_hw_fem_set_bank(hw_mgt);
+
+ ht_size.info.pp0_size = HT_PORT0_BTM;
+ ht_size.info.pp1_size = HT_PORT1_BTM;
+ ht_size.info.pp2_size = HT_PORT2_BTM;
+ nbl_hw_wr_regs(hw_mgt, NBL_FEM_HT_SIZE_REG, ht_size.data,
+ NBL_FEM_HT_SIZE_TBL_WIDTH);
+
+ nbl_hw_fem_clear_tcam_ad(hw_mgt);
+
+ return ret;
+}
+
static void nbl_configure_dped_checksum(struct nbl_hw_mgt *hw_mgt)
{
union dped_l4_ck_cmd_40_u l4_ck_cmd_40;
@@ -2007,6 +2468,20 @@ static void nbl_hw_set_coalesce(void *priv, u16 interrupt_id, u16 pnum,
(u8 *)&msix_info, sizeof(msix_info));
}
+static int nbl_hw_set_vsi_mtu(void *priv, u16 vsi_id, u16 mtu_sel)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_ipro_dn_src_port_tbl dpsport = { 0 };
+
+ nbl_hw_rd_regs(hw_mgt, NBL_IPRO_DN_SRC_PORT_TABLE(vsi_id),
+ (u8 *)&dpsport, sizeof(struct nbl_ipro_dn_src_port_tbl));
+ dpsport.mtu_sel = mtu_sel;
+ nbl_hw_wr_regs(hw_mgt, NBL_IPRO_DN_SRC_PORT_TABLE(vsi_id),
+ (u8 *)&dpsport, sizeof(struct nbl_ipro_dn_src_port_tbl));
+
+ return 0;
+}
+
static void nbl_hw_config_adminq_rxq(void *priv, dma_addr_t dma_addr,
int size_bwid)
{
@@ -2172,6 +2647,36 @@ static void nbl_hw_set_fw_pong(void *priv, u32 pong)
sizeof(pong));
}
+static int nbl_hw_set_mtu(void *priv, u16 mtu_index, u16 mtu)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_ipro_mtu_sel ipro_mtu_sel = { 0 };
+
+ nbl_hw_rd_regs(hw_mgt, NBL_IPRO_MTU_SEL_REG(mtu_index / 2),
+ (u8 *)&ipro_mtu_sel, sizeof(ipro_mtu_sel));
+
+ if (mtu_index % 2 == 0)
+ ipro_mtu_sel.mtu_0 = mtu;
+ else
+ ipro_mtu_sel.mtu_1 = mtu;
+
+ nbl_hw_wr_regs(hw_mgt, NBL_IPRO_MTU_SEL_REG(mtu_index / 2),
+ (u8 *)&ipro_mtu_sel, sizeof(ipro_mtu_sel));
+
+ return 0;
+}
+
+static u16 nbl_hw_get_mtu_index(void *priv, u16 vsi_id)
+{
+ struct nbl_hw_mgt *hw_mgt = (struct nbl_hw_mgt *)priv;
+ struct nbl_ipro_dn_src_port_tbl ipro_dn_src_port_tbl = { 0 };
+
+ nbl_hw_rd_regs(hw_mgt, NBL_IPRO_DN_SRC_PORT_TBL_REG(vsi_id),
+ (u8 *)&ipro_dn_src_port_tbl,
+ sizeof(ipro_dn_src_port_tbl));
+ return ipro_dn_src_port_tbl.mtu_sel;
+}
+
static int nbl_hw_process_abnormal_queue(struct nbl_hw_mgt *hw_mgt,
u16 queue_id, int type,
struct nbl_abnormal_details *detail)
@@ -2431,9 +2936,23 @@ static struct nbl_hw_ops hw_ops = {
.save_uvn_ctx = nbl_hw_save_uvn_ctx,
.setup_queue_switch = nbl_hw_setup_queue_switch,
.init_pfc = nbl_hw_init_pfc,
+ .set_vsi_mtu = nbl_hw_set_vsi_mtu,
+ .set_mtu = nbl_hw_set_mtu,
+ .get_mtu_index = nbl_hw_get_mtu_index,
+
.configure_msix_map = nbl_hw_configure_msix_map,
.configure_msix_info = nbl_hw_configure_msix_info,
.set_coalesce = nbl_hw_set_coalesce,
+
+ .set_ht = nbl_hw_set_ht,
+ .set_kt = nbl_hw_set_kt,
+ .search_key = nbl_hw_search_key,
+ .add_tcam = nbl_hw_add_tcam,
+ .del_tcam = nbl_hw_del_tcam,
+ .add_mcc = nbl_hw_add_mcc,
+ .del_mcc = nbl_hw_del_mcc,
+ .update_mcc_next_node = nbl_hw_update_mcc_next_node,
+ .init_fem = nbl_hw_init_fem,
.update_mailbox_queue_tail_ptr = nbl_hw_update_mailbox_queue_tail_ptr,
.config_mailbox_rxq = nbl_hw_config_mailbox_rxq,
.config_mailbox_txq = nbl_hw_config_mailbox_txq,
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
index 161ba88a61c0..010a4c1363ed 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.c
@@ -550,9 +550,14 @@ static int nbl_res_setup_ops(struct device *dev,
return -ENOMEM;
if (!is_ops_inited) {
+ ret = nbl_flow_setup_ops_leonis(&res_ops);
+ if (ret)
+ goto setup_fail;
+
ret = nbl_queue_setup_ops_leonis(&res_ops);
if (ret)
goto setup_fail;
+
ret = nbl_intr_setup_ops(&res_ops);
if (ret)
goto setup_fail;
@@ -884,6 +889,7 @@ static void nbl_res_stop(struct nbl_resource_mgt_leonis *res_mgt_leonis)
nbl_intr_mgt_stop(res_mgt);
nbl_adminq_mgt_stop(res_mgt);
nbl_vsi_mgt_stop(res_mgt);
+ nbl_flow_mgt_stop_leonis(res_mgt);
nbl_res_ctrl_dev_ustore_stats_remove(res_mgt);
nbl_res_ctrl_dev_remove_vsi_info(res_mgt);
nbl_res_ctrl_dev_remove_eth_info(res_mgt);
@@ -936,6 +942,10 @@ static int nbl_res_start(struct nbl_resource_mgt_leonis *res_mgt_leonis,
if (ret)
goto start_fail;
+ ret = nbl_flow_mgt_start_leonis(res_mgt);
+ if (ret)
+ goto start_fail;
+
ret = nbl_queue_mgt_start(res_mgt);
if (ret)
goto start_fail;
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
index 3763c33db00f..a486d2e64626 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_hw/nbl_hw_leonis/nbl_resource_leonis.h
@@ -11,6 +11,9 @@
#define NBL_MAX_PF_LEONIS 8
+int nbl_flow_mgt_start_leonis(struct nbl_resource_mgt *res_mgt);
+void nbl_flow_mgt_stop_leonis(struct nbl_resource_mgt *res_mgt);
+int nbl_flow_setup_ops_leonis(struct nbl_resource_ops *resource_ops);
int nbl_queue_setup_ops_leonis(struct nbl_resource_ops *resource_ops);
void nbl_queue_remove_ops_leonis(struct nbl_resource_ops *resource_ops);
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
index 853bb3022e51..c52a17acc4f3 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_common.h
@@ -10,6 +10,93 @@
#include <linux/netdev_features.h>
#include "nbl_include.h"
+#define NBL_HASH_CFT_MAX 4
+#define NBL_HASH_CFT_AVL 2
+
+#define NBL_CRC16_CCITT(data, size) \
+ nbl_calc_crc16(data, size, 0x1021, 0x0000, 1, 0x0000)
+#define NBL_CRC16_CCITT_FALSE(data, size) \
+ nbl_calc_crc16(data, size, 0x1021, 0xFFFF, 0, 0x0000)
+#define NBL_CRC16_XMODEM(data, size) \
+ nbl_calc_crc16(data, size, 0x1021, 0x0000, 0, 0x0000)
+#define NBL_CRC16_IBM(data, size) \
+ nbl_calc_crc16(data, size, 0x8005, 0x0000, 1, 0x0000)
+
+static inline u8 nbl_invert_uint8(const u8 data)
+{
+ u8 i, result = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (data & (1 << i))
+ result |= 1 << (7 - i);
+ }
+
+ return result;
+}
+
+static inline u16 nbl_invert_uint16(const u16 data)
+{
+ u16 i, result = 0;
+
+ for (i = 0; i < 16; i++) {
+ if (data & (1 << i))
+ result |= 1 << (15 - i);
+ }
+
+ return result;
+}
+
+static inline u16 nbl_calc_crc16(const u8 *data, u32 size, u16 crc_poly,
+ u16 init_value, u8 ref_flag, u16 xorout)
+{
+ u16 crc_reg = init_value, tmp = 0;
+ u8 j, byte = 0;
+
+ while (size--) {
+ byte = *(data++);
+ if (ref_flag)
+ byte = nbl_invert_uint8(byte);
+ crc_reg ^= byte << 8;
+ for (j = 0; j < 8; j++) {
+ tmp = crc_reg & 0x8000;
+ crc_reg <<= 1;
+ if (tmp)
+ crc_reg ^= crc_poly;
+ }
+ }
+
+ if (ref_flag)
+ crc_reg = nbl_invert_uint16(crc_reg);
+
+ crc_reg = crc_reg ^ xorout;
+ return crc_reg;
+}
+
+static inline u16 nbl_hash_transfer(u16 hash, u16 power, u16 depth)
+{
+ u16 temp = 0;
+ u16 val = 0;
+ u32 val2 = 0;
+ u16 off = 16 - power;
+
+ temp = (hash >> power);
+ val = hash << off;
+ val = val >> off;
+
+ if (depth == 0) {
+ val = temp + val;
+ val = val << off;
+ val = val >> off;
+ } else {
+ val2 = val;
+ val2 *= depth;
+ val2 = val2 >> power;
+ val = (u16)val2;
+ }
+
+ return val;
+}
+
#define nbl_err(common, fmt, ...) \
do { \
typeof(common) _common = (common); \
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
index b8f49cc75bc8..e2c5a865892f 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_hw.h
@@ -105,10 +105,28 @@ struct nbl_hw_ops {
void (*update_adminq_queue_tail_ptr)(void *priv, u16 tail_ptr, u8 txrx);
bool (*check_adminq_dma_err)(void *priv, bool tx);
+ int (*set_vsi_mtu)(void *priv, u16 vsi_id, u16 mtu_sel);
+
u8 __iomem *(*get_hw_addr)(void *priv, size_t *size);
int (*set_sfp_state)(void *priv, u8 eth_id, u8 state);
void (*set_hw_status)(void *priv, enum nbl_hw_status hw_status);
enum nbl_hw_status (*get_hw_status)(void *priv);
+ int (*set_mtu)(void *priv, u16 mtu_index, u16 mtu);
+ u16 (*get_mtu_index)(void *priv, u16 vsi_id);
+
+ int (*set_ht)(void *priv, u16 hash, u16 hash_other, u8 ht_table,
+ u8 bucket, u32 key_index, u8 valid);
+ int (*set_kt)(void *priv, u8 *key, u32 key_index, u8 key_type);
+ int (*search_key)(void *priv, u8 *key, u8 key_type);
+ int (*add_tcam)(void *priv, u32 index, u8 *key, u32 *action,
+ u8 key_type, u8 pp_type);
+ void (*del_tcam)(void *priv, u32 index, u8 key_type, u8 pp_type);
+ int (*add_mcc)(void *priv, u16 mcc_id, u16 prev_mcc_id, u16 next_mcc_id,
+ u16 action);
+ void (*del_mcc)(void *priv, u16 mcc_id, u16 prev_mcc_id,
+ u16 next_mcc_id);
+ void (*update_mcc_next_node)(void *priv, u16 mcc_id, u16 next_mcc_id);
+ int (*init_fem)(void *priv);
void (*set_fw_ping)(void *priv, u32 ping);
u32 (*get_fw_pong)(void *priv);
void (*set_fw_pong)(void *priv, u32 pong);
--
2.47.3
Powered by blists - more mailing lists