diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index f055d8e93bc49240da646fff336def76a6b694ff..cd75e62e7537599cfdfd9a3f3229395438517af3 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -2757,6 +2757,7 @@ CONFIG_HNS3_HCLGE=m CONFIG_HNS3_DCB=y CONFIG_HNS3_HCLGEVF=m CONFIG_HNS3_ENET=m +CONFIG_HNS3_UBL=y CONFIG_NET_VENDOR_HUAWEI=y CONFIG_HINIC=m CONFIG_HINIC3=m @@ -2903,6 +2904,7 @@ CONFIG_NET_VENDOR_NEBULA_MATRIX=y CONFIG_M1600=m # CONFIG_FDDI is not set # CONFIG_HIPPI is not set +CONFIG_UBL=y # CONFIG_NET_SB1000 is not set CONFIG_PHYLIB=y CONFIG_SWPHY=y diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f208080243055fe965b6586f624cc91e4496a0ef..f3ac803d05c0c8352df94dc5a0b5479c245e543c 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -444,6 +444,8 @@ source "drivers/net/fddi/Kconfig" source "drivers/net/hippi/Kconfig" +source "drivers/net/ub/Kconfig" + source "drivers/net/ipa/Kconfig" config NET_SB1000 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 72e18d505d1acf73f540a7cad2a6c691784c81af..8aca829a3e870a272b3160ad612fe941cb2e44a6 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_WLAN) += wireless/ obj-$(CONFIG_WIMAX) += wimax/ obj-$(CONFIG_IEEE802154) += ieee802154/ +obj-$(CONFIG_UBL) += ub/ obj-$(CONFIG_VMXNET3) += vmxnet3/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index 2ba0e7bd34666c72924a915463a425ada8acabc7..df684d97bc0c5c42cfbe94d105a3a88a1082e214 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -139,6 +139,16 @@ config HNS3_ENET family of SoCs. This module depends upon HNAE3 driver to access the HNAE3 devices and their associated operations. +config HNS3_UBL + bool "Hisilicon HNS3 UB Link Device Support" + default n + depends on UBL + help + This selects the HNS UBL support which depends upon configuration UBL. + It is needed by Hisilicon Network Subsystem 3 to use the UB Link and + its associated operations. + Say N if using non-UB device and network. + endif #HNS3 endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile index 53e1aa50ae8e4dbc91564fac22bbc10decbeaee8..bd5aea7dd4904faea75015f5b15a92fca8d44d8e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/Makefile +++ b/drivers/net/ethernet/hisilicon/hns3/Makefile @@ -4,6 +4,7 @@ # ccflags-y += -I$(srctree)/$(src) +ccflags-y += -I$(srctree)/drivers/net/ub/dev ccflags-y += -I$(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3pf ccflags-y += -I$(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3vf ccflags-y += -I$(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3_common @@ -15,16 +16,23 @@ hns3-objs = hns3_enet.o hns3_ethtool.o hns3_debugfs.o hns3-objs += hns3_ext.o hns3-$(CONFIG_HNS3_DCB) += hns3_dcbnl.o +hns3-$(CONFIG_HNS3_UBL) += hns3_unic.o hns3_unic_debugfs.o obj-$(CONFIG_HNS3_HCLGEVF) += hclgevf.o hclgevf-objs = hns3vf/hclgevf_main.o hns3vf/hclgevf_mbx.o hns3vf/hclgevf_devlink.o hns3vf/hclgevf_regs.o \ - hns3_common/hclge_comm_cmd.o hns3_common/hclge_comm_rss.o hns3_common/hclge_comm_tqp_stats.o + hns3_common/hclge_comm_cmd.o hns3_common/hclge_comm_rss.o hns3_common/hclge_comm_tqp_stats.o \ + hns3vf/hclgevf_udma.o +hclgevf-$(CONFIG_HNS3_UBL) += hns3_common/hclge_comm_unic_addr.o hns3vf/hclgevf_unic_ip.o hns3vf/hclgevf_unic_guid.o \ + hns3vf/hclgevf_unic_addr.o obj-$(CONFIG_HNS3_HCLGE) += hclge.o hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hclge_sysfs.o hns3pf/hclge_regs.o \ hns3pf/hclge_mbx.o hns3pf/hclge_err.o hns3pf/hclge_debugfs.o hns3pf/hclge_ptp.o hns3pf/hclge_devlink.o \ - hns3_common/hclge_comm_cmd.o hns3_common/hclge_comm_rss.o hns3_common/hclge_comm_tqp_stats.o + hns3_common/hclge_comm_cmd.o hns3_common/hclge_comm_rss.o hns3_common/hclge_comm_tqp_stats.o \ + hns3pf/hclge_udma.o hclge-objs += hns3pf/hclge_ext.o +hclge-$(CONFIG_HNS3_UBL) += hns3_common/hclge_comm_unic_addr.o hns3pf/hclge_unic_ip.o hns3pf/hclge_unic_guid.o \ + hns3pf/hclge_unic_addr.o hclge-$(CONFIG_HNS3_DCB) += hns3pf/hclge_dcb.o diff --git a/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h b/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h index 0de9b83c9d4ecfd32b21c2639112e651ba7e1378..14e830ff18924058a49f968e2c8a9d6feed3dbff 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h +++ b/drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h @@ -50,6 +50,9 @@ enum HCLGE_MBX_OPCODE { HCLGE_MBX_SET_QB = 0x28, /* (VF -> PF) set queue bonding */ HCLGE_MBX_PUSH_QB_STATE, /* (PF -> VF) push qb state */ + HCLGE_MBX_SET_MGUID = 0x50, /* (VF -> PF) set mc guid */ + HCLGE_UNIC_MBX_SET_IP, /* (VF -> PF) set ip addr */ + HCLGE_MBX_GET_VF_FLR_STATUS = 200, /* (M7 -> PF) get vf flr status */ HCLGE_MBX_PUSH_LINK_STATUS, /* (M7 -> PF) get port link status */ HCLGE_MBX_NCSI_ERROR, /* (M7 -> PF) receive a NCSI error */ @@ -65,6 +68,12 @@ enum hclge_mbx_mac_vlan_subcode { HCLGE_MBX_MAC_VLAN_MC_REMOVE, /* remove MC mac addr */ }; +/* below are per-VF ip table subcodes */ +enum hclge_mbx_ip_table_subcode { + HCLGE_UNIC_MBX_IP_TABLE_ADD = 0, /* add ip addr */ + HCLGE_UNIC_MBX_IP_TABLE_REMOVE, /* remove ip addr */ +}; + /* below are per-VF vlan cfg subcodes */ enum hclge_mbx_vlan_cfg_subcode { HCLGE_MBX_VLAN_FILTER = 0, /* set vlan filter */ @@ -75,6 +84,12 @@ enum hclge_mbx_vlan_cfg_subcode { HCLGE_MBX_ENABLE_VLAN_FILTER, }; +/* below are per-VF mc guid subcodes */ +enum hclge_mbx_mc_guid_subcode { + HCLGE_MBX_MC_GUID_MC_ADD = 0, /* add new MC guid addr */ + HCLGE_MBX_MC_GUID_MC_DELETE, /* delete MC guid addr */ +}; + enum hclge_mbx_tbl_cfg_subcode { HCLGE_MBX_VPORT_LIST_CLEAR, }; diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.c b/drivers/net/ethernet/hisilicon/hns3/hnae3.c index ee4b7f5910b1fa3d97405c2c350da0acce483397..82ce3f5744a0d691a0725c9e9137a3d9fea5c6ae 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.c +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.c @@ -41,7 +41,8 @@ static bool hnae3_client_match(enum hnae3_client_type client_type) { if (client_type == HNAE3_CLIENT_KNIC || client_type == HNAE3_CLIENT_ROCE || - client_type == HNAE3_CLIENT_ROH) + client_type == HNAE3_CLIENT_ROH || + client_type == HNAE3_CLIENT_UDMA) return true; return false; @@ -64,6 +65,9 @@ void hnae3_set_client_init_flag(struct hnae3_client *client, case HNAE3_CLIENT_ROH: hnae3_set_bit(ae_dev->flag, HNAE3_ROH_CLIENT_INITED_B, inited); break; + case HNAE3_CLIENT_UDMA: + hnae3_set_bit(ae_dev->flag, HNAE3_UDMA_CLIENT_INITED_B, inited); + break; default: break; } @@ -88,6 +92,10 @@ static int hnae3_get_client_init_flag(struct hnae3_client *client, inited = hnae3_get_bit(ae_dev->flag, HNAE3_ROH_CLIENT_INITED_B); break; + case HNAE3_CLIENT_UDMA: + inited = hnae3_get_bit(ae_dev->flag, + HNAE3_UDMA_CLIENT_INITED_B); + break; default: break; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index 95c855bac7a006be6bcb5ae18d3364e38d3644d7..0d6137ed357b331a7aa2345e7141bbd5b9cc6b89 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -58,6 +58,12 @@ #define HNAE3_DEV_ID_400G_ROH 0xA22D #define HNAE3_DEV_ID_VF 0xA22E #define HNAE3_DEV_ID_RDMA_DCB_PFC_VF 0xA22F +#define HNAE3_DEV_ID_UDMA_OVER_UBL 0xA260 +#define HNAE3_DEV_ID_UDMA 0xA261 +#define HNAE3_DEV_ID_RDMA_OVER_UBL 0xA262 +#define HNAE3_DEV_ID_UDMA_OVER_UBL_VF 0xA268 +#define HNAE3_DEV_ID_UDMA_VF 0xA269 +#define HNAE3_DEV_ID_RDMA_OVER_UBL_VF 0xA26A #define HNAE3_CLASS_NAME_SIZE 16 @@ -68,10 +74,30 @@ #define HNAE3_UNIC_CLIENT_INITED_B 0x4 #define HNAE3_ROCE_CLIENT_INITED_B 0x5 #define HNAE3_ROH_CLIENT_INITED_B 0x6 +#define HNAE3_UDMA_CLIENT_INITED_B 0x7 +#define HNAE3_DEV_SUPPORT_UDMA_B 0x8 +#define HNAE3_DEV_SUPPORT_UBL_B 0x9 #define HNAE3_DEV_SUPPORT_ROCE_DCB_BITS (BIT(HNAE3_DEV_SUPPORT_DCB_B) | \ BIT(HNAE3_DEV_SUPPORT_ROCE_B)) +#define HNAE3_DEV_SUPPORT_UDMA_OVER_UBL_DCB_BITS \ + (BIT(HNAE3_DEV_SUPPORT_DCB_B) | BIT(HNAE3_DEV_SUPPORT_UDMA_B) | \ + BIT(HNAE3_DEV_SUPPORT_UBL_B)) + +#define HNAE3_DEV_SUPPORT_ROCE_OVER_UBL_DCB_BITS \ + (BIT(HNAE3_DEV_SUPPORT_DCB_B) | BIT(HNAE3_DEV_SUPPORT_ROCE_B) | \ + BIT(HNAE3_DEV_SUPPORT_UBL_B)) + +#define HNAE3_DEV_SUPPORT_UDMA_DCB_BITS \ + (BIT(HNAE3_DEV_SUPPORT_DCB_B) | BIT(HNAE3_DEV_SUPPORT_UDMA_B)) + +#define hnae3_dev_udma_supported(ae_dev) \ + hnae3_get_bit((ae_dev)->flag, HNAE3_DEV_SUPPORT_UDMA_B) + +#define hnae3_dev_ubl_supported(ae_dev) \ + hnae3_get_bit((ae_dev)->flag, HNAE3_DEV_SUPPORT_UBL_B) + #define hnae3_dev_roh_supported(hdev) \ hnae3_get_bit((hdev)->ae_dev->flag, HNAE3_ROH_CLIENT_INITED_B) @@ -226,6 +252,7 @@ enum hnae3_client_type { HNAE3_CLIENT_KNIC, HNAE3_CLIENT_ROCE, HNAE3_CLIENT_ROH, + HNAE3_CLIENT_UDMA, }; enum hnae3_mac_type { @@ -344,6 +371,10 @@ enum hnae3_dbg_cmd { HNAE3_DBG_CMD_PAGE_POOL_INFO, HNAE3_DBG_CMD_COAL_INFO, HNAE3_DBG_CMD_WOL_INFO, + HNAE3_DBG_CMD_IP_SPEC, + HNAE3_DBG_CMD_GUID_SPEC, + HNAE3_DBG_CMD_IP_LIST, + HNAE3_DBG_CMD_GUID_LIST, HNAE3_DBG_CMD_UNKNOWN, }; @@ -400,6 +431,8 @@ struct hnae3_dev_specs { u16 mc_mac_size; u32 mac_stats_num; u8 tnl_num; + u16 guid_tbl_space; + u16 ip_tbl_space; }; struct hnae3_client_ops { @@ -434,6 +467,11 @@ struct hnae3_ae_dev { void *priv; }; +enum hnae3_unic_addr_type { + HNAE3_UNIC_IP_ADDR, + HNAE3_UNIC_MCGUID_ADDR +}; + /* This struct defines the operation on the handle. * * init_ae_dev(): (mandatory) @@ -799,6 +837,14 @@ struct hnae3_ae_ops { struct ethtool_wolinfo *wol); int (*priv_ops)(struct hnae3_handle *handle, int opcode, void *data, size_t length); + int (*add_addr)(struct hnae3_handle *handle, + const unsigned char *addr, + enum hnae3_unic_addr_type addr_type); + int (*rm_addr)(struct hnae3_handle *handle, + const unsigned char *addr, + enum hnae3_unic_addr_type addr_type); + int (*get_func_guid)(struct hnae3_handle *handle, u8 *guid); + int (*set_func_guid)(struct hnae3_handle *handle, u8 *guid); }; struct hnae3_dcb_ops { @@ -887,6 +933,17 @@ struct hnae3_roh_private_info { unsigned long reset_state; }; +struct hnae3_udma_private_info { + struct net_device *netdev; + void __iomem *udma_io_base; + void __iomem *udma_mem_base; + int base_vector; + int num_vectors; + unsigned long reset_state; + unsigned long instance_state; + unsigned long state; +}; + #define HNAE3_SUPPORT_APP_LOOPBACK BIT(0) #define HNAE3_SUPPORT_PHY_LOOPBACK BIT(1) #define HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK BIT(2) @@ -899,7 +956,8 @@ struct hnae3_roh_private_info { #define HNAE3_BPE BIT(2) /* broadcast promisc enable */ #define HNAE3_OVERFLOW_UPE BIT(3) /* unicast mac vlan overflow */ #define HNAE3_OVERFLOW_MPE BIT(4) /* multicast mac vlan overflow */ -#define HNAE3_UPE (HNAE3_USER_UPE | HNAE3_OVERFLOW_UPE) +#define HNAE3_OVERFLOW_MGP BIT(5) /* multicast guid overflow */ +#define HNAE3_UPE (HNAE3_USER_UPE | HNAE3_OVERFLOW_UPE | HNAE3_OVERFLOW_MGP) #define HNAE3_MPE (HNAE3_USER_MPE | HNAE3_OVERFLOW_MPE) enum hnae3_pflag { @@ -921,6 +979,7 @@ struct hnae3_handle { struct hnae3_knic_private_info kinfo; struct hnae3_roce_private_info rinfo; struct hnae3_roh_private_info rohinfo; + struct hnae3_udma_private_info udmainfo; }; u32 numa_node_mask; /* for multi-chip support */ @@ -932,6 +991,7 @@ struct hnae3_handle { /* protects concurrent contention between debugfs commands */ struct mutex dbgfs_lock; char **dbgfs_buf; + char **ub_dbgfs_buf; /* Network interface message level enabled bits */ u32 msg_enable; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h index 8b2544f1b4a0466aaa07d76961ddb72a1c9c292b..9f47d86c37f1abe48051a2a90669374f8fa53d75 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_cmd.h @@ -310,6 +310,13 @@ enum hclge_opcode_type { /* Query link diagnosis info command */ HCLGE_OPC_QUERY_LINK_DIAGNOSIS = 0x702A, + + /* UB commands */ + HCLGE_OPC_COMM_GET_FUNC_GUID = 0xA001, + HCLGE_OPC_ADD_IP_TBL = 0xA100, + HCLGE_OPC_DEL_IP_TBL = 0xA101, + HCLGE_OPC_COMM_CFG_FUNC_GUID = 0xA122, + HCLGE_OPC_CFG_MC_GUID_CMD = 0xA123, }; enum hclge_comm_cmd_return_status { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_unic_addr.c b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_unic_addr.c new file mode 100644 index 0000000000000000000000000000000000000000..136ec25d2c4540b95e735ec8476a5b482df85b48 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_unic_addr.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include "hclge_comm_unic_addr.h" + +static struct hclge_comm_unic_addr_node * +hclge_comm_unic_find_addr_node(struct list_head *list, const u8 *addr) +{ + struct hclge_comm_unic_addr_node *addr_node, *tmp; + + list_for_each_entry_safe(addr_node, tmp, list, node) + if (hclge_comm_unic_addr_equal((const u8 *)&addr_node->unic_addr, + addr)) + return addr_node; + + return NULL; +} + +static void +hclge_comm_unic_update_addr_node(struct hclge_comm_unic_addr_node *addr_node, + enum HCLGE_COMM_ADDR_NODE_STATE state) +{ + switch (state) { + /* from set_rx_mode or tmp_add_list */ + case HCLGE_COMM_UNIC_ADDR_TO_ADD: + if (addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_DEL) + addr_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + break; + /* only from set_rx_mode */ + case HCLGE_COMM_UNIC_ADDR_TO_DEL: + if (addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) { + list_del(&addr_node->node); + kfree(addr_node); + } else { + addr_node->state = HCLGE_COMM_UNIC_ADDR_TO_DEL; + } + break; + /* only from tmp_add_list, the addr_node->state won't be + * ACTIVE. + */ + case HCLGE_COMM_UNIC_ADDR_ACTIVE: + if (addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) + addr_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + break; + } +} + +void hclge_comm_unic_sync_from_addr_del_list(struct list_head *del_list, + struct list_head *addr_list) +{ + struct hclge_comm_unic_addr_node *addr_node, *tmp, *new_node; + + list_for_each_entry_safe(addr_node, tmp, del_list, node) { + new_node = hclge_comm_unic_find_addr_node(addr_list, + addr_node->unic_addr); + if (new_node) { + /* If the addr exists in the addr list, it means + * received a new TO_ADD request during the time window + * of configuring the addr, so we just need + * to change the addr node state to ACTIVE. + */ + new_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + list_del(&addr_node->node); + kfree(addr_node); + } else { + list_move_tail(&addr_node->node, addr_list); + } + } +} + +static void +hclge_comm_unic_sync_from_addr_add_list(struct list_head *add_list, + struct list_head *addr_list, + bool *all_added) +{ + struct hclge_comm_unic_addr_node *addr_node, *tmp, *new_node; + + list_for_each_entry_safe(addr_node, tmp, add_list, node) { + if (*all_added && + addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) + *all_added = false; + + /* If the addr from tmp_add_list is not in the + * addr_list, it means have received a TO_DEL request + * during the time window of adding the addr. If addr_node + * state is ACTIVE, then change its state to TO_DEL, + * then it will be removed at next time. If is TO_ADD, + * it means this address hasn't been added successfully, + * so just remove the addr node. + */ + new_node = hclge_comm_unic_find_addr_node(addr_list, + addr_node->unic_addr); + if (new_node) { + hclge_comm_unic_update_addr_node(new_node, + addr_node->state); + list_del(&addr_node->node); + kfree(addr_node); + } else if (addr_node->state == HCLGE_COMM_UNIC_ADDR_ACTIVE) { + addr_node->state = HCLGE_COMM_UNIC_ADDR_TO_DEL; + list_move_tail(&addr_node->node, addr_list); + } else { + list_del(&addr_node->node); + kfree(addr_node); + } + } +} + +int hclge_comm_unic_update_addr_list(struct list_head *list, + spinlock_t *addr_list_lock, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const unsigned char *addr) +{ + struct hclge_comm_unic_addr_node *addr_node; + + spin_lock_bh(addr_list_lock); + + /* if the addr is already in the addr list, no need to add a new + * one into it, just check the addr state, convert it to a new + * state, or just remove it, or do nothing. + */ + addr_node = hclge_comm_unic_find_addr_node(list, addr); + if (addr_node) { + hclge_comm_unic_update_addr_node(addr_node, state); + spin_unlock_bh(addr_list_lock); + return 0; + } + + /* if this addr is never added, unnecessary to delete */ + if (state == HCLGE_COMM_UNIC_ADDR_TO_DEL) { + spin_unlock_bh(addr_list_lock); + return -ENOENT; + } + + addr_node = kzalloc(sizeof(*addr_node), GFP_ATOMIC); + if (!addr_node) { + spin_unlock_bh(addr_list_lock); + return -ENOMEM; + } + + addr_node->state = state; + memcpy(addr_node->unic_addr, addr, UNIC_ADDR_LEN); + list_add_tail(&addr_node->node, list); + + spin_unlock_bh(addr_list_lock); + + return 0; +} + +bool hclge_comm_unic_sync_addr_table(struct hnae3_handle *handle, + struct list_head *list, + spinlock_t *addr_list_lock, + void (*sync)(struct hnae3_handle *, + struct list_head *), + void (*unsync)(struct hnae3_handle *, + struct list_head *)) +{ + struct hclge_comm_unic_addr_node *addr_node, *tmp, *new_node; + struct list_head tmp_add_list, tmp_del_list; + bool all_added = true; + + INIT_LIST_HEAD(&tmp_add_list); + INIT_LIST_HEAD(&tmp_del_list); + + /* move the addr to the tmp_add_list and tmp_del_list, then + * we can add/delete these addr outside the spin lock + */ + spin_lock_bh(addr_list_lock); + + list_for_each_entry_safe(addr_node, tmp, list, node) { + switch (addr_node->state) { + case HCLGE_COMM_UNIC_ADDR_TO_DEL: + list_move_tail(&addr_node->node, &tmp_del_list); + break; + case HCLGE_COMM_UNIC_ADDR_TO_ADD: + new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC); + if (!new_node) + goto stop_traverse; + memcpy(new_node->unic_addr, addr_node->unic_addr, + UNIC_ADDR_LEN); + new_node->state = addr_node->state; + list_add_tail(&new_node->node, &tmp_add_list); + break; + default: + break; + } + } + +stop_traverse: + spin_unlock_bh(addr_list_lock); + + /* delete first, in order to get max addr table space for adding */ + if (unsync) + unsync(handle, &tmp_del_list); + if (sync) + sync(handle, &tmp_add_list); + + /* if some addr were added/deleted fail, move back to the + * addr_list, and retry at next time. + */ + spin_lock_bh(addr_list_lock); + + hclge_comm_unic_sync_from_addr_del_list(&tmp_del_list, list); + hclge_comm_unic_sync_from_addr_add_list(&tmp_add_list, list, + &all_added); + + spin_unlock_bh(addr_list_lock); + + return all_added; +} + +int hclge_comm_unic_convert_ip_addr(const struct sockaddr *addr, + struct in6_addr *ip_addr) +{ + __be32 v4addr; + + switch (addr->sa_family) { + case AF_INET: + /* we transform ipv4 addr to ipv6 addr for later configuring */ + v4addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + ipv6_addr_set_v4mapped(v4addr, ip_addr); + break; + case AF_INET6: + memcpy(ip_addr, &((struct sockaddr_in6 *)addr)->sin6_addr, + sizeof(struct in6_addr)); + break; + default: + return -EINVAL; + } + return 0; +} + +static void +hclge_comm_unic_func_guid_cmd_prepare(u8 *guid, + struct hclge_comm_func_guid_cmd *req) +{ + req->entry_vld = HCLGE_COMM_FUNC_GUID_ENTRY_VALID_EN; + memcpy(req->guid, guid, UBL_ALEN); +} + +int hclge_comm_unic_set_func_guid(struct hclge_comm_hw *hw, u8 *guid) +{ + struct hclge_comm_func_guid_cmd *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_comm_func_guid_cmd *)desc.data; + + hclge_comm_cmd_setup_basic_desc(&desc, HCLGE_OPC_COMM_CFG_FUNC_GUID, + false); + hclge_comm_unic_func_guid_cmd_prepare(guid, req); + + ret = hclge_comm_cmd_send(hw, &desc, 1); + if (ret) + dev_err(&hw->cmq.csq.pdev->dev, + "failed to set guid for cmd_send, ret = %d\n", ret); + + return ret; +} + +void hclge_comm_unic_rm_func_guid(struct hclge_comm_hw *hw) +{ + struct hclge_comm_func_guid_cmd *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_comm_func_guid_cmd *)desc.data; + + hclge_comm_cmd_setup_basic_desc(&desc, HCLGE_OPC_COMM_CFG_FUNC_GUID, + false); + req->entry_vld = 0; + ret = hclge_comm_cmd_send(hw, &desc, 1); + if (ret) + dev_warn(&hw->cmq.csq.pdev->dev, + "failed to delete func guid for cmd_send, ret = %d.\n", + ret); +} + +static bool hclge_comm_unic_is_valid_func_guid(u8 *guid) +{ + u8 invalid_guid_zero[UBL_ALEN] = {0}; + u8 invalid_guid_all_one[UBL_ALEN]; + + memset(invalid_guid_all_one, 0xff, UBL_ALEN); + if (!(memcmp(guid, invalid_guid_all_one, HCLGE_COMM_MGUID_PREFIX_LEN) && + memcmp(guid, invalid_guid_zero, UBL_ALEN))) + return false; + + return true; +} + +static void hclge_comm_unic_guid_le_to_net_trans(u8 *src_guid, u8 *dest_guid) +{ + int i; + + for (i = 0; i < UBL_ALEN; i++) + dest_guid[i] = src_guid[UBL_ALEN - i - 1]; +} + +int hclge_comm_unic_get_func_guid(struct hclge_comm_hw *hw, u8 *guid) +{ + struct hclge_desc desc; + bool is_random = false; + int ret; + + hclge_comm_cmd_setup_basic_desc(&desc, HCLGE_OPC_COMM_GET_FUNC_GUID, + true); + ret = hclge_comm_cmd_send(hw, &desc, 1); + if (ret) { + dev_err(&hw->cmq.csq.pdev->dev, + "failed to get function GUID, ret = %d\n", ret); + return ret; + } + + hclge_comm_unic_guid_le_to_net_trans((u8 *)desc.data, guid); + while (unlikely(!hclge_comm_unic_is_valid_func_guid(guid))) { + get_random_bytes(guid, UBL_ALEN); + is_random = true; + } + + if (unlikely(is_random)) + dev_warn(&hw->cmq.csq.pdev->dev, + "using random GUID %02x:%02x:...:%02x:%02x\n", + guid[0], guid[1], + guid[UBL_ALEN - 2], guid[UBL_ALEN - 1]); + + return 0; +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_unic_addr.h b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_unic_addr.h new file mode 100644 index 0000000000000000000000000000000000000000..0db62ac8dbcbc8c5977b8c54f7e17a11f7b9f3af --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_common/hclge_comm_unic_addr.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGE_COMM_UNIC_ADDR_H +#define __HCLGE_COMM_UNIC_ADDR_H + +#include +#include +#include + +#include "ubl.h" +#include "hnae3.h" +#include "hclge_comm_cmd.h" + +#define HCLGE_COMM_UNIC_IPV6_UPPER_LEN 13 +#define HCLGE_COMM_UNIC_IPV6_LOWER_LEN 3 +#define HCLGE_COMM_UNIC_MSG_IPADDR_POS 1 + +enum HCLGE_COMM_ADDR_NODE_STATE { + HCLGE_COMM_UNIC_ADDR_TO_ADD, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + HCLGE_COMM_UNIC_ADDR_ACTIVE +}; + +#define UNIC_ADDR_LEN 16 +#define HCLGE_COMM_MGUID_PREFIX_LEN 14 +struct hclge_comm_unic_addr_node { + struct list_head node; + enum HCLGE_COMM_ADDR_NODE_STATE state; + union { + u8 unic_addr[UNIC_ADDR_LEN]; + u8 mguid[UBL_ALEN]; + struct { + u8 prefix[HCLGE_COMM_MGUID_PREFIX_LEN]; + __le16 proto; + }; + struct in6_addr ip_addr; + }; +}; + +#define HCLGE_COMM_FUNC_GUID_ENTRY_VALID_EN 0x01 + +struct hclge_comm_func_guid_cmd { + u8 entry_vld : 1; + u8 lookup_enable : 1; + u8 rsv0 : 6; + u8 rsv1; + __le16 rsv2; + /* use big endian here */ + u8 guid[UBL_ALEN]; + __le16 hit_info; + __le16 rsv3; +}; + +#define HCLGE_COMM_FORMAT_GUID_ADDR_LEN 48 +#define HCLGE_COMM_FORMAT_GUID_ADDR_PROTO_HIGH 14 +#define HCLGE_COMM_FORMAT_GUID_ADDR_PROTO_LOW 15 + +static inline void hclge_comm_format_guid_addr(char *format_guid_addr, + const u8 *guid_addr) +{ + snprintf(format_guid_addr, HCLGE_COMM_FORMAT_GUID_ADDR_LEN, + "ff:ff:**:**:**:**:**:**:**:**:**:**:**:**:%02x:%02x", + guid_addr[HCLGE_COMM_FORMAT_GUID_ADDR_PROTO_HIGH], + guid_addr[HCLGE_COMM_FORMAT_GUID_ADDR_PROTO_LOW]); +} + +static inline bool hclge_comm_unic_addr_equal(const u8 *addr1, const u8 *addr2) +{ + const u32 *a = (const u32 *)addr1; + const u32 *b = (const u32 *)addr2; + + return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | + (a[2] ^ b[2]) | (a[3] ^ b[3])) == 0; +} + +void hclge_comm_unic_sync_from_addr_del_list(struct list_head *del_list, + struct list_head *addr_list); +int hclge_comm_unic_update_addr_list(struct list_head *list, + spinlock_t *addr_list_lock, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const unsigned char *addr); +bool hclge_comm_unic_sync_addr_table(struct hnae3_handle *handle, + struct list_head *list, + spinlock_t *addr_list_lock, + void (*sync)(struct hnae3_handle *, + struct list_head *), + void (*unsync)(struct hnae3_handle *, + struct list_head *)); +int hclge_comm_unic_convert_ip_addr(const struct sockaddr *addr, + struct in6_addr *ip_addr); +int hclge_comm_unic_set_func_guid(struct hclge_comm_hw *hw, u8 *guid); +int hclge_comm_unic_get_func_guid(struct hclge_comm_hw *hw, u8 *guid); +void hclge_comm_unic_rm_func_guid(struct hclge_comm_hw *hw); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c b/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c index 3b6dbf158b98db7d9ec97bfa22b34e4e61f0d260..78499658374697887502db877db8107189461e35 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_dcbnl.c @@ -115,6 +115,15 @@ static const struct dcbnl_rtnl_ops hns3_dcbnl_ops = { .setdcbx = hns3_dcbnl_setdcbx, }; +static const struct dcbnl_rtnl_ops hns3_unic_dcbnl_ops = { + .ieee_getets = hns3_dcbnl_ieee_getets, + .ieee_setets = hns3_dcbnl_ieee_setets, + .ieee_setapp = hns3_dcbnl_ieee_setapp, + .ieee_delapp = hns3_dcbnl_ieee_delapp, + .getdcbx = hns3_dcbnl_getdcbx, + .setdcbx = hns3_dcbnl_setdcbx, +}; + /* hclge_dcbnl_setup - DCBNL setup * @handle: the corresponding vport handle * Set up DCBNL @@ -126,5 +135,12 @@ void hns3_dcbnl_setup(struct hnae3_handle *handle) if ((!handle->kinfo.dcb_ops) || (handle->flags & HNAE3_SUPPORT_VF)) return; +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) + dev->dcbnl_ops = &hns3_unic_dcbnl_ops; + else + dev->dcbnl_ops = &hns3_dcbnl_ops; +#else dev->dcbnl_ops = &hns3_dcbnl_ops; +#endif } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 00d3de71de0143be941a2a99adbf232c53fdcb2f..5cdfa8e06ddf5f1d725ef211801e8e32cef7772e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -7,6 +7,7 @@ #include "hnae3.h" #include "hns3_debugfs.h" #include "hns3_enet.h" +#include "hns3_unic_debugfs.h" static struct dentry *hns3_dbgfs_root; @@ -1415,6 +1416,13 @@ int hns3_dbg_init(struct hnae3_handle *handle) goto out; } } +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) { + ret = hns3_unic_dbg_init(handle, handle->hnae3_dbgfs); + if (ret) + goto out; + } +#endif return 0; @@ -1432,6 +1440,11 @@ void hns3_dbg_uninit(struct hnae3_handle *handle) debugfs_remove_recursive(handle->hnae3_dbgfs); handle->hnae3_dbgfs = NULL; +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) + hns3_unic_dbg_uninit(handle); +#endif + for (i = 0; i < ARRAY_SIZE(hns3_dbg_cmd); i++) if (handle->dbgfs_buf[i]) { kvfree(handle->dbgfs_buf[i]); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index 4c7896f2c78031f452aa9e184947b356b9e885b9..26deefda36f1a58f180b05e3070f9db1c028ff5f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -23,9 +23,11 @@ #include #include +#include "ubl.h" #include "hnae3.h" #include "hnae3_ext.h" #include "hns3_enet.h" +#include "hns3_unic.h" /* All hns3 tracepoints are defined by the include below, which * must be included exactly once across the whole kernel with * CREATE_TRACE_POINTS defined @@ -103,9 +105,23 @@ static const struct pci_device_id hns3_pci_tbl[] = { HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_400G_ROH), HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA), + HNAE3_DEV_SUPPORT_UDMA_DCB_BITS}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), 0}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF), HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA_VF), + HNAE3_DEV_SUPPORT_UDMA_DCB_BITS}, +#ifdef CONFIG_HNS3_UBL + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA_OVER_UBL), + HNAE3_DEV_SUPPORT_UDMA_OVER_UBL_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_OVER_UBL), + HNAE3_DEV_SUPPORT_ROCE_OVER_UBL_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA_OVER_UBL_VF), + HNAE3_DEV_SUPPORT_UDMA_OVER_UBL_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_OVER_UBL_VF), + HNAE3_DEV_SUPPORT_ROCE_OVER_UBL_DCB_BITS}, +#endif /* required last entry */ {0,} }; @@ -1901,6 +1917,10 @@ static int hns3_fill_skb_desc(struct hns3_nic_priv *priv, } /* Set txbd */ +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(priv->ae_handle)) + hns3_unic_set_l3_type(skb, ¶m.type_cs_vlan_tso); +#endif desc->tx.ol_type_vlan_len_msec = cpu_to_le32(param.ol_type_vlan_len_msec); desc->tx.type_cs_vlan_tso_len = cpu_to_le32(param.type_cs_vlan_tso); @@ -2562,7 +2582,12 @@ netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, struct net_device *netdev) hns3_rl_err(netdev, "xmit error: %d!\n", ret); goto out_err_tx_ok; } - +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(hns3_get_handle(netdev))) { + ubl_rmv_sw_ctype(skb); + hns3_unic_set_default_cc(skb); + } +#endif ret = hns3_handle_skb_desc(priv, ring, skb, desc_cb, ring->next_to_use); if (unlikely(ret <= 0)) goto out_err_tx_ok; @@ -3252,6 +3277,30 @@ static u16 hns3_nic_select_queue(struct net_device *netdev, return netdev_pick_tx(netdev, skb, sb_dev); } +const struct net_device_ops hns3_unic_netdev_ops = { + .ndo_open = hns3_nic_net_open, + .ndo_stop = hns3_nic_net_stop, + .ndo_start_xmit = hns3_nic_net_xmit, + .ndo_tx_timeout = hns3_nic_net_timeout, + .ndo_do_ioctl = hns3_nic_do_ioctl, + .ndo_change_mtu = hns3_nic_change_mtu, + .ndo_set_features = hns3_nic_set_features, + .ndo_features_check = hns3_features_check, + .ndo_get_stats64 = hns3_nic_get_stats64, + .ndo_setup_tc = hns3_nic_setup_tc, +#ifdef CONFIG_HNS3_UBL + .ndo_set_rx_mode = hns3_unic_set_rx_mode, +#endif + .ndo_set_vf_trust = hns3_set_vf_trust, +#ifdef CONFIG_RFS_ACCEL + .ndo_rx_flow_steer = hns3_rx_flow_steer, +#endif + .ndo_get_vf_config = hns3_nic_get_vf_config, + .ndo_set_vf_link_state = hns3_nic_set_vf_link_state, + .ndo_set_vf_rate = hns3_nic_set_vf_rate, + .ndo_select_queue = hns3_nic_select_queue, +}; + static const struct net_device_ops hns3_nic_netdev_ops = { .ndo_open = hns3_nic_net_open, .ndo_stop = hns3_nic_net_stop, @@ -3280,6 +3329,11 @@ static const struct net_device_ops hns3_nic_netdev_ops = { .ndo_select_queue = hns3_nic_select_queue, }; +bool hns3_unic_port_dev_check(const struct net_device *dev) +{ + return dev->netdev_ops == &hns3_unic_netdev_ops; +} + bool hns3_is_phys_func(struct pci_dev *pdev) { u32 dev_id = pdev->device; @@ -3296,9 +3350,19 @@ bool hns3_is_phys_func(struct pci_dev *pdev) case HNAE3_DEV_ID_200G_RDMA: case HNAE3_DEV_ID_200G_ROH: case HNAE3_DEV_ID_400G_ROH: +#ifdef CONFIG_HNS3_UBL + case HNAE3_DEV_ID_UDMA_OVER_UBL: + case HNAE3_DEV_ID_RDMA_OVER_UBL: +#endif + case HNAE3_DEV_ID_UDMA: return true; case HNAE3_DEV_ID_VF: case HNAE3_DEV_ID_RDMA_DCB_PFC_VF: +#ifdef CONFIG_HNS3_UBL + case HNAE3_DEV_ID_UDMA_OVER_UBL_VF: + case HNAE3_DEV_ID_RDMA_OVER_UBL_VF: +#endif + case HNAE3_DEV_ID_UDMA_VF: return false; default: dev_warn(&pdev->dev, "un-recognized pci device-id %u", @@ -4302,8 +4366,14 @@ static int hns3_alloc_skb(struct hns3_enet_ring *ring, unsigned int length, skb_mark_for_recycle(skb); hns3_ring_stats_update(ring, seg_pkt_cnt); - +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(hns3_get_handle(netdev))) + ring->pull_len = HNS3_RX_HEAD_SIZE; + else + ring->pull_len = eth_get_headlen(netdev, va, HNS3_RX_HEAD_SIZE); +#else ring->pull_len = eth_get_headlen(netdev, va, HNS3_RX_HEAD_SIZE); +#endif __skb_put(skb, ring->pull_len); hns3_nic_reuse_page(skb, ring->frag_num++, ring, ring->pull_len, desc_cb); @@ -4373,6 +4443,23 @@ static int hns3_add_frag(struct hns3_enet_ring *ring) return 0; } +u32 hns3_get_l3_type(struct hns3_nic_priv *priv, u32 l234info, u32 ol_info) +{ + u32 l3_type; + + if (test_bit(HNS3_NIC_STATE_RXD_ADV_LAYOUT_ENABLE, &priv->state)) { + u32 ptype = hnae3_get_field(ol_info, HNS3_RXD_PTYPE_M, + HNS3_RXD_PTYPE_S); + + l3_type = hns3_rx_ptype_tbl[ptype].l3_type; + } else { + l3_type = hnae3_get_field(l234info, HNS3_RXD_L3ID_M, + HNS3_RXD_L3ID_S); + } + + return l3_type; +} + static int hns3_set_gro_and_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb, u32 l234info, u32 bd_base_info, u32 ol_info, u16 csum) @@ -4395,15 +4482,7 @@ static int hns3_set_gro_and_checksum(struct hns3_enet_ring *ring, HNS3_RXD_GRO_COUNT_M, HNS3_RXD_GRO_COUNT_S); - if (test_bit(HNS3_NIC_STATE_RXD_ADV_LAYOUT_ENABLE, &priv->state)) { - u32 ptype = hnae3_get_field(ol_info, HNS3_RXD_PTYPE_M, - HNS3_RXD_PTYPE_S); - - l3_type = hns3_rx_ptype_tbl[ptype].l3_type; - } else { - l3_type = hnae3_get_field(l234info, HNS3_RXD_L3ID_M, - HNS3_RXD_L3ID_S); - } + l3_type = hns3_get_l3_type(priv, l234info, ol_info); if (l3_type == HNS3_L3_TYPE_IPV4) skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; @@ -4523,7 +4602,17 @@ static int hns3_handle_bdinfo(struct hns3_enet_ring *ring, struct sk_buff *skb) len = skb->len; /* Do update ip stack process */ +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(hns3_get_handle(netdev))) + skb->protocol = ubl_type_trans(skb, netdev, + hns3_unic_get_l3_type(netdev, + ol_info, + l234info)); + else + skb->protocol = eth_type_trans(skb, netdev); +#else skb->protocol = eth_type_trans(skb, netdev); +#endif /* This is needed in order to enable forwarding support */ ret = hns3_set_gro_and_checksum(ring, skb, l234info, @@ -5373,6 +5462,9 @@ static int hns3_init_mac_addr(struct net_device *netdev) u8 mac_addr_temp[ETH_ALEN]; int ret = 0; + if (hns3_ubl_supported(h)) + return 0; + if (h->ae_algo->ops->get_mac_addr) h->ae_algo->ops->get_mac_addr(h, mac_addr_temp); @@ -5533,7 +5625,14 @@ static int hns3_client_init(struct hnae3_handle *handle) handle->ae_algo->ops->get_tqps_and_rss_info(handle, &alloc_tqps, &max_rss_size); +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) + netdev = alloc_ubndev_mq(sizeof(struct hns3_nic_priv), alloc_tqps); + else + netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv), alloc_tqps); +#else netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv), alloc_tqps); +#endif if (!netdev) return -ENOMEM; @@ -5557,7 +5656,16 @@ static int hns3_client_init(struct hnae3_handle *handle) netdev->watchdog_timeo = HNS3_TX_TIMEOUT; netdev->priv_flags |= IFF_UNICAST_FLT; + +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) + netdev->netdev_ops = &hns3_unic_netdev_ops; + else + netdev->netdev_ops = &hns3_nic_netdev_ops; +#else netdev->netdev_ops = &hns3_nic_netdev_ops; +#endif + SET_NETDEV_DEV(netdev, &pdev->dev); hns3_ethtool_set_ops(netdev); @@ -5625,6 +5733,10 @@ static int hns3_client_init(struct hnae3_handle *handle) } netdev->max_mtu = HNS3_MAX_MTU(ae_dev->dev_specs.max_frm_size); +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) + hns3_unic_init(netdev); +#endif hns3_state_init(handle); @@ -5941,6 +6053,10 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle) dev_err(priv->dev, "hns3_client_start fail! ret=%d\n", ret); goto err_client_start_fail; } +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(handle)) + hns3_unic_init_guid(netdev); +#endif set_bit(HNS3_NIC_STATE_INITED, &priv->state); @@ -6230,6 +6346,9 @@ static int __init hns3_init_module(void) if (ret) goto err_reg_driver; +#ifdef CONFIG_HNS3_UBL + register_ipaddr_notifier(); +#endif return ret; err_reg_driver: @@ -6246,6 +6365,9 @@ module_init(hns3_init_module); */ static void __exit hns3_exit_module(void) { +#ifdef CONFIG_HNS3_UBL + unregister_ipaddr_notifier(); +#endif pci_unregister_driver(&hns3_driver); hnae3_unregister_client(&client); hns3_dbg_unregister_debugfs(); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index 861979579c3652d4fc589c8635a02d8d0984c5f5..fb9a9fd7ea36e4e90478b7acb8064062ba240c32 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -746,6 +746,9 @@ static inline unsigned int hns3_page_order(struct hns3_enet_ring *ring) return 0; } +#define hns3_ubl_supported(handle) \ + hnae3_dev_ubl_supported((struct hnae3_ae_dev *)pci_get_drvdata((handle)->pdev)) + #define hns3_page_size(_ring) (PAGE_SIZE << hns3_page_order(_ring)) /* iterator for handling rings in ring group */ @@ -771,11 +774,13 @@ void hns3_ethtool_set_ops(struct net_device *netdev); int hns3_set_channels(struct net_device *netdev, struct ethtool_channels *ch); +u32 hns3_get_l3_type(struct hns3_nic_priv *priv, u32 l234info, u32 ol_info); void hns3_clean_tx_ring(struct hns3_enet_ring *ring, int budget); int hns3_init_all_ring(struct hns3_nic_priv *priv); int hns3_nic_reset_all_ring(struct hnae3_handle *h); void hns3_fini_ring(struct hns3_enet_ring *ring); netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, struct net_device *netdev); +bool hns3_unic_port_dev_check(const struct net_device *dev); bool hns3_is_phys_func(struct pci_dev *pdev); int hns3_clean_rx_ring( struct hns3_enet_ring *ring, int budget, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index 315211f7be3930dcaa721e69586d55e82848189e..4f732e950f09e6c9ae53965c2d1a863747ac20f8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -8,6 +8,7 @@ #include "hns3_enet.h" #include "hns3_ethtool.h" +#include "hns3_unic.h" /* tqp related stats */ #define HNS3_TQP_STAT(_string, _member) { \ @@ -178,6 +179,32 @@ static void hns3_lp_setup_skb(struct sk_buff *skb) packet[i] = (unsigned char)(i & 0xff); } +static struct sk_buff *hns3_lp_skb_prepare(struct net_device *ndev) +{ + unsigned int size = hns3_ubl_supported(hns3_get_handle(ndev)) ? + HNS3_NIC_LB_TEST_PACKET_SIZE + 1 + NET_IP_ALIGN : + HNS3_NIC_LB_TEST_PACKET_SIZE + ETH_HLEN + NET_IP_ALIGN; + struct sk_buff *skb; + + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return NULL; + + skb->dev = ndev; +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(hns3_get_handle(ndev))) { + skb->protocol = htons(ETH_P_UB); + hns3_unic_lp_setup_skb(skb); + } else { + hns3_lp_setup_skb(skb); + } +#else + hns3_lp_setup_skb(skb); +#endif + skb->queue_mapping = HNS3_NIC_LB_TEST_RING_ID; + return skb; +} + static void hns3_lb_check_skb_data(struct hns3_enet_ring *ring, struct sk_buff *skb) { @@ -218,7 +245,16 @@ static u32 hns3_lb_check_rx_ring(struct hns3_nic_priv *priv, u32 budget) pre_rx_pkt = rx_group->total_packets; preempt_disable(); +#ifdef CONFIG_HNS3_UBL + if (hns3_ubl_supported(hns3_get_handle(priv->netdev))) + hns3_clean_rx_ring(ring, budget, + hns3_unic_lb_check_skb_data); + else + hns3_clean_rx_ring(ring, budget, + hns3_lb_check_skb_data); +#else hns3_clean_rx_ring(ring, budget, hns3_lb_check_skb_data); +#endif preempt_enable(); rcv_good_pkt_total += (rx_group->total_packets - pre_rx_pkt); @@ -253,15 +289,10 @@ static int hns3_lp_run_test(struct net_device *ndev, enum hnae3_loop mode) u32 i, good_cnt; int ret_val = 0; - skb = alloc_skb(HNS3_NIC_LB_TEST_PACKET_SIZE + ETH_HLEN + NET_IP_ALIGN, - GFP_KERNEL); + skb = hns3_lp_skb_prepare(ndev); if (!skb) return HNS3_NIC_LB_TEST_NO_MEM_ERR; - skb->dev = ndev; - hns3_lp_setup_skb(skb); - skb->queue_mapping = HNS3_NIC_LB_TEST_RING_ID; - good_cnt = 0; for (i = 0; i < HNS3_NIC_LB_TEST_PKT_NUM; i++) { netdev_tx_t tx_ret; @@ -2101,6 +2132,48 @@ static const struct ethtool_ops hns3vf_ethtool_ops = { .reset = hns3_set_reset, }; +static const struct ethtool_ops hns3_unic_ethtool_ops = { + .supported_coalesce_params = HNS3_ETHTOOL_COALESCE, + .supported_ring_params = HNS3_ETHTOOL_RING, + .self_test = hns3_self_test, + .get_drvinfo = hns3_get_drvinfo, + .get_link = hns3_get_link, + .get_ringparam = hns3_get_ringparam, + .set_ringparam = hns3_set_ringparam, + .get_strings = hns3_get_strings, + .get_ethtool_stats = hns3_get_stats, + .get_sset_count = hns3_get_sset_count, + .get_channels = hns3_get_channels, + .set_channels = hns3_set_channels, + .get_rxnfc = hns3_get_rxnfc, + .set_rxnfc = hns3_set_rxnfc, + .get_rxfh_key_size = hns3_get_rss_key_size, + .get_rxfh_indir_size = hns3_get_rss_indir_size, + .get_rxfh = hns3_get_rss, + .set_rxfh = hns3_set_rss, + .get_link_ksettings = hns3_get_link_ksettings, + .set_link_ksettings = hns3_set_link_ksettings, + .nway_reset = hns3_nway_reset, + .get_coalesce = hns3_get_coalesce, + .set_coalesce = hns3_set_coalesce, + .get_regs_len = hns3_get_regs_len, + .get_regs = hns3_get_regs, + .set_phys_id = hns3_set_phys_id, + .get_msglevel = hns3_get_msglevel, + .set_msglevel = hns3_set_msglevel, + .get_fecparam = hns3_get_fecparam, + .set_fecparam = hns3_set_fecparam, + .get_module_info = hns3_get_module_info, + .get_module_eeprom = hns3_get_module_eeprom, + .get_priv_flags = hns3_get_priv_flags, + .set_priv_flags = hns3_set_priv_flags, + .get_ts_info = hns3_get_ts_info, + .get_tunable = hns3_get_tunable, + .set_tunable = hns3_set_tunable, + .reset = hns3_set_reset, + .get_link_ext_state = hns3_get_link_ext_state, +}; + static const struct ethtool_ops hns3_ethtool_ops = { .supported_coalesce_params = HNS3_ETHTOOL_COALESCE, .supported_ring_params = HNS3_ETHTOOL_RING, @@ -2153,6 +2226,10 @@ void hns3_ethtool_set_ops(struct net_device *netdev) if (h->flags & HNAE3_SUPPORT_VF) netdev->ethtool_ops = &hns3vf_ethtool_ops; +#ifdef CONFIG_HNS3_UBL + else if (hns3_ubl_supported(h)) + netdev->ethtool_ops = &hns3_unic_ethtool_ops; +#endif else netdev->ethtool_ops = &hns3_ethtool_ops; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_unic.c b/drivers/net/ethernet/hisilicon/hns3/hns3_unic.c new file mode 100644 index 0000000000000000000000000000000000000000..3320d1ad5bd9bcdcebf2db61ab41a673fc0b9f75 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_unic.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "ubl.h" +#include "hnae3.h" +#include "hns3_enet.h" +#include "hns3_unic.h" + +#define HNS3_UNIC_LB_TEST_PACKET_SIZE 128 + +void hns3_unic_set_default_cc(struct sk_buff *skb) +{ + struct ublhdr *ubl = (struct ublhdr *)skb->data; + + if (skb->protocol == htons(ETH_P_IP) || + skb->protocol == htons(ETH_P_IPV6)) + ubl->h_cc = htons(UNIC_CC_DEFAULT_FECN_MODE); +} + +void hns3_unic_init(struct net_device *netdev) +{ + struct hnae3_handle *h = hns3_get_handle(netdev); + struct pci_dev *pdev = h->pdev; + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); + + netdev->features &= ~(NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_CSUM | + NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX); + netdev->features |= NETIF_F_VLAN_CHALLENGED; + netdev->hw_features &= ~(NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_CSUM | + NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX); + + netdev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); + netdev->max_mtu = ae_dev->dev_specs.max_frm_size; + + hns3_unic_init_guid(netdev); +} + +/** + * L3T is an element of the TX BD interface for software and hardware + * interaction, used to identify the message type. As the message data + * given by software to the chip cannot be self-decoded, the driver needs + * to actively inform the chip of the message type, which is unrelated + * to checksum offloading. + */ +void hns3_unic_set_l3_type(struct sk_buff *skb, u32 *type_cs_vlan_tso) +{ + if (skb->protocol == htons(ETH_P_IP)) + hnae3_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_M, + HNS3_TXD_L3T_S, HNS3_L3T_IPV4); + else if (skb->protocol == htons(ETH_P_IPV6)) + hnae3_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_M, + HNS3_TXD_L3T_S, HNS3_L3T_IPV6); +} + +u8 hns3_unic_get_l3_type(struct net_device *netdev, u32 ol_info, u32 l234info) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + u32 l3_type; + + l3_type = hns3_get_l3_type(priv, l234info, ol_info); + + if (l3_type == HNS3_L3_TYPE_IPV4) + return UB_IPV4_CFG_TYPE; + else if (l3_type == HNS3_L3_TYPE_IPV6) + return UB_IPV6_CFG_TYPE; + else if (l3_type != HNS3_L3_TYPE_PARSE_FAIL) + return UB_NOIP_CFG_TYPE; + + return UB_UNKNOWN_CFG_TYPE; +} + +static int addr_event(struct notifier_block *nb, unsigned long event, + struct sockaddr *sa, struct net_device *ndev) +{ + struct hnae3_handle *handle; + int ret; + + if (ndev->type != ARPHRD_UB) + return NOTIFY_DONE; + + if (!hns3_unic_port_dev_check(ndev)) + return NOTIFY_DONE; + + handle = hns3_get_handle(ndev); + + switch (event) { + case NETDEV_UP: + if (handle->ae_algo->ops->add_addr) { + ret = handle->ae_algo->ops->add_addr(handle, + (const unsigned char *)sa, HNAE3_UNIC_IP_ADDR); + if (ret) + return NOTIFY_BAD; + } else { + return NOTIFY_DONE; + } + break; + case NETDEV_DOWN: + if (handle->ae_algo->ops->rm_addr) { + ret = handle->ae_algo->ops->rm_addr(handle, + (const unsigned char *)sa, HNAE3_UNIC_IP_ADDR); + if (ret) + return NOTIFY_BAD; + } else { + return NOTIFY_DONE; + } + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int unic_inetaddr_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa4 = (struct in_ifaddr *)ptr; + struct net_device *ndev = (struct net_device *)ifa4->ifa_dev->dev; + struct sockaddr_in in; + + in.sin_family = AF_INET; + in.sin_addr.s_addr = ifa4->ifa_address; + + return addr_event(this, event, (struct sockaddr *)&in, ndev); +} + +static int unic_inet6addr_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr; + struct net_device *ndev = (struct net_device *)ifa6->idev->dev; + struct sockaddr_in6 in6; + + in6.sin6_family = AF_INET6; + in6.sin6_addr = ifa6->addr; + + return addr_event(this, event, (struct sockaddr *)&in6, ndev); +} + +static struct notifier_block unic_inetaddr_notifier = { + .notifier_call = unic_inetaddr_event +}; + +static struct notifier_block unic_inet6addr_notifier = { + .notifier_call = unic_inet6addr_event +}; + +void register_ipaddr_notifier(void) +{ + register_inetaddr_notifier(&unic_inetaddr_notifier); + register_inet6addr_notifier(&unic_inet6addr_notifier); +} + +void unregister_ipaddr_notifier(void) +{ + unregister_inetaddr_notifier(&unic_inetaddr_notifier); + unregister_inet6addr_notifier(&unic_inet6addr_notifier); +} + +#define UNIC_DHCPV4_PROTO 0x0100 +void hns3_unic_lp_setup_skb(struct sk_buff *skb) +{ + unsigned int nip_ctrl_len = sizeof(struct ub_nip_ctrl_fld); + struct net_device *ndev = skb->dev; + struct ub_nip_ctrl_fld *ctrl_fld; + unsigned char *sw_ptype; + unsigned char *packet; + unsigned int i; + + skb_reserve(skb, NET_IP_ALIGN); + + sw_ptype = (unsigned char *)skb_put(skb, sizeof(unsigned char)); + *sw_ptype = UB_NOIP_CFG_TYPE; + ctrl_fld = (struct ub_nip_ctrl_fld *)skb_put(skb, nip_ctrl_len); + packet = (unsigned char *)skb_put(skb, HNS3_UNIC_LB_TEST_PACKET_SIZE - + nip_ctrl_len); + ctrl_fld->proto = htons(UNIC_DHCPV4_PROTO); + memcpy(ctrl_fld->d_guid, ndev->dev_addr, UBL_ALEN); + memcpy(ctrl_fld->s_guid, ndev->dev_addr, UBL_ALEN); + + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + + for (i = 0; i < HNS3_UNIC_LB_TEST_PACKET_SIZE - nip_ctrl_len; i++) + packet[i] = (unsigned char)(i & 0xff); +} + +void hns3_unic_lb_check_skb_data(struct hns3_enet_ring *ring, + struct sk_buff *skb) +{ + unsigned int nip_ctrl_len = sizeof(struct ub_nip_ctrl_fld); + struct hns3_enet_tqp_vector *tqp_vector = ring->tqp_vector; + struct net_device *ndev = skb->dev; + struct ub_nip_ctrl_fld *ctrl_fld; + u32 len = skb_headlen(skb); + bool is_success = false; + unsigned char *packet; + u32 i; + + if (len != HNS3_UNIC_LB_TEST_PACKET_SIZE + 1) + goto out; + + ctrl_fld = (struct ub_nip_ctrl_fld *)(skb->data + 1); + if (memcmp(ctrl_fld->d_guid, ndev->dev_addr, UBL_ALEN) || + memcmp(ctrl_fld->s_guid, ndev->dev_addr, UBL_ALEN) || + ctrl_fld->proto != htons(UNIC_DHCPV4_PROTO)) + goto out; + + packet = (unsigned char *)ctrl_fld + nip_ctrl_len; + for (i = 0; i < HNS3_UNIC_LB_TEST_PACKET_SIZE - nip_ctrl_len; i++) + if (packet[i] != (unsigned char)(i & 0xff)) + goto out; + + is_success = true; + +out: + if (is_success) + tqp_vector->rx_group.total_packets++; + else + print_hex_dump(KERN_ERR, "ubn selftest:", DUMP_PREFIX_OFFSET, + 16, 1, skb->data, len, true); + + dev_kfree_skb_any(skb); +} + +static void hns3_unic_extern_mc_guid(u8 *mguid, const unsigned char *addr) +{ + int proto_size = sizeof(u16); + int addr_proto_oft = HNS3_SIMPLE_GUID_LEN - proto_size; + int proto_oft = UBL_ALEN - proto_size; + + memset(mguid, 0xFF, proto_oft); + memcpy(&mguid[proto_oft], &addr[addr_proto_oft], proto_size); +} + +static int hns3_unic_add_mc_guid(struct net_device *netdev, + const unsigned char *addr) +{ + u8 format_simple_guid_addr[HNS3_SIMPLE_FORMAT_GUID_ADDR_LEN] = {0}; + struct hnae3_handle *h = hns3_get_handle(netdev); + u8 mguid[UBL_ALEN] = {0}; + + if (!hns3_unic_mguid_valid_check(addr)) { + hns3_unic_format_sim_guid_addr(format_simple_guid_addr, addr); + netdev_err(netdev, "Add mc guid err! invalid guid: %s\n", + format_simple_guid_addr); + return -EINVAL; + } + + hns3_unic_extern_mc_guid(mguid, addr); + if (h->ae_algo->ops->add_addr) + return h->ae_algo->ops->add_addr(h, (const u8 *)mguid, + HNAE3_UNIC_MCGUID_ADDR); + + return 0; +} + +static int hns3_unic_del_mc_guid(struct net_device *netdev, + const unsigned char *addr) +{ + u8 format_simple_guid_addr[HNS3_SIMPLE_FORMAT_GUID_ADDR_LEN] = {0}; + struct hnae3_handle *h = hns3_get_handle(netdev); + u8 mguid[UBL_ALEN] = {0}; + + if (!hns3_unic_mguid_valid_check(addr)) { + hns3_unic_format_sim_guid_addr(format_simple_guid_addr, addr); + netdev_err(netdev, "Del mc guid err! invalid guid: %s\n", + format_simple_guid_addr); + return -EINVAL; + } + + hns3_unic_extern_mc_guid(mguid, addr); + if (h->ae_algo->ops->rm_addr) + return h->ae_algo->ops->rm_addr(h, (const u8 *)mguid, + HNAE3_UNIC_MCGUID_ADDR); + + return 0; +} + +static u8 hns3_unic_get_netdev_flags(struct net_device *netdev) +{ + u8 flags = 0; + + /* GUID promiscuous multiplexing unicast promiscuous, IP promiscuous + * multiplexing multicast promiscuous + */ + if (netdev->flags & IFF_PROMISC) + flags = HNAE3_USER_UPE | HNAE3_USER_MPE; + + return flags; +} + +void hns3_unic_set_rx_mode(struct net_device *netdev) +{ + struct hnae3_handle *h = hns3_get_handle(netdev); + u8 new_flags; + + new_flags = hns3_unic_get_netdev_flags(netdev); + + __dev_mc_sync(netdev, hns3_unic_add_mc_guid, hns3_unic_del_mc_guid); + + h->netdev_flags = new_flags; + hns3_request_update_promisc_mode(h); +} + +void hns3_unic_init_guid(struct net_device *netdev) +{ + const u8 bc_guid[HNS3_SIMPLE_GUID_LEN] = {0xff, 0xff, 0xff, 0xff, + 0xff, 0xff}; + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u8 temp_guid_addr[UBL_ALEN]; + int ret; + + if (!h->ae_algo->ops->get_func_guid || + !h->ae_algo->ops->set_func_guid) { + netdev_err(netdev, "the guid handlers may not exist\n"); + return; + } + + ret = h->ae_algo->ops->get_func_guid(h, temp_guid_addr); + if (ret) { + netdev_err(netdev, "get function guid fail, ret = %d!\n", ret); + return; + } + + memcpy(netdev->dev_addr, temp_guid_addr, netdev->addr_len); + memcpy(netdev->perm_addr, temp_guid_addr, netdev->addr_len); + + ret = h->ae_algo->ops->set_func_guid(h, netdev->dev_addr); + if (ret) { + netdev_err(netdev, "set function guid fail, ret = %d\n", ret); + return; + } + + hns3_unic_add_mc_guid(netdev, bc_guid); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_unic.h b/drivers/net/ethernet/hisilicon/hns3/hns3_unic.h new file mode 100644 index 0000000000000000000000000000000000000000..d4071dd723766bc58d47d9fd24d6a7e53a05004c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_unic.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HNS3_UNIC_H +#define __HNS3_UNIC_H + +#include "ubl.h" + +#define UNIC_CC_DEFAULT_FECN_MODE 0x4000 + +struct ub_nip_ctrl_fld { + __be16 proto; + unsigned char d_guid[UBL_ALEN]; + unsigned char s_guid[UBL_ALEN]; +}; + +static inline bool hns3_unic_mguid_valid_check(const u8 *addr) +{ +#define HNS3_UNIC_MCGUID_VALID_PREFIX 0xffffffffu + u32 *upper = (u32 *)addr; + + /* The guid from the user is used as the lower 48 bits of the actual + * guid. Therefore, this interface is used to check only the lower + * 48 bits of the guid. + */ + return *upper == HNS3_UNIC_MCGUID_VALID_PREFIX; +} + +#define HNS3_SIMPLE_FORMAT_GUID_ADDR_LEN 18 +#define HNS3_SIMPLE_GUID_LEN 6 + +static inline void hns3_unic_format_sim_guid_addr(char *format_simple_guid_addr, + const u8 *guid_addr) +{ + snprintf(format_simple_guid_addr, HNS3_SIMPLE_FORMAT_GUID_ADDR_LEN, + "%02x:%02x:%02x:%02x:%02x:%02x", + guid_addr[0], guid_addr[1], guid_addr[2], + guid_addr[3], guid_addr[4], guid_addr[5]); +} + +void hns3_unic_set_default_cc(struct sk_buff *skb); +void hns3_unic_init(struct net_device *netdev); +void hns3_unic_set_l3_type(struct sk_buff *skb, u32 *type_cs_vlan_tso); +u8 hns3_unic_get_l3_type(struct net_device *netdev, u32 ol_info, u32 l234info); +void hns3_unic_lp_setup_skb(struct sk_buff *skb); +void hns3_unic_lb_check_skb_data(struct hns3_enet_ring *ring, + struct sk_buff *skb); +void register_ipaddr_notifier(void); +void unregister_ipaddr_notifier(void); +void hns3_unic_set_rx_mode(struct net_device *netdev); +void hns3_unic_init_guid(struct net_device *netdev); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_unic_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_unic_debugfs.c new file mode 100644 index 0000000000000000000000000000000000000000..1d68fd31636e1e79ed59bcdf6c3e1ac448fb661d --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_unic_debugfs.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include + +#include "hnae3.h" +#include "hns3_debugfs.h" +#include "hns3_enet.h" +#include "hns3_unic_debugfs.h" + +static const char ub_dbg_root_name[] = "ub"; +static struct dentry *ub_dbg_root; + +static struct hns3_dbg_dentry_info ub_dbg_dentry[] = { + { + .name = "ip_tbl" + }, + { + .name = "guid_tbl" + }, +}; + +static int hns3_unic_dbg_file_init(struct hnae3_handle *handle, u32 cmd); + +static const struct hns3_dbg_cmd_info ub_dbg_cmd[] = { + { + .name = "ip_tbl_spec", + .cmd = HNAE3_DBG_CMD_IP_SPEC, + .dentry = UB_DBG_DENTRY_IP, + .buf_len = HNS3_DBG_READ_LEN, + .init = hns3_unic_dbg_file_init, + }, + { + .name = "guid_tbl_spec", + .cmd = HNAE3_DBG_CMD_GUID_SPEC, + .dentry = UB_DBG_DENTRY_GUID, + .buf_len = HNS3_DBG_READ_LEN, + .init = hns3_unic_dbg_file_init, + }, + { + .name = "ip_tbl_list", + .cmd = HNAE3_DBG_CMD_IP_LIST, + .dentry = UB_DBG_DENTRY_IP, + .buf_len = HNS3_DBG_READ_LEN, + .init = hns3_unic_dbg_file_init, + }, + { + .name = "guid_tbl_list", + .cmd = HNAE3_DBG_CMD_GUID_LIST, + .dentry = UB_DBG_DENTRY_GUID, + .buf_len = HNS3_DBG_READ_LEN, + .init = hns3_unic_dbg_file_init, + }, +}; + +static int hns3_unic_dbg_get_cmd_index(struct hns3_dbg_data *dbg_data, + u32 *index) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(ub_dbg_cmd); i++) { + if (ub_dbg_cmd[i].cmd == dbg_data->cmd) { + *index = i; + return 0; + } + } + + dev_err(&dbg_data->handle->pdev->dev, "unknown unic command(%d)\n", + dbg_data->cmd); + return -EINVAL; +} + +static int hns3_unic_dbg_read_cmd(struct hns3_dbg_data *dbg_data, + enum hnae3_dbg_cmd cmd, char *buf, int len) +{ + const struct hnae3_ae_ops *ops = dbg_data->handle->ae_algo->ops; + + if (!ops->dbg_read_cmd) + return -EOPNOTSUPP; + + return ops->dbg_read_cmd(dbg_data->handle, cmd, buf, len); +} + +static ssize_t hns3_unic_dbg_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct hns3_dbg_data *dbg_data = filp->private_data; + struct hnae3_handle *handle = dbg_data->handle; + struct hns3_nic_priv *priv = handle->priv; + char **save_buf; + char *read_buf; + ssize_t size; + u32 index; + int ret; + + ret = hns3_unic_dbg_get_cmd_index(dbg_data, &index); + if (ret) + return ret; + + mutex_lock(&handle->dbgfs_lock); + save_buf = &handle->ub_dbgfs_buf[index]; + + if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) || + test_bit(HNS3_NIC_STATE_RESETTING, &priv->state)) { + ret = -EBUSY; + goto out; + } + + if (*save_buf) { + read_buf = *save_buf; + } else { + read_buf = kvzalloc(ub_dbg_cmd[index].buf_len, GFP_KERNEL); + if (!read_buf) { + ret = -ENOMEM; + goto out; + } + + /* save the buffer addr until the last read operation */ + *save_buf = read_buf; + + /* get data ready for the first time to read */ + ret = hns3_unic_dbg_read_cmd(dbg_data, ub_dbg_cmd[index].cmd, + read_buf, + ub_dbg_cmd[index].buf_len); + if (ret) + goto out; + } + + size = simple_read_from_buffer(buffer, count, ppos, read_buf, + strlen(read_buf)); + if (size > 0) { + mutex_unlock(&handle->dbgfs_lock); + return size; + } + +out: + /* free the buffer for the last read operation */ + if (*save_buf) { + kvfree(*save_buf); + *save_buf = NULL; + } + + mutex_unlock(&handle->dbgfs_lock); + return ret; +} + +static const struct file_operations ub_dbg_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = hns3_unic_dbg_read, +}; + +static int hns3_unic_dbg_file_init(struct hnae3_handle *handle, u32 cmd) +{ + struct hns3_dbg_data *data; + struct dentry *entry_dir; + + data = devm_kzalloc(&handle->pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->handle = handle; + data->cmd = ub_dbg_cmd[cmd].cmd; + entry_dir = ub_dbg_dentry[ub_dbg_cmd[cmd].dentry].dentry; + debugfs_create_file(ub_dbg_cmd[cmd].name, 0400, entry_dir, + data, &ub_dbg_fops); + + return 0; +} + +int hns3_unic_dbg_init(struct hnae3_handle *handle, struct dentry *parent) +{ + int ret = 0; + u32 i; + + if (!parent) + return -EINVAL; + + handle->ub_dbgfs_buf = devm_kcalloc(&handle->pdev->dev, + ARRAY_SIZE(ub_dbg_cmd), + sizeof(*handle->ub_dbgfs_buf), + GFP_KERNEL); + if (!handle->ub_dbgfs_buf) + return -ENOMEM; + + ub_dbg_root = debugfs_create_dir(ub_dbg_root_name, parent); + + for (i = 0; i < UB_DBG_DENTRY_END; i++) + ub_dbg_dentry[i].dentry = + debugfs_create_dir(ub_dbg_dentry[i].name, ub_dbg_root); + + for (i = 0; i < ARRAY_SIZE(ub_dbg_cmd); i++) { + if (!ub_dbg_cmd[i].init) { + dev_err(&handle->pdev->dev, + "cmd %s lack of init func\n", + ub_dbg_cmd[i].name); + ret = -EINVAL; + break; + } + + ret = ub_dbg_cmd[i].init(handle, i); + if (ret) { + dev_err(&handle->pdev->dev, "failed to init cmd %s\n", + ub_dbg_cmd[i].name); + break; + } + } + + return ret; +} + +void hns3_unic_dbg_uninit(struct hnae3_handle *handle) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(ub_dbg_cmd); i++) + if (handle->ub_dbgfs_buf[i]) { + kvfree(handle->ub_dbgfs_buf[i]); + handle->ub_dbgfs_buf[i] = NULL; + } +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_unic_debugfs.h b/drivers/net/ethernet/hisilicon/hns3/hns3_unic_debugfs.h new file mode 100644 index 0000000000000000000000000000000000000000..2e1e2a2138ec373b8101c7efb25220b10f92b3ce --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_unic_debugfs.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HNS3_UNIC_DEBUGFS_H +#define __HNS3_UNIC_DEBUGFS_H + +enum hns3_dbg_ub_dentry_type { + UB_DBG_DENTRY_IP, + UB_DBG_DENTRY_GUID, + UB_DBG_DENTRY_END, +}; + +int hns3_unic_dbg_init(struct hnae3_handle *handle, struct dentry *parent); +void hns3_unic_dbg_uninit(struct hnae3_handle *handle); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index dde17c5425bfac7dd9d04629687131b256a66338..3b2b1831e5da98b3c40feca98d69bb7b9f041e9f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -426,6 +426,24 @@ struct hclge_check_mac_addr_cmd { u8 rsv[16]; }; +enum hclge_ip_tbl_opcode { + HCLGE_IP_TBL_ADD, /* Add new or modify ip_table */ + HCLGE_IP_TBL_REMOVE, /* Remove an entry through ip_table key */ + HCLGE_IP_TBL_LKUP, /* Lookup an entry through ip_table key */ +}; + +#define HCLGE_ADD_IP_TBL_OVERFLOW 2 +#define HCLGE_IP_PORT_VFID_S 0 +#define HCLGE_IP_PORT_VFID_M GENMASK(7, 0) + +struct hclge_ip_tbl_entry_cmd { + u8 resp_code; + u8 rsv1[3]; + u8 ipaddr[16]; + __le16 dip_ad; + u8 rsv2[2]; +}; + #define HCLGE_UMV_SPC_ALC_B 0 struct hclge_umv_spc_alc_cmd { u8 allocate; @@ -846,7 +864,9 @@ struct hclge_dev_specs_1_cmd { u8 rsv0[2]; __le16 umv_size; __le16 mc_mac_size; - u8 rsv1[6]; + u8 rsv1[2]; + __le16 guid_tbl_space; + __le16 ip_tbl_space; u8 tnl_num; u8 rsv2[5]; }; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c index cc4c6049fb64cfe7890c7eceb80a5c6585d42440..d7092b401cae5f83ef5569120bd2b142706904a6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c @@ -523,7 +523,8 @@ static int hclge_mqprio_qopt_check_rate(struct hclge_dev *hdev, u64 min_rate, if (!max_rate) return 0; - if (hnae3_dev_roh_supported(hdev)) { + if (hnae3_dev_roh_supported(hdev) || + hnae3_dev_ubl_supported(hdev->ae_dev)) { if (max_rate < TM_RATE_PORT_RATE_SCALE || max_speed > hdev->hw.mac.max_speed) { dev_err(&hdev->pdev->dev, @@ -624,7 +625,7 @@ static int hclge_config_tc(struct hclge_dev *hdev, if (ret) return ret; - if (hnae3_dev_roh_supported(hdev)) + if (hnae3_dev_roh_supported(hdev) || hnae3_dev_ubl_supported(hdev->ae_dev)) return hclge_tm_set_tc_rate_limit(hdev, tc_info); return 0; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index 14294f27e3b12713db0f479e39ab444bcbb83fae..54ce0cb8b05e40a244194d4f840e6df573e56eef 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -6,12 +6,14 @@ #include "hclge_debugfs.h" #include "hclge_err.h" #include "hclge_main.h" +#include "hclge_comm_unic_addr.h" #include "hclge_regs.h" #include "hclge_tm.h" +#include "hclge_udma.h" #include "hnae3.h" static const char * const state_str[] = { "off", "on" }; -static const char * const hclge_mac_state_str[] = { +static const char * const hclge_entry_state_str[] = { "TO_ADD", "TO_DEL", "ACTIVE" }; @@ -393,9 +395,14 @@ static void hclge_dbg_dump_mac_type(struct hclge_dev *hdev, char *buf, int len, { struct hclge_vport *vport = &hdev->vport[0]; struct hnae3_handle *handle = &vport->nic; + char *type; - *pos += scnprintf(buf + *pos, len - *pos, "type: %s\n", - handle->mac_type ? "ROH" : "Ethernet"); + if (hnae3_dev_ubl_supported(hdev->ae_dev)) + type = "UB"; + else + type = handle->mac_type ? "ROH" : "Ethernet"; + + *pos += scnprintf(buf + *pos, len - *pos, "type: %s\n", type); } static int hclge_dbg_dump_mac(struct hclge_dev *hdev, char *buf, int len) @@ -1814,6 +1821,8 @@ int hclge_dbg_dump_rst_info(struct hclge_dev *hdev, char *buf, int len) hclge_read_dev(&hdev->hw, offset)); } + hclge_dbg_dump_udma_rst_info(hdev, buf, len, &pos); + pos += scnprintf(buf + pos, len - pos, "hdev state: 0x%lx\n", hdev->state); @@ -2118,7 +2127,7 @@ static void hclge_dbg_dump_mac_list(struct hclge_dev *hdev, char *buf, int len, func_id); sprintf(result[i++], "%pM", mac_node->mac_addr); sprintf(result[i++], "%5s", - hclge_mac_state_str[mac_node->state]); + hclge_entry_state_str[mac_node->state]); hclge_dbg_fill_content(content, sizeof(content), mac_list_items, (const char **)result, @@ -2572,6 +2581,150 @@ static int hclge_dbg_dump_wol_info(struct hclge_dev *hdev, char *buf, int len) return 0; } +static int hclge_dbg_dump_ip_spec(struct hclge_dev *hdev, char *buf, int len) +{ + struct unic_ip_table_info *iptbl_info = &hdev->iptbl_info; + u8 func_num = pci_num_vf(hdev->pdev) + 1; + struct hclge_vport *vport; + int pos = 0; + u8 i; + + pos += scnprintf(buf, len, "num_alloc_vport : %u\n", + hdev->num_alloc_vport); + pos += scnprintf(buf + pos, len - pos, "max_ip_table_size : %u\n", + iptbl_info->max_iptbl_size); + pos += scnprintf(buf + pos, len - pos, "priv_ip_table_size : %u\n", + iptbl_info->priv_iptbl_size); + + mutex_lock(&hdev->vport_lock); + pos += scnprintf(buf + pos, len - pos, "share_ip_table_size : %u\n", + iptbl_info->share_iptbl_size); + for (i = 0; i < func_num; i++) { + vport = &hdev->vport[i]; + pos += scnprintf(buf + pos, len - pos, + "vport(%u) used_ip_table_num : %u\n", + i, vport->used_iptbl_num); + } + mutex_unlock(&hdev->vport_lock); + + return 0; +} + +static int hclge_dbg_dump_guid_spec(struct hclge_dev *hdev, char *buf, int len) +{ + u16 mc_guid_tbl_size; + + mc_guid_tbl_size = min(HCLGE_UNIC_MC_GUID_NUM, + hdev->ae_dev->dev_specs.guid_tbl_space - + HCLGE_VPORT_NUM); + scnprintf(buf, len, "function guid tbl size: %u\nmc guid tbl size: %u\n", + HCLGE_VPORT_NUM, mc_guid_tbl_size); + + return 0; +} + +#define HCLGE_UNIC_DBG_DATA_STR_LEN 50 +#define HCLGE_UNIC_IPV6_LEN 16 + +static const struct hclge_dbg_item ip_list_items[] = { + { "FUNC_ID", 2 }, + { "IP_ADDR", 34 }, + { "STATE", 2 }, +}; + +static int hclge_dbg_dump_ip_list(struct hclge_dev *hdev, char *buf, int len) +{ + char data_str[ARRAY_SIZE(ip_list_items)][HCLGE_UNIC_DBG_DATA_STR_LEN]; + char content[HCLGE_DBG_INFO_LEN], str_id[HCLGE_DBG_ID_LEN]; + struct hclge_comm_unic_addr_node *ip_node, *tmp; + char *result[ARRAY_SIZE(ip_list_items)]; + struct hclge_vport *vport; + struct list_head *list; + u16 used_iptbl_num = 0; + u32 func_id; + int pos = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(ip_list_items); i++) + result[i] = &data_str[i][0]; + + for (i = 0; i < hdev->num_alloc_vport; i++) + used_iptbl_num += hdev->vport[i].used_iptbl_num; + + pos += scnprintf(buf + pos, len - pos, "used ip number: %u\n", + used_iptbl_num); + + hclge_dbg_fill_content(content, sizeof(content), ip_list_items, + NULL, ARRAY_SIZE(ip_list_items)); + pos += scnprintf(buf + pos, len - pos, "%s", content); + + for (func_id = 0; func_id < hdev->num_alloc_vport; func_id++) { + vport = &hdev->vport[func_id]; + list = &vport->ip_list; + spin_lock_bh(&vport->ip_list_lock); + list_for_each_entry_safe(ip_node, tmp, list, node) { + i = 0; + result[i++] = hclge_dbg_get_func_id_str(str_id, + func_id); + sprintf(result[i++], "%pI6c", &ip_node->ip_addr.s6_addr); + sprintf(result[i++], "%5s", + hclge_entry_state_str[ip_node->state]); + hclge_dbg_fill_content(content, sizeof(content), + ip_list_items, + (const char **)result, + ARRAY_SIZE(ip_list_items)); + + if (len - pos < strlen(content)) { + spin_unlock_bh(&vport->ip_list_lock); + dev_warn(&hdev->pdev->dev, + "Warning: IP list debugfs buffer overflow.\n"); + return 0; + } + + pos += scnprintf(buf + pos, len - pos, "%s", content); + } + spin_unlock_bh(&vport->ip_list_lock); + } + return 0; +} + +static int hclge_dbg_dump_guid_list(struct hclge_dev *hdev, char *buf, int len) +{ + char format_guid_addr[HCLGE_COMM_FORMAT_GUID_ADDR_LEN]; + struct hclge_comm_unic_addr_node *guid_node, *tmp; + char str_id[HCLGE_DBG_ID_LEN]; + struct hclge_vport *vport; + struct list_head *list; + u16 func_id; + int pos = 0; + u16 i; + + pos += scnprintf(buf + pos, len - pos, "used mc guid number: %u\n", + hdev->used_mc_guid_num); + pos += scnprintf(buf + pos, len - pos, "mc guid table bitmap: "); + for (i = 0; i < BITS_TO_LONGS(HCLGE_UNIC_MC_GUID_NUM); i++) + pos += scnprintf(buf + pos, len - pos, "%lx ", + hdev->mc_guid_tbl_bmap[i]); + pos += scnprintf(buf + pos, len - pos, "\nMC GUID LIST:\n"); + pos += scnprintf(buf + pos, len - pos, "No. FUNC_ID %-48s STATE\n", "MC_GUID"); + for (func_id = 0, i = 0; func_id < hdev->num_alloc_vport; func_id++) { + vport = &hdev->vport[func_id]; + list = &vport->mc_guid_list; + spin_lock_bh(&vport->mguid_list_lock); + list_for_each_entry_safe(guid_node, tmp, list, node) { + hclge_comm_format_guid_addr(format_guid_addr, + guid_node->mguid); + pos += scnprintf(buf + pos, len - pos, + "%-3d %-7s %-48s %s\n", i++, + hclge_dbg_get_func_id_str(str_id, func_id), + format_guid_addr, + hclge_entry_state_str[guid_node->state]); + } + spin_unlock_bh(&vport->mguid_list_lock); + } + return 0; +} + static const struct hclge_dbg_func hclge_dbg_cmd_func[] = { { .cmd = HNAE3_DBG_CMD_TM_NODES, @@ -2725,6 +2878,22 @@ static const struct hclge_dbg_func hclge_dbg_cmd_func[] = { .cmd = HNAE3_DBG_CMD_WOL_INFO, .dbg_dump = hclge_dbg_dump_wol_info, }, + { + .cmd = HNAE3_DBG_CMD_IP_SPEC, + .dbg_dump = hclge_dbg_dump_ip_spec, + }, + { + .cmd = HNAE3_DBG_CMD_GUID_SPEC, + .dbg_dump = hclge_dbg_dump_guid_spec, + }, + { + .cmd = HNAE3_DBG_CMD_IP_LIST, + .dbg_dump = hclge_dbg_dump_ip_list, + }, + { + .cmd = HNAE3_DBG_CMD_GUID_LIST, + .dbg_dump = hclge_dbg_dump_guid_list, + }, }; int hclge_dbg_read_cmd(struct hnae3_handle *handle, enum hnae3_dbg_cmd cmd, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c index 4c8743fe1c6ba4356e7a132aa50c128d58e13b2a..bd849e06c65859af92737d01c5c549f15620dd74 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.c @@ -2,6 +2,7 @@ /* Copyright (c) 2016-2017 Hisilicon Limited. */ #include "hclge_err.h" +#include "hclge_udma.h" static const struct hclge_hw_error hclge_imp_tcm_ecc_int[] = { { @@ -1245,6 +1246,12 @@ static const struct hclge_hw_module_id hclge_hw_module_id_st[] = { }, { .module_id = MODULE_HIMAC, .msg = "MODULE_HIMAC" + }, { + .module_id = MODULE_PFA, + .msg = "MODULE_PFA" + }, { + .module_id = MODULE_TXPM, + .msg = "MODULE_TXPM" }, { .module_id = MODULE_ROCEE_TOP, .msg = "MODULE_ROCEE_TOP" @@ -2751,7 +2758,7 @@ void hclge_handle_all_hns_hw_errors(struct hnae3_ae_dev *ae_dev) bool hclge_find_error_source(struct hclge_dev *hdev) { - u32 msix_src_flag, hw_err_src_flag; + u32 msix_src_flag, hw_err_src_flag, udma_err_src_flag; msix_src_flag = hclge_read_dev(&hdev->hw, HCLGE_MISC_VECTOR_INT_STS) & HCLGE_VECTOR0_REG_MSIX_MASK; @@ -2759,8 +2766,10 @@ bool hclge_find_error_source(struct hclge_dev *hdev) hw_err_src_flag = hclge_read_dev(&hdev->hw, HCLGE_RAS_PF_OTHER_INT_STS_REG) & HCLGE_RAS_REG_ERR_MASK; + udma_err_src_flag = hclge_get_udma_error_reg(hdev) & + HCLGE_RAS_REG_ERR_MASK_UB; - return msix_src_flag || hw_err_src_flag; + return msix_src_flag || hw_err_src_flag || udma_err_src_flag; } void hclge_handle_occurred_error(struct hclge_dev *hdev) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h index 6d66483e17c284fa38240e6f0069480570e3f7f6..aea392dce165d61a178aedb52eb8b672f78b8ad6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_err.h @@ -141,6 +141,8 @@ enum hclge_mod_name_list { MODULE_TXDMA = 13, MODULE_MASTER = 14, MODULE_HIMAC = 15, + MODULE_PFA = 16, + MODULE_TXPM = 17, /* add new MODULE NAME for NIC here in order */ MODULE_ROCEE_TOP = 40, MODULE_ROCEE_TIMER = 41, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 373af8118dd3521cbdc890847a4f79fa8dbb163f..e9159f2988db73fe6353b7b2103509892bb7a621 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -16,6 +16,7 @@ #include #include #include +#include "ubl.h" #include "hclge_cmd.h" #include "hclge_dcb.h" #include "hclge_ext.h" @@ -28,6 +29,11 @@ #include "hnae3.h" #include "hclge_devlink.h" #include "hclge_comm_cmd.h" +#include "hclge_udma.h" +#include "hclge_comm_unic_addr.h" +#include "hclge_unic_ip.h" +#include "hclge_unic_guid.h" +#include "hclge_unic_addr.h" #define HCLGE_NAME "hclge" @@ -79,6 +85,11 @@ static const struct pci_device_id ae_algo_pci_tbl[] = { {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA), 0}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_ROH), 0}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_400G_ROH), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA), 0}, +#ifdef CONFIG_HNS3_UBL + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA_OVER_UBL), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_OVER_UBL), 0}, +#endif /* required last entry */ {0, } }; @@ -785,6 +796,7 @@ static int hclge_query_function_status(struct hclge_dev *hdev) static int hclge_query_pf_resource(struct hclge_dev *hdev) { + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); struct hclge_pf_res_cmd *req; struct hclge_desc desc; int ret; @@ -837,6 +849,11 @@ static int hclge_query_pf_resource(struct hclge_dev *hdev) */ hdev->num_msi = hdev->num_nic_msi + hdev->num_roce_msi + hdev->num_roh_msi; + } else if (hnae3_dev_udma_supported(ae_dev)) { + hdev->num_udma_msi = + le16_to_cpu(req->pf_intr_vector_number_roce); + + hdev->num_msi = hdev->num_nic_msi + hdev->num_udma_msi; } else { hdev->num_msi = hdev->num_nic_msi; } @@ -1358,12 +1375,35 @@ static void hclge_parse_dev_specs(struct hclge_dev *hdev, ae_dev->dev_specs.umv_size = le16_to_cpu(req1->umv_size); ae_dev->dev_specs.mc_mac_size = le16_to_cpu(req1->mc_mac_size); ae_dev->dev_specs.tnl_num = req1->tnl_num; +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(ae_dev)) { + ae_dev->dev_specs.guid_tbl_space = + le16_to_cpu(req1->guid_tbl_space); + ae_dev->dev_specs.ip_tbl_space = + le16_to_cpu(req1->ip_tbl_space); + } +#endif } static void hclge_check_dev_specs(struct hclge_dev *hdev) { struct hnae3_dev_specs *dev_specs = &hdev->ae_dev->dev_specs; +#ifdef CONFIG_HNS3_UBL + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); + if (hnae3_dev_ubl_supported(ae_dev)) { + if (!dev_specs->guid_tbl_space) { + dev_warn(&hdev->pdev->dev, + "Can't get guid table size from firmware!\n"); + dev_specs->guid_tbl_space = HCLGE_DEFAULT_GUID_TBL_SIZE; + } + if (!dev_specs->ip_tbl_space) { + dev_warn(&hdev->pdev->dev, + "Can't get ip table size from firmware!\n"); + dev_specs->ip_tbl_space = HCLGE_DEFAULT_IP_TBL_SIZE; + } + } +#endif if (!dev_specs->max_non_tso_bd_num) dev_specs->max_non_tso_bd_num = HCLGE_MAX_NON_TSO_BD_NUM; if (!dev_specs->rss_ind_tbl_size) @@ -1539,6 +1579,11 @@ static int hclge_configure(struct hclge_dev *hdev) hdev->fd_en = true; hdev->fd_active_type = HCLGE_FD_RULE_NONE; } +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(ae_dev)) + hdev->iptbl_info.max_iptbl_size = + hdev->ae_dev->dev_specs.ip_tbl_space; +#endif ret = hclge_parse_speed(cfg.default_speed, &hdev->hw.mac.speed); if (ret) { @@ -1824,6 +1869,15 @@ static int hclge_alloc_vport(struct hclge_dev *hdev) vport->vport_id = i; vport->vf_info.link_state = IFLA_VF_LINK_STATE_AUTO; vport->mps = HCLGE_MAC_DEFAULT_FRAME; +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + vport->mps = UB_DATA_LEN; + INIT_LIST_HEAD(&vport->mc_guid_list); + spin_lock_init(&vport->mguid_list_lock); + INIT_LIST_HEAD(&vport->ip_list); + spin_lock_init(&vport->ip_list_lock); + } +#endif vport->port_base_vlan_cfg.state = HNAE3_PORT_BASE_VLAN_DISABLE; vport->port_base_vlan_cfg.tbl_sta = true; vport->rxvlan_cfg.rx_vlan_offload_en = true; @@ -2928,8 +2982,10 @@ static void hclge_push_link_status(struct hclge_dev *hdev) static void hclge_update_link_status(struct hclge_dev *hdev) { struct hnae3_handle *rhandle = &hdev->vport[0].roce; + struct hnae3_handle *uhandle = &hdev->vport[0].udma; struct hnae3_handle *handle = &hdev->vport[0].nic; struct hnae3_client *rclient = hdev->roce_client; + struct hnae3_client *uclient = hdev->udma_client; struct hnae3_client *client = hdev->nic_client; int state; int ret; @@ -2952,6 +3008,8 @@ static void hclge_update_link_status(struct hclge_dev *hdev) hclge_config_mac_tnl_int(hdev, state); if (rclient && rclient->ops->link_status_change) rclient->ops->link_status_change(rhandle, state); + if (uclient && uclient->ops->link_status_change) + uclient->ops->link_status_change(uhandle, state); hclge_push_link_status(hdev); } @@ -3406,13 +3464,14 @@ static int hclge_set_vf_link_state(struct hnae3_handle *handle, int vf, static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) { - u32 cmdq_src_reg, msix_src_reg, hw_err_src_reg; + u32 cmdq_src_reg, msix_src_reg, hw_err_src_reg, udma_err_src_reg; /* fetch the events from their corresponding regs */ cmdq_src_reg = hclge_read_dev(&hdev->hw, HCLGE_VECTOR0_CMDQ_SRC_REG); msix_src_reg = hclge_read_dev(&hdev->hw, HCLGE_MISC_VECTOR_INT_STS); hw_err_src_reg = hclge_read_dev(&hdev->hw, HCLGE_RAS_PF_OTHER_INT_STS_REG); + udma_err_src_reg = hclge_get_udma_error_reg(hdev); /* Assumption: If by any chance reset and mailbox events are reported * together then we will only process reset event in this go and will @@ -3442,7 +3501,8 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) /* check for vector0 msix event and hardware error event source */ if (msix_src_reg & HCLGE_VECTOR0_REG_MSIX_MASK || - hw_err_src_reg & HCLGE_RAS_REG_ERR_MASK) + hw_err_src_reg & HCLGE_RAS_REG_ERR_MASK || + udma_err_src_reg & HCLGE_RAS_REG_ERR_MASK_UB) return HCLGE_VECTOR0_EVENT_ERR; /* check for vector0 ptp event source */ @@ -3460,8 +3520,9 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) /* print other vector0 event source */ dev_info(&hdev->pdev->dev, - "INT status: CMDQ(%#x) HW errors(%#x) other(%#x)\n", - cmdq_src_reg, hw_err_src_reg, msix_src_reg); + "INT status: CMDQ(%#x) HW errors(%#x, %#x) other(%#x)\n", + cmdq_src_reg, hw_err_src_reg, udma_err_src_reg, + msix_src_reg); return HCLGE_VECTOR0_EVENT_OTHER; } @@ -4196,6 +4257,10 @@ static int hclge_reset_prepare(struct hclge_dev *hdev) if (ret) return ret; + ret = hclge_notify_udma_client(hdev, HNAE3_DOWN_CLIENT); + if (ret) + return ret; + rtnl_lock(); ret = hclge_notify_client(hdev, HNAE3_DOWN_CLIENT); rtnl_unlock(); @@ -4220,6 +4285,10 @@ static int hclge_reset_rebuild(struct hclge_dev *hdev) if (ret) return ret; + ret = hclge_notify_udma_client(hdev, HNAE3_UNINIT_CLIENT); + if (ret) + return ret; + rtnl_lock(); ret = hclge_reset_stack(hdev); rtnl_unlock(); @@ -4244,6 +4313,14 @@ static int hclge_reset_rebuild(struct hclge_dev *hdev) hdev->rst_stats.reset_fail_cnt < HCLGE_RESET_MAX_FAIL_CNT - 1) return ret; + ret = hclge_notify_udma_client(hdev, HNAE3_INIT_CLIENT); + /* ignore udma notify error if it fails HCLGE_RESET_MAX_FAIL_CNT - 1 + * times + */ + if (ret && + hdev->rst_stats.reset_fail_cnt < HCLGE_RESET_MAX_FAIL_CNT - 1) + return ret; + ret = hclge_reset_prepare_up(hdev); if (ret) return ret; @@ -4262,6 +4339,10 @@ static int hclge_reset_rebuild(struct hclge_dev *hdev) if (ret) return ret; + ret = hclge_notify_udma_client(hdev, HNAE3_UP_CLIENT); + if (ret) + return ret; + hdev->last_reset_time = jiffies; hdev->rst_stats.reset_fail_cnt = 0; hdev->rst_stats.reset_done_cnt++; @@ -4685,6 +4766,12 @@ static void hclge_periodic_service_task(struct hclge_dev *hdev) */ hclge_update_link_status(hdev); hclge_sync_mac_table(hdev); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclge_unic_sync_mguid_table(hdev); + hclge_unic_sync_ip_table(hdev); + } +#endif hclge_sync_promisc_mode(hdev); hclge_sync_fd_qb_mode(hdev); hclge_sync_fd_table(hdev); @@ -4771,6 +4858,8 @@ struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle) return container_of(handle, struct hclge_vport, roce); else if (handle->client->type == HNAE3_CLIENT_ROH) return container_of(handle, struct hclge_vport, roh); + else if (handle->client->type == HNAE3_CLIENT_UDMA) + return container_of(handle, struct hclge_vport, udma); else return container_of(handle, struct hclge_vport, nic); } @@ -8430,6 +8519,12 @@ int hclge_vport_start(struct hclge_vport *vport) if (vport->vport_id) { hclge_restore_mac_table_common(vport); hclge_restore_vport_vlan_table(vport); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclge_unic_restore_mc_guid_table(vport); + hclge_unic_restore_ip_table(vport); + } +#endif } else { hclge_restore_hw_table(hdev); } @@ -10641,6 +10736,12 @@ static void hclge_restore_hw_table(struct hclge_dev *hdev) set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state); clear_bit(HCLGE_STATE_HW_QB_ENABLE, &hdev->state); hclge_restore_fd_entries(handle); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclge_unic_restore_mc_guid_table(vport); + hclge_unic_restore_ip_table(vport); + } +#endif } int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable) @@ -11030,16 +11131,28 @@ static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu) int hclge_set_vport_mtu(struct hclge_vport *vport, int new_mtu) { + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(vport->nic.pdev); + int l2_hlen = ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN; + int default_size = HCLGE_MAC_DEFAULT_FRAME; + int min_frm_size = HCLGE_MAC_MIN_FRAME; struct hclge_dev *hdev = vport->back; int i, max_frm_size, ret; +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(ae_dev)) { + /* UB MTU */ + l2_hlen = 0; + min_frm_size = UB_MIN_MTU; + default_size = UB_DATA_LEN; + } +#endif /* HW supprt 2 layer vlan */ - max_frm_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + 2 * VLAN_HLEN; - if (max_frm_size < HCLGE_MAC_MIN_FRAME || + max_frm_size = new_mtu + l2_hlen; + if (max_frm_size < min_frm_size || max_frm_size > hdev->ae_dev->dev_specs.max_frm_size) return -EINVAL; - max_frm_size = max(max_frm_size, HCLGE_MAC_DEFAULT_FRAME); + max_frm_size = max(max_frm_size, default_size); mutex_lock(&hdev->vport_lock); /* VF's mps must fit within hdev->mps */ if (vport->vport_id && max_frm_size > hdev->mps) { @@ -11681,6 +11794,10 @@ static int hclge_init_client_instance(struct hnae3_client *client, if (ret) goto clear_roce; + ret = hclge_init_udma_client_instance(ae_dev, vport); + if (ret) + goto clear_udma; + break; case HNAE3_CLIENT_ROCE: if (hnae3_dev_roce_supported(hdev)) { @@ -11701,6 +11818,17 @@ static int hclge_init_client_instance(struct hnae3_client *client, if (ret) goto clear_roh; + break; + case HNAE3_CLIENT_UDMA: + if (hnae3_dev_udma_supported(ae_dev)) { + hdev->udma_client = client; + vport->udma.client = client; + } + + ret = hclge_init_udma_client_instance(ae_dev, vport); + if (ret) + goto clear_udma; + break; default: return -EINVAL; @@ -11720,6 +11848,10 @@ static int hclge_init_client_instance(struct hnae3_client *client, hdev->roh_client = NULL; vport->roh.client = NULL; return ret; +clear_udma: + hdev->udma_client = NULL; + vport->udma.client = NULL; + return ret; } static void hclge_uninit_client_instance(struct hnae3_client *client, @@ -11728,6 +11860,19 @@ static void hclge_uninit_client_instance(struct hnae3_client *client, struct hclge_dev *hdev = ae_dev->priv; struct hclge_vport *vport = &hdev->vport[0]; + if (hdev->udma_client && (client->type == HNAE3_CLIENT_UDMA || + client->type == HNAE3_CLIENT_KNIC)) { + clear_bit(HCLGE_STATE_UDMA_REGISTERED, &hdev->state); + while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) + msleep(HCLGE_WAIT_RESET_DONE); + + hdev->udma_client->ops->uninit_instance(&vport->udma, 0); + hdev->udma_client = NULL; + vport->udma.client = NULL; + } + if (client->type == HNAE3_CLIENT_UDMA) + return; + if (hdev->roh_client && (client->type == HNAE3_CLIENT_ROH || client->type == HNAE3_CLIENT_KNIC)) { clear_bit(HCLGE_STATE_ROH_REGISTERED, &hdev->state); @@ -12217,6 +12362,14 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_mdiobus_unreg; +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(ae_dev)) { + ret = hclge_unic_init_iptbl_info(hdev); + if (ret) + goto err_mdiobus_unreg; + } +#endif + ret = hclge_mac_init(hdev); if (ret) { dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret); @@ -12594,6 +12747,12 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev) memset(hdev->vf_vlan_full, 0, sizeof(hdev->vf_vlan_full)); bitmap_set(hdev->vport_config_block, 0, hdev->num_alloc_vport); hclge_reset_umv_space(hdev); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(ae_dev)) { + hclge_unic_reset_iptbl_space(hdev); + hclge_unic_reset_mc_guid_space(hdev); + } +#endif } ret = hclge_comm_cmd_init(hdev->ae_dev, &hdev->hw.hw, &hdev->fw_version, @@ -12728,6 +12887,13 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) hclge_ptp_uninit(hdev); hclge_uninit_rxd_adv_layout(hdev); hclge_uninit_mac_table(hdev); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(ae_dev)) { + hclge_unic_uninit_mguid_table(hdev); + hclge_unic_uninit_ip_table(hdev); + hclge_unic_rm_func_guid(hdev); + } +#endif hclge_del_all_fd_entries(hdev); if (mac->phydev) @@ -12969,7 +13135,8 @@ static int hclge_sync_vport_promisc_mode(struct hclge_vport *vport) /* for VF */ if (vport->vf_info.trusted) { uc_en = vport->vf_info.request_uc_en > 0 || - vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE; + vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE || + vport->overflow_promisc_flags & HNAE3_OVERFLOW_MGP; mc_en = vport->vf_info.request_mc_en > 0 || vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE; } @@ -13302,6 +13469,12 @@ struct hnae3_ae_ops hclge_ops = { .get_wol = hclge_get_wol, .set_wol = hclge_set_wol, .priv_ops = hclge_ext_ops_handle, +#ifdef CONFIG_HNS3_UBL + .add_addr = hclge_unic_add_addr, + .rm_addr = hclge_unic_rm_addr, + .get_func_guid = hclge_unic_get_func_guid, + .set_func_guid = hclge_unic_set_func_guid, +#endif }; static struct hnae3_ae_algo ae_algo = { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index ce9a2fd1af124a817c9159b31d2044903bf64e98..6c9209a51b2744b6e5da8d66363d8d86272da930 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -97,6 +97,8 @@ #define HCLGE_UMV_TBL_SIZE 3072 #define HCLGE_DEFAULT_UMV_SPACE_PER_PF \ (HCLGE_UMV_TBL_SIZE / HCLGE_MAX_PF_NUM) +#define HCLGE_DEFAULT_GUID_TBL_SIZE 64 +#define HCLGE_DEFAULT_IP_TBL_SIZE 1024 #define HCLGE_TQP_RESET_TRY_TIMES 200 @@ -205,6 +207,7 @@ enum HCLGE_DEV_STATE { HCLGE_STATE_NIC_REGISTERED, HCLGE_STATE_ROCE_REGISTERED, HCLGE_STATE_ROH_REGISTERED, + HCLGE_STATE_UDMA_REGISTERED, HCLGE_STATE_SERVICE_INITED, HCLGE_STATE_RST_SERVICE_SCHED, HCLGE_STATE_RST_HANDLING, @@ -286,6 +289,10 @@ struct hclge_mac { __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); }; +#ifndef UBL_ALEN +#define UBL_ALEN 16 +#endif + struct hclge_hw { struct hclge_comm_hw hw; struct hclge_mac mac; @@ -808,6 +815,18 @@ struct hclge_vf_vlan_cfg { #pragma pack() +struct unic_ip_table_info { + u16 max_iptbl_size; + /* private ip table space, it's same for PF and its VFs */ + u16 priv_iptbl_size; + /* ip table space shared by PF and its VFs */ + u16 share_iptbl_size; + /* store ip addr to assemble */ + struct sockaddr_in6 ipaddr_to_assemble; + /* save upper ip addr subcode, NOT SET: 255 */ + u8 upper_ip_addr_state; +}; + /* For each bit of TCAM entry, it uses a pair of 'x' and * 'y' to indicate which value to match, like below: * ---------------------------------- @@ -834,6 +853,8 @@ struct hclge_vf_vlan_cfg { #define HCLGE_MAC_TNL_LOG_SIZE 8 #define HCLGE_VPORT_NUM 256 + +#define HCLGE_UNIC_MC_GUID_NUM 64 struct hclge_dev { struct pci_dev *pdev; struct hnae3_ae_dev *ae_dev; @@ -889,6 +910,7 @@ struct hclge_dev { u16 num_nic_msi; /* Num of nic vectors for this PF */ u16 num_roce_msi; /* Num of roce vectors for this PF */ u16 num_roh_msi; /* Num of roh vectors for this PF */ + u16 num_udma_msi; /* Num of udma vectors for this PF */ unsigned long service_timer_period; unsigned long service_timer_previous; @@ -906,6 +928,7 @@ struct hclge_dev { struct hnae3_client *nic_client; struct hnae3_client *roce_client; struct hnae3_client *roh_client; + struct hnae3_client *udma_client; #define HCLGE_FLAG_MAIN BIT(0) #define HCLGE_FLAG_DCB_CAPABLE BIT(1) @@ -949,6 +972,12 @@ struct hclge_dev { /* multicast mac address number used by PF and its VFs */ u16 used_mc_mac_num; + struct unic_ip_table_info iptbl_info; + + /* multicast guid number used by PF and its VFs */ + u16 used_mc_guid_num; + DECLARE_BITMAP(mc_guid_tbl_bmap, HCLGE_UNIC_MC_GUID_NUM); + DECLARE_KFIFO(mac_tnl_log, struct hclge_mac_tnl_stats, HCLGE_MAC_TNL_LOG_SIZE); @@ -990,6 +1019,8 @@ enum HCLGE_VPORT_STATE { HCLGE_VPORT_STATE_PROMISC_CHANGE, HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE, HCLGE_VPORT_STATE_INITED, + HCLGE_VPORT_STATE_GUID_TBL_CHANGE, + HCLGE_VPORT_STATE_IP_TBL_CHANGE, HCLGE_VPORT_STATE_MAX }; @@ -1040,11 +1071,14 @@ struct hclge_vport { u16 used_umv_num; + u16 used_iptbl_num; + u16 vport_id; struct hclge_dev *back; /* Back reference to associated dev */ struct hnae3_handle nic; struct hnae3_handle roce; struct hnae3_handle roh; + struct hnae3_handle udma; unsigned long state; unsigned long need_notify; @@ -1060,6 +1094,12 @@ struct hclge_vport { struct list_head mc_mac_list; /* Store VF multicast table */ struct list_head vlan_list; /* Store VF vlan table */ + + spinlock_t mguid_list_lock; /* protect mc guid need to add/detele */ + struct list_head mc_guid_list; /* Store VF mc guid table */ + + spinlock_t ip_list_lock; /* protect ip address need to add/detele */ + struct list_head ip_list; /* Store VF ip table */ }; struct hclge_speed_bit_map { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c index b429f700d4eceaf43627b1870be6981da53a7d4c..61e0adb4b2b009ce3f68e87f2688e0367958e7e3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c @@ -5,6 +5,9 @@ #include "hclge_mbx.h" #include "hnae3.h" #include "hclge_comm_rss.h" +#include "hclge_comm_unic_addr.h" +#include "hclge_unic_guid.h" +#include "hclge_unic_ip.h" #define CREATE_TRACE_POINTS #include "hclge_trace.h" @@ -825,6 +828,12 @@ static void hclge_handle_vf_tbl(struct hclge_vport *vport, hclge_rm_vport_all_mac_table(vport, true, HCLGE_MAC_ADDR_UC); hclge_rm_vport_all_mac_table(vport, true, HCLGE_MAC_ADDR_MC); hclge_rm_vport_all_vlan_table(vport, true); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclge_unic_del_vport_all_mc_guid_table(vport, true); + hclge_unic_rm_vport_all_ip_table(vport, true); + } +#endif } else { dev_warn(&hdev->pdev->dev, "Invalid cmd(%u)\n", msg_cmd->subcode); @@ -1021,21 +1030,37 @@ static int hclge_mbx_get_link_mode_handler(struct hclge_mbx_ops_param *param) static int hclge_mbx_get_vf_flr_status_handler(struct hclge_mbx_ops_param *param) { + struct hclge_dev *hdev = param->vport->back; + hclge_rm_vport_all_mac_table(param->vport, false, HCLGE_MAC_ADDR_UC); hclge_rm_vport_all_mac_table(param->vport, false, HCLGE_MAC_ADDR_MC); hclge_rm_vport_all_vlan_table(param->vport, false); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclge_unic_del_vport_all_mc_guid_table(param->vport, false); + hclge_unic_rm_vport_all_ip_table(param->vport, false); + } +#endif return 0; } static int hclge_mbx_vf_uninit_handler(struct hclge_mbx_ops_param *param) { + struct hclge_dev *hdev = param->vport->back; + hclge_rm_vport_all_mac_table(param->vport, true, HCLGE_MAC_ADDR_UC); hclge_rm_vport_all_mac_table(param->vport, true, HCLGE_MAC_ADDR_MC); hclge_rm_vport_all_vlan_table(param->vport, true); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclge_unic_del_vport_all_mc_guid_table(param->vport, false); + hclge_unic_rm_vport_all_ip_table(param->vport, false); + } +#endif param->vport->mps = 0; return 0; } @@ -1076,6 +1101,34 @@ static int hclge_mbx_handle_vf_qb_handler(struct hclge_mbx_ops_param *param) return 0; } +#ifdef CONFIG_HNS3_UBL +static int hclge_unic_mbx_set_mc_guid_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_unic_set_vf_mc_guid(param->vport, param->req); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF fail(%d) to set VF MC guid\n", + ret); + return ret; +} + +static int +hclge_unic_mbx_set_vf_ip_addr_handler(struct hclge_mbx_ops_param *param) +{ + int ret; + + ret = hclge_unic_set_vf_ip_addr(param->vport, param->req); + if (ret) + dev_err(¶m->vport->back->pdev->dev, + "PF fail(%d) to set VF IP Addr\n", + ret); + + return ret; +} +#endif + static const hclge_mbx_ops_fn hclge_mbx_ops_list[HCLGE_MBX_OPCODE_MAX] = { [HCLGE_MBX_RESET] = hclge_mbx_reset_handler, [HCLGE_MBX_SET_UNICAST] = hclge_mbx_set_unicast_handler, @@ -1101,6 +1154,10 @@ static const hclge_mbx_ops_fn hclge_mbx_ops_list[HCLGE_MBX_OPCODE_MAX] = { [HCLGE_MBX_HANDLE_VF_TBL] = hclge_mbx_handle_vf_tbl_handler, [HCLGE_MBX_GET_RING_VECTOR_MAP] = hclge_mbx_get_ring_vector_map_handler, [HCLGE_MBX_SET_QB] = hclge_mbx_handle_vf_qb_handler, +#ifdef CONFIG_HNS3_UBL + [HCLGE_MBX_SET_MGUID] = hclge_unic_mbx_set_mc_guid_handler, + [HCLGE_UNIC_MBX_SET_IP] = hclge_unic_mbx_set_vf_ip_addr_handler, +#endif [HCLGE_MBX_GET_VF_FLR_STATUS] = hclge_mbx_get_vf_flr_status_handler, [HCLGE_MBX_PUSH_LINK_STATUS] = hclge_mbx_push_link_status_handler, [HCLGE_MBX_NCSI_ERROR] = hclge_mbx_ncsi_error_handler, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index 2b7086cb62d20d31011b1ec5020ca3f3928d1d88..b3c11cfcddd11049886fb0a849a7e67f3615835e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -1734,7 +1734,8 @@ int hclge_tm_init_hw(struct hclge_dev *hdev, bool init) if (ret) return ret; - if (hnae3_dev_roh_supported(hdev)) + if (hnae3_dev_roh_supported(hdev) || + hnae3_dev_ubl_supported(hdev->ae_dev)) return hclge_tm_set_tc_rate_limit(hdev, tc_info); return 0; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_udma.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_udma.c new file mode 100644 index 0000000000000000000000000000000000000000..f4ca53136b69c8ff6c9231afe74ca058f1cc3828 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_udma.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include "hclge_main.h" +#include "hclge_err.h" +#include "hclge_debugfs.h" +#include "hclge_udma.h" + +static const struct hclge_dbg_status_dfx_info hclge_dbg_rst_info_ub[] = { + {HCLGE_RAS_PF_OTHER_INT_STS_REG_UB, "UB RAS interrupt status"} +}; + +static int hclge_init_udma_base_info(struct hclge_vport *vport) +{ + struct hnae3_handle *udma = &vport->udma; + struct hnae3_handle *nic = &vport->nic; + struct hclge_dev *hdev = vport->back; + + udma->udmainfo.num_vectors = vport->back->num_udma_msi; + + if (hdev->num_msi < hdev->num_nic_msi + hdev->num_udma_msi) + return -EINVAL; + + udma->udmainfo.base_vector = hdev->num_nic_msi; + + udma->udmainfo.netdev = nic->kinfo.netdev; + udma->udmainfo.udma_io_base = hdev->hw.hw.io_base; + udma->udmainfo.udma_mem_base = hdev->hw.hw.mem_base; + + udma->pdev = nic->pdev; + udma->ae_algo = nic->ae_algo; + udma->numa_node_mask = nic->numa_node_mask; + + return 0; +} + +int hclge_notify_udma_client(struct hclge_dev *hdev, + enum hnae3_reset_notify_type type) +{ + struct hnae3_handle *handle = &hdev->vport[0].udma; + struct hnae3_client *client = hdev->udma_client; + int ret; + + if (!test_bit(HCLGE_STATE_UDMA_REGISTERED, &hdev->state) || !client) + return 0; + + if (!client->ops->reset_notify) + return -EOPNOTSUPP; + + ret = client->ops->reset_notify(handle, type); + if (ret) + dev_err(&hdev->pdev->dev, "notify udma client failed %d(%d)", + type, ret); + + return ret; +} + +int hclge_init_udma_client_instance(struct hnae3_ae_dev *ae_dev, + struct hclge_vport *vport) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hnae3_client *client; + int rst_cnt; + int ret; + + if (!hnae3_dev_udma_supported(ae_dev) || !hdev->udma_client || + !hdev->nic_client) + return 0; + + client = hdev->udma_client; + ret = hclge_init_udma_base_info(vport); + if (ret) + return ret; + + rst_cnt = hdev->rst_stats.reset_cnt; + ret = client->ops->init_instance(&vport->udma); + if (ret) + return ret; + + set_bit(HCLGE_STATE_UDMA_REGISTERED, &hdev->state); + if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) || + rst_cnt != hdev->rst_stats.reset_cnt) { + ret = -EBUSY; + goto init_udma_err; + } + + hnae3_set_client_init_flag(client, ae_dev, 1); + + return 0; + +init_udma_err: + clear_bit(HCLGE_STATE_UDMA_REGISTERED, &hdev->state); + while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) + msleep(HCLGE_WAIT_RESET_DONE); + + hdev->udma_client->ops->uninit_instance(&vport->udma, 0); + + return ret; +} + +u32 hclge_get_udma_error_reg(struct hclge_dev *hdev) +{ + u32 hw_err_src_reg = 0; + + if (hnae3_dev_ubl_supported(hdev->ae_dev) || + hnae3_dev_udma_supported(hdev->ae_dev)) + hw_err_src_reg = hclge_read_dev(&hdev->hw, + HCLGE_RAS_PF_OTHER_INT_STS_REG_UB); + + return hw_err_src_reg; +} + +void hclge_dbg_dump_udma_rst_info(struct hclge_dev *hdev, char *buf, int len, + int *pos) +{ + u32 i, offset; + + if (hnae3_dev_ubl_supported(hdev->ae_dev) || + hnae3_dev_udma_supported(hdev->ae_dev)) { + for (i = 0; i < ARRAY_SIZE(hclge_dbg_rst_info_ub); i++) { + offset = hclge_dbg_rst_info_ub[i].offset; + *pos += scnprintf(buf + *pos, len - *pos, "%s: 0x%x\n", + hclge_dbg_rst_info_ub[i].message, + hclge_read_dev(&hdev->hw, offset)); + } + } +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_udma.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_udma.h new file mode 100644 index 0000000000000000000000000000000000000000..b3a99df54218f466abb303916b734526f125f091 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_udma.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGE_UDMA_H +#define __HCLGE_UDMA_H + +#include "hclge_main.h" + +#define HCLGE_RAS_PF_OTHER_INT_STS_REG_UB 0x20B04 +#define HCLGE_RAS_REG_NFE_MASK_UB 0x12 +#define HCLGE_RAS_REG_ERR_MASK_UB HCLGE_RAS_REG_NFE_MASK_UB + +int hclge_notify_udma_client(struct hclge_dev *hdev, + enum hnae3_reset_notify_type type); +int hclge_init_udma_client_instance(struct hnae3_ae_dev *ae_dev, + struct hclge_vport *vport); +u32 hclge_get_udma_error_reg(struct hclge_dev *hdev); +void hclge_dbg_dump_udma_rst_info(struct hclge_dev *hdev, char *buf, int len, + int *pos); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_addr.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_addr.c new file mode 100644 index 0000000000000000000000000000000000000000..9ae5ca22537f7ccca967116f24143330babac679 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_addr.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include "hclge_comm_unic_addr.h" +#include "hclge_main.h" +#include "hclge_mbx.h" +#include "hclge_unic_guid.h" +#include "hclge_unic_ip.h" +#include "hclge_unic_addr.h" + +int hclge_unic_add_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + switch (addr_type) { + case HNAE3_UNIC_IP_ADDR: + return hclge_unic_update_ip_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_ADD, + (const struct sockaddr *)addr); + case HNAE3_UNIC_MCGUID_ADDR: + return hclge_unic_update_guid_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_ADD, + addr); + default: + return -EINVAL; + } +} + +int hclge_unic_rm_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + switch (addr_type) { + case HNAE3_UNIC_IP_ADDR: + return hclge_unic_update_ip_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + (const struct sockaddr *)addr); + case HNAE3_UNIC_MCGUID_ADDR: + return hclge_unic_update_guid_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + addr); + default: + return -EINVAL; + } +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_addr.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_addr.h new file mode 100644 index 0000000000000000000000000000000000000000..8ad9fbbaad85ab63656287a02dca71d242da50d0 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_addr.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGE_UNIC_ADDR_H +#define __HCLGE_UNIC_ADDR_H + +#include "hnae3.h" + +int hclge_unic_add_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type); +int hclge_unic_rm_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_guid.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_guid.c new file mode 100644 index 0000000000000000000000000000000000000000..afbffa0aafc20f19ab8d99f08a1dce876327f43e --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_guid.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include + +#include "ubl.h" +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_mbx.h" +#include "hclge_comm_unic_addr.h" +#include "hnae3.h" +#include "hclge_unic_guid.h" + +static bool hclge_unic_need_sync_guid_table(struct hclge_vport *vport) +{ + struct hclge_dev *hdev = vport->back; + + if (test_bit(vport->vport_id, hdev->vport_config_block)) + return false; + + if (test_and_clear_bit(HCLGE_VPORT_STATE_GUID_TBL_CHANGE, &vport->state)) + return true; + + return false; +} + +int hclge_unic_update_guid_list(struct hclge_vport *vport, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const unsigned char *addr) +{ + char format_guid_addr[HCLGE_COMM_FORMAT_GUID_ADDR_LEN]; + struct hclge_dev *hdev = vport->back; + int ret; + + ret = hclge_comm_unic_update_addr_list(&vport->mc_guid_list, + &vport->mguid_list_lock, + state, addr); + + if (ret == -ENOENT) { + hclge_comm_format_guid_addr(format_guid_addr, addr); + dev_err(&hdev->pdev->dev, + "failed to delete guid %s from mc guid list\n", + format_guid_addr); + } + + if (!ret) + set_bit(HCLGE_VPORT_STATE_GUID_TBL_CHANGE, &vport->state); + + return ret; +} + +static int hclge_unic_lookup_mc_guid(struct hclge_vport *vport, + struct hclge_unic_mc_guid_cfg_cmd *req, + struct hclge_desc *desc) +{ + struct hclge_unic_mc_guid_cfg_cmd *resp; + struct hclge_dev *hdev = vport->back; + u16 resp_code; + u16 retval; + int ret; + + resp = (struct hclge_unic_mc_guid_cfg_cmd *)desc[0].data; + hnae3_set_bit(req->vld_lookup_flag, HCLGE_UNIC_LOOKUP_EN_B, 1); + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_CFG_MC_GUID_CMD, true); + desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + memcpy(desc[0].data, req, sizeof(struct hclge_unic_mc_guid_cfg_cmd)); + hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_CFG_MC_GUID_CMD, true); + desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_CFG_MC_GUID_CMD, true); + ret = hclge_cmd_send(&hdev->hw, desc, 3); + if (ret) { + dev_err(&hdev->pdev->dev, "lookup mc guid failed for cmd_send, ret = %d\n", + ret); + return ret; + } + resp_code = resp->hit_info; + retval = le16_to_cpu(desc[0].retval); + if (retval) { + dev_err(&hdev->pdev->dev, "cmdq execute failed for lookup mc guid, status = %u.\n", + retval); + return -EIO; + } else if (!(resp_code & HCLGE_UNIC_GUID_HIT)) { + dev_dbg(&hdev->pdev->dev, "lookup mc guid failed for miss.\n"); + return -ENOENT; + } + + return ret; +} + +static int hclge_unic_fill_add_desc(struct hclge_vport *vport, + struct hclge_unic_mc_guid_cfg_cmd *req, + struct hclge_desc *desc, + bool is_new_guid) +{ + struct hclge_unic_mc_guid_cfg_cmd *rsp; + struct hclge_dev *hdev = vport->back; + u16 mc_guid_tbl_size; + + mc_guid_tbl_size = min(HCLGE_UNIC_MC_GUID_NUM, + hdev->ae_dev->dev_specs.guid_tbl_space - + HCLGE_VPORT_NUM); + if (is_new_guid) { + req->index = find_first_zero_bit(hdev->mc_guid_tbl_bmap, + HCLGE_UNIC_MC_GUID_NUM); + if (req->index >= mc_guid_tbl_size) + return -ENOSPC; + } else { + rsp = (struct hclge_unic_mc_guid_cfg_cmd *)desc[0].data; + req->index = rsp->index; + } + + if (vport->vport_id >= HCLGE_VPORT_NUM) + return -EIO; + req->ad_data = req->index; + if (vport->vport_id >= HCLGE_UNIC_BIT_NUM_PER_BD && + test_and_set_bit(vport->vport_id - HCLGE_UNIC_BIT_NUM_PER_BD, + (unsigned long *)&desc[1].data[2])) + return -EEXIST; + else if (test_and_set_bit(vport->vport_id, + (unsigned long *)&desc[2].data[2])) + return -EEXIST; + + return 0; +} + +static int hclge_unic_add_mc_guid_cmd(struct hclge_vport *vport, + struct hclge_unic_mc_guid_cfg_cmd *req, + struct hclge_desc *desc) +{ + struct hclge_dev *hdev = vport->back; + u16 retval; + int ret; + + req->vld_lookup_flag = BIT(HCLGE_UNIC_ENTRY_VLD_B); + hclge_comm_cmd_reuse_desc(&desc[0], false); + desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_comm_cmd_reuse_desc(&desc[1], false); + desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_comm_cmd_reuse_desc(&desc[2], false); + desc[2].flag &= cpu_to_le16(~HCLGE_COMM_CMD_FLAG_NEXT); + memcpy(desc[0].data, req, sizeof(struct hclge_unic_mc_guid_cfg_cmd)); + ret = hclge_cmd_send(&hdev->hw, desc, 3); + if (ret) { + dev_err(&hdev->pdev->dev, "add mc guid failed, ret = %d\n", ret); + return ret; + } + retval = le16_to_cpu(desc[0].retval); + if (retval) { + dev_err(&hdev->pdev->dev, "cmdq execute failed for add mc guid, status = %u.\n", + retval); + return -EIO; + } + + return 0; +} + +int hclge_unic_add_mc_guid_common(struct hclge_vport *vport, + const unsigned char *mguid) +{ + struct hclge_unic_mc_guid_cfg_cmd req = {0}; + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc[3]; + bool is_new_guid = false; + int ret; + + memcpy(req.mguid, mguid, UBL_ALEN); + ret = hclge_unic_lookup_mc_guid(vport, &req, desc); + if (ret) { + if (hdev->used_mc_guid_num >= + hdev->ae_dev->dev_specs.guid_tbl_space - HCLGE_VPORT_NUM) + goto err_no_space; + is_new_guid = true; + memset(desc[0].data, 0, sizeof(desc[0].data)); + memset(desc[1].data, 0, sizeof(desc[0].data)); + memset(desc[2].data, 0, sizeof(desc[0].data)); + } + + ret = hclge_unic_fill_add_desc(vport, &req, desc, is_new_guid); + if (ret == -EEXIST) + return 0; + if (ret == -ENOSPC) + goto err_no_space; + if (ret) + return ret; + ret = hclge_unic_add_mc_guid_cmd(vport, &req, desc); + if (!ret && is_new_guid) { + set_bit(req.index, hdev->mc_guid_tbl_bmap); + hdev->used_mc_guid_num++; + } + + return 0; +err_no_space: + /* if already overflow, not to print each time */ + if (!(vport->overflow_promisc_flags & HNAE3_OVERFLOW_MGP)) { + vport->overflow_promisc_flags |= HNAE3_OVERFLOW_MGP; + dev_err(&hdev->pdev->dev, "mc guid table is full\n"); + } + + return -ENOSPC; +} + +static bool hclge_unic_is_all_function_deleted(struct hclge_desc *desc) +{ +#define HCLGE_UNIC_DWORD_OF_MGUID 4 + int i; + + for (i = 0; i < HCLGE_UNIC_DWORD_OF_MGUID; i++) { + if (desc[1].data[2 + i] || desc[2].data[2 + i]) + return false; + } + + return true; +} + +static int hclge_unic_fill_del_desc(struct hclge_vport *vport, + struct hclge_unic_mc_guid_cfg_cmd *req, + struct hclge_desc *desc) +{ + struct hclge_unic_mc_guid_cfg_cmd *rsp; + + if (vport->vport_id >= HCLGE_VPORT_NUM) + return -EIO; + rsp = (struct hclge_unic_mc_guid_cfg_cmd *)desc[0].data; + req->index = rsp->index; + req->ad_data = rsp->index; + if (vport->vport_id >= HCLGE_UNIC_BIT_NUM_PER_BD) + clear_bit(vport->vport_id - HCLGE_UNIC_BIT_NUM_PER_BD, + (unsigned long *)&desc[1].data[2]); + else + clear_bit(vport->vport_id, (unsigned long *)&desc[2].data[2]); + + return 0; +} + +static int hclge_unic_del_mc_guid_cmd(struct hclge_vport *vport, + struct hclge_unic_mc_guid_cfg_cmd *req, + struct hclge_desc *desc) +{ + struct hclge_dev *hdev = vport->back; + u16 retval; + int ret; + + req->vld_lookup_flag = 0; + hclge_comm_cmd_reuse_desc(&desc[0], false); + desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_comm_cmd_reuse_desc(&desc[1], false); + desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); + hclge_comm_cmd_reuse_desc(&desc[2], false); + desc[2].flag &= cpu_to_le16(~HCLGE_COMM_CMD_FLAG_NEXT); + memcpy(desc[0].data, req, sizeof(struct hclge_unic_mc_guid_cfg_cmd)); + ret = hclge_cmd_send(&hdev->hw, desc, 3); + if (ret) { + dev_err(&hdev->pdev->dev, "del mc guid failed, ret = %d\n", ret); + return ret; + } + retval = le16_to_cpu(desc[0].retval); + if (retval) { + dev_err(&hdev->pdev->dev, "cmdq execute failed for add mc guid, status = %u.\n", + retval); + return -EIO; + } + + return 0; +} + +int hclge_unic_del_mc_guid_common(struct hclge_vport *vport, + const unsigned char *mguid) +{ + struct hclge_unic_mc_guid_cfg_cmd req = {0}; + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc[3]; + int ret; + + memcpy(req.mguid, mguid, UBL_ALEN); + ret = hclge_unic_lookup_mc_guid(vport, &req, desc); + if (!ret) { + ret = hclge_unic_fill_del_desc(vport, &req, desc); + if (ret) + return ret; + if (hclge_unic_is_all_function_deleted(desc)) { + ret = hclge_unic_del_mc_guid_cmd(vport, &req, desc); + if (!ret) { + clear_bit(req.index, hdev->mc_guid_tbl_bmap); + hdev->used_mc_guid_num--; + } + } else { + return hclge_unic_add_mc_guid_cmd(vport, &req, desc); + } + } else if (ret == -ENOENT) { + ret = 0; + } + + return ret; +} + +static void hclge_unic_sync_vport_mguid_list(struct hnae3_handle *h, + struct list_head *list) +{ + struct hclge_vport *vport = container_of(h, struct hclge_vport, nic); + struct hclge_comm_unic_addr_node *guid_node, *tmp; + int ret; + + list_for_each_entry_safe(guid_node, tmp, list, node) { + ret = hclge_unic_add_mc_guid_common(vport, guid_node->mguid); + if (!ret) { + guid_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + } else { + set_bit(HCLGE_VPORT_STATE_GUID_TBL_CHANGE, + &vport->state); + + /* Mc guid can be reusable, even though there is no + * space to add new mc guid, we should check whether + * other mc guid are existing in hardware for reuse. + */ + if (ret != -ENOSPC) + break; + } + } +} + +static void hclge_unic_unsync_vport_mguid_list(struct hnae3_handle *h, + struct list_head *list) +{ + struct hclge_vport *vport = container_of(h, struct hclge_vport, nic); + struct hclge_comm_unic_addr_node *guid_node, *tmp; + int ret; + + list_for_each_entry_safe(guid_node, tmp, list, node) { + ret = hclge_unic_del_mc_guid_common(vport, guid_node->mguid); + if (!ret || ret == -ENOENT) { + list_del(&guid_node->node); + kfree(guid_node); + } else { + set_bit(HCLGE_VPORT_STATE_GUID_TBL_CHANGE, + &vport->state); + break; + } + } +} + +static void hclge_unic_sync_vport_guid_table(struct hclge_vport *vport) +{ + void (*unsync)(struct hnae3_handle *h, struct list_head *list); + void (*sync)(struct hnae3_handle *h, struct list_head *list); + bool all_added; + + sync = hclge_unic_sync_vport_mguid_list; + unsync = hclge_unic_unsync_vport_mguid_list; + all_added = hclge_comm_unic_sync_addr_table(&vport->nic, + &vport->mc_guid_list, + &vport->mguid_list_lock, + sync, unsync); + if (all_added) + vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_MGP; + else + vport->overflow_promisc_flags |= HNAE3_OVERFLOW_MGP; +} + +void hclge_unic_sync_mguid_table(struct hclge_dev *hdev) +{ + int i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + struct hclge_vport *vport = &hdev->vport[i]; + + if (!hclge_unic_need_sync_guid_table(vport)) + continue; + hclge_unic_sync_vport_guid_table(vport); + } +} + +/* remove all guid when uninitailize */ +static void hclge_unic_uninit_vport_guid_list(struct hclge_vport *vport) +{ + struct hclge_comm_unic_addr_node *guid_node, *tmp; + struct hclge_dev *hdev = vport->back; + struct list_head tmp_del_list, *list; + + INIT_LIST_HEAD(&tmp_del_list); + + list = &vport->mc_guid_list; + + spin_lock_bh(&vport->mguid_list_lock); + + list_for_each_entry_safe(guid_node, tmp, list, node) { + switch (guid_node->state) { + case HCLGE_COMM_UNIC_ADDR_TO_DEL: + case HCLGE_COMM_UNIC_ADDR_ACTIVE: + list_move_tail(&guid_node->node, &tmp_del_list); + break; + case HCLGE_COMM_UNIC_ADDR_TO_ADD: + list_del(&guid_node->node); + kfree(guid_node); + break; + } + } + + spin_unlock_bh(&vport->mguid_list_lock); + + hclge_unic_unsync_vport_mguid_list(&vport->nic, &tmp_del_list); + + if (!list_empty(&tmp_del_list)) + dev_warn(&hdev->pdev->dev, + "uninit mguid list for vport %u not completely.\n", + vport->vport_id); + + list_for_each_entry_safe(guid_node, tmp, &tmp_del_list, node) { + list_del(&guid_node->node); + kfree(guid_node); + } +} + +void hclge_unic_uninit_mguid_table(struct hclge_dev *hdev) +{ + struct hclge_vport *vport; + int i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + vport = &hdev->vport[i]; + hclge_unic_uninit_vport_guid_list(vport); + } + bitmap_zero(hdev->mc_guid_tbl_bmap, HCLGE_UNIC_MC_GUID_NUM); +} + +int hclge_unic_set_vf_mc_guid(struct hclge_vport *vport, + struct hclge_mbx_vf_to_pf_cmd *mbx_req) +{ + __le16 proto = *(__le16 *)(mbx_req->msg.data); + struct hclge_dev *hdev = vport->back; + __le16 *mguid_proto = NULL; + u8 mguid[UBL_ALEN]; + int ret = 0; + + memset(mguid, 0xff, UBL_ALEN); + mguid_proto = (__le16 *)&mguid[HCLGE_COMM_MGUID_PREFIX_LEN]; + *mguid_proto = proto; + + if (mbx_req->msg.subcode == HCLGE_MBX_MC_GUID_MC_ADD) { + ret = hclge_unic_update_guid_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_ADD, + (const u8 *)mguid); + } else if (mbx_req->msg.subcode == HCLGE_MBX_MC_GUID_MC_DELETE) { + ret = hclge_unic_update_guid_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + (const u8 *)mguid); + } else { + dev_err(&hdev->pdev->dev, + "failed to set mc guid, unknown subcode %u\n", + mbx_req->msg.subcode); + return -EIO; + } + + return ret; +} + +/* For global reset and imp reset, hardware will clear the guid table, + * so we change the guid state from ACTIVE to TO_ADD, then they + * can be restored in the service task after reset complete. Furtherly, + * the guid with state TO_DEL or DEL_FAIL are unnecessary to + * be restored after reset, so just remove these guid nodes from guid_list. + */ +void hclge_unic_restore_mc_guid_table(struct hclge_vport *vport) +{ + struct hclge_comm_unic_addr_node *guid_node, *tmp; + struct list_head *list = &vport->mc_guid_list; + + spin_lock_bh(&vport->mguid_list_lock); + + list_for_each_entry_safe(guid_node, tmp, list, node) { + if (guid_node->state == HCLGE_COMM_UNIC_ADDR_ACTIVE) { + guid_node->state = HCLGE_COMM_UNIC_ADDR_TO_ADD; + } else if (guid_node->state == HCLGE_COMM_UNIC_ADDR_TO_DEL) { + list_del(&guid_node->node); + kfree(guid_node); + } + } + set_bit(HCLGE_VPORT_STATE_GUID_TBL_CHANGE, &vport->state); + + spin_unlock_bh(&vport->mguid_list_lock); +} + +static void hclge_unic_build_del_list(struct list_head *list, + bool is_del_list, + struct list_head *tmp_del_list) +{ + struct hclge_comm_unic_addr_node *guid_node, *tmp; + + list_for_each_entry_safe(guid_node, tmp, list, node) { + switch (guid_node->state) { + case HCLGE_COMM_UNIC_ADDR_TO_DEL: + case HCLGE_COMM_UNIC_ADDR_ACTIVE: + list_move_tail(&guid_node->node, tmp_del_list); + break; + case HCLGE_COMM_UNIC_ADDR_TO_ADD: + if (is_del_list) { + list_del(&guid_node->node); + kfree(guid_node); + } + break; + } + } +} + +static void hclge_unic_unsync_del_list(struct hclge_vport *vport, + int (*unsync)(struct hclge_vport *vport, + const unsigned char *mguid), + bool is_del_list, + struct list_head *tmp_del_list) +{ + struct hclge_comm_unic_addr_node *guid_node, *tmp; + int ret; + + list_for_each_entry_safe(guid_node, tmp, tmp_del_list, node) { + ret = unsync(vport, guid_node->mguid); + if (!ret || ret == -ENOENT) { + /* clear all mac addr from hardware, but remain these + * mac addr in the mac list, and restore them after + * vf reset finished. + */ + if (!is_del_list && + guid_node->state == HCLGE_COMM_UNIC_ADDR_ACTIVE) { + guid_node->state = HCLGE_COMM_UNIC_ADDR_TO_ADD; + } else { + list_del(&guid_node->node); + kfree(guid_node); + } + } else if (is_del_list) { + guid_node->state = HCLGE_COMM_UNIC_ADDR_TO_DEL; + } + } +} + +void hclge_unic_del_vport_all_mc_guid_table(struct hclge_vport *vport, + bool is_del_list) +{ + struct hclge_dev *hdev = vport->back; + struct list_head tmp_del_list, *list; + + list = &vport->mc_guid_list; + INIT_LIST_HEAD(&tmp_del_list); + + if (!is_del_list) + set_bit(vport->vport_id, hdev->vport_config_block); + + spin_lock_bh(&vport->mguid_list_lock); + + hclge_unic_build_del_list(list, is_del_list, &tmp_del_list); + + spin_unlock_bh(&vport->mguid_list_lock); + + hclge_unic_unsync_del_list(vport, hclge_unic_del_mc_guid_common, + is_del_list, &tmp_del_list); + + spin_lock_bh(&vport->mguid_list_lock); + + hclge_comm_unic_sync_from_addr_del_list(&tmp_del_list, list); + + spin_unlock_bh(&vport->mguid_list_lock); +} + +void hclge_unic_reset_mc_guid_space(struct hclge_dev *hdev) +{ + hdev->used_mc_guid_num = 0; + bitmap_zero(hdev->mc_guid_tbl_bmap, HCLGE_UNIC_MC_GUID_NUM); +} + +int hclge_unic_set_func_guid(struct hnae3_handle *handle, u8 *guid) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_comm_unic_set_func_guid(&hdev->hw.hw, guid); +} + +int hclge_unic_get_func_guid(struct hnae3_handle *handle, u8 *guid) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_comm_unic_get_func_guid(&hdev->hw.hw, guid); +} + +void hclge_unic_rm_func_guid(struct hclge_dev *hdev) +{ + hclge_comm_unic_rm_func_guid(&hdev->hw.hw); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_guid.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_guid.h new file mode 100644 index 0000000000000000000000000000000000000000..c9b66400f51d6928f42d665d83c653f8b569cb45 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_guid.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +// Copyright (c) 2023 Hisilicon Limited. + +#ifndef __HCLGE_UNIC_GUID_H +#define __HCLGE_UNIC_GUID_H + +#include + +#include "hclge_mbx.h" + +struct hclge_dev; + +struct hclge_unic_mc_guid_cfg_cmd { + __le16 index; + u8 vld_lookup_flag; + u8 rsvd; + u8 mguid[UBL_ALEN]; + __le16 ad_data; + __le16 hit_info; +}; + +#define HCLGE_UNIC_BIT_NUM_PER_BD 128 + +#define HCLGE_UNIC_ENTRY_VLD_B 0 +#define HCLGE_UNIC_LOOKUP_EN_B 1 + +#define HCLGE_UNIC_GUID_HIT BIT(15) + +void hclge_unic_sync_mguid_table(struct hclge_dev *hdev); +void hclge_unic_uninit_mguid_table(struct hclge_dev *hdev); +int hclge_unic_set_vf_mc_guid(struct hclge_vport *vport, + struct hclge_mbx_vf_to_pf_cmd *mbx_req); +void hclge_unic_restore_mc_guid_table(struct hclge_vport *vport); +void hclge_unic_reset_mc_guid_space(struct hclge_dev *hdev); +void hclge_unic_del_vport_all_mc_guid_table(struct hclge_vport *vport, + bool is_del_list); +int hclge_unic_update_guid_list(struct hclge_vport *vport, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const unsigned char *addr); +int hclge_unic_set_func_guid(struct hnae3_handle *handle, u8 *guid); +int hclge_unic_get_func_guid(struct hnae3_handle *handle, u8 *guid); +void hclge_unic_rm_func_guid(struct hclge_dev *hdev); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_ip.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_ip.c new file mode 100644 index 0000000000000000000000000000000000000000..9028fc080aa0db219428f0b7c983d1da491301a2 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_ip.c @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include +#include + +#include "ubl.h" +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_comm_cmd.h" +#include "hclge_err.h" +#include "hclge_mbx.h" +#include "hclge_comm_unic_addr.h" +#include "hclge_unic_guid.h" +#include "hclge_unic_ip.h" + +static int hclge_unic_get_ip_tbl_cmd_status(struct hclge_vport *vport, + u16 cmdq_resp, u8 resp_code, + enum hclge_ip_tbl_opcode op) +{ + struct hclge_dev *hdev = vport->back; + + if (cmdq_resp) { + dev_err(&hdev->pdev->dev, + "cmdq execute failed for get_ip_tbl_cmd_status, status=%u.\n", + cmdq_resp); + return -EIO; + } + + if (op == HCLGE_IP_TBL_ADD) { + if (!resp_code || resp_code == 1) + return 0; + else if (resp_code == HCLGE_ADD_IP_TBL_OVERFLOW) + return -ENOSPC; + + dev_err(&hdev->pdev->dev, + "add ip addr failed for undefined, code=%u.\n", + resp_code); + return -EIO; + } else if (op == HCLGE_IP_TBL_REMOVE) { + if (!resp_code) { + return 0; + } else if (resp_code == 1) { + dev_dbg(&hdev->pdev->dev, + "remove ip addr failed for miss.\n"); + return -ENOENT; + } + + dev_err(&hdev->pdev->dev, + "remove ip addr failed for undefined, code=%u.\n", + resp_code); + return -EIO; + } else if (op == HCLGE_IP_TBL_LKUP) { + if (!resp_code) { + return 0; + } else if (resp_code == 1) { + dev_dbg(&hdev->pdev->dev, + "lookup ip addr failed for miss.\n"); + return -ENOENT; + } + + dev_err(&hdev->pdev->dev, + "lookup ip addr failed for undefined, code=%u.\n", + resp_code); + return -EIO; + } + + dev_err(&hdev->pdev->dev, + "unknown opcode for get_ip_tbl_cmd_status, opcode=%d.\n", op); + + return -EINVAL; +} + +static int hclge_unic_remove_ip_tbl(struct hclge_vport *vport, + struct hclge_ip_tbl_entry_cmd *req) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc; + u8 resp_code; + u16 retval; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_DEL_IP_TBL, false); + + memcpy(desc.data, req, sizeof(struct hclge_ip_tbl_entry_cmd)); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "del ip addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + resp_code = (le32_to_cpu(desc.data[0])) & 0xff; + retval = le16_to_cpu(desc.retval); + + return hclge_unic_get_ip_tbl_cmd_status(vport, retval, resp_code, + HCLGE_IP_TBL_REMOVE); +} + +static int hclge_unic_lookup_ip_tbl(struct hclge_vport *vport, + struct hclge_ip_tbl_entry_cmd *req, + struct hclge_desc *desc) +{ + struct hclge_dev *hdev = vport->back; + u8 resp_code; + u16 retval; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_ADD_IP_TBL, true); + + memcpy(desc[0].data, req, sizeof(struct hclge_ip_tbl_entry_cmd)); + + ret = hclge_cmd_send(&hdev->hw, desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "lookup ip addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + resp_code = (le32_to_cpu(desc[0].data[0])) & 0xff; + retval = le16_to_cpu(desc[0].retval); + + return hclge_unic_get_ip_tbl_cmd_status(vport, retval, resp_code, + HCLGE_IP_TBL_LKUP); +} + +static int hclge_unic_add_ip_tbl(struct hclge_vport *vport, + struct hclge_ip_tbl_entry_cmd *req) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc; + u8 resp_code; + u16 retval; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_ADD_IP_TBL, false); + + memcpy(desc.data, req, sizeof(struct hclge_ip_tbl_entry_cmd)); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "add ip addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + resp_code = (le32_to_cpu(desc.data[0])) & 0xff; + retval = le16_to_cpu(desc.retval); + + return hclge_unic_get_ip_tbl_cmd_status(vport, retval, resp_code, + HCLGE_IP_TBL_ADD); +} + +int hclge_unic_init_iptbl_info(struct hclge_dev *hdev) +{ + struct unic_ip_table_info *iptbl_info = &hdev->iptbl_info; + + iptbl_info->priv_iptbl_size = iptbl_info->max_iptbl_size / + (hdev->num_alloc_vport + 1); + iptbl_info->share_iptbl_size = iptbl_info->priv_iptbl_size + + iptbl_info->max_iptbl_size % (hdev->num_alloc_vport + 1); + + memset(&iptbl_info->ipaddr_to_assemble, 0, + sizeof(struct sockaddr_in6)); + iptbl_info->upper_ip_addr_state = HCLGE_UNIC_IP_ADDR_NOTSET; + + return 0; +} + +void hclge_unic_reset_iptbl_space(struct hclge_dev *hdev) +{ + struct unic_ip_table_info *iptbl_info = &hdev->iptbl_info; + struct hclge_vport *vport; + int i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + vport = &hdev->vport[i]; + vport->used_iptbl_num = 0; + } + + mutex_lock(&hdev->vport_lock); + iptbl_info->share_iptbl_size = iptbl_info->priv_iptbl_size + + iptbl_info->max_iptbl_size % (hdev->num_alloc_vport + 1); + mutex_unlock(&hdev->vport_lock); +} + +static bool hclge_unic_is_iptbl_space_full(struct hclge_vport *vport, + bool need_lock) +{ + struct hclge_dev *hdev = vport->back; + struct unic_ip_table_info *iptbl_info = &hdev->iptbl_info; + bool is_full; + + if (need_lock) + mutex_lock(&hdev->vport_lock); + + is_full = (vport->used_iptbl_num >= iptbl_info->priv_iptbl_size && + iptbl_info->share_iptbl_size == 0); + + if (need_lock) + mutex_unlock(&hdev->vport_lock); + + return is_full; +} + +static void hclge_unic_update_iptbl_space(struct hclge_vport *vport, + bool is_free) +{ + struct hclge_dev *hdev = vport->back; + struct unic_ip_table_info *iptbl_info = &hdev->iptbl_info; + + if (is_free) { + if (vport->used_iptbl_num > iptbl_info->priv_iptbl_size) + iptbl_info->share_iptbl_size++; + + if (vport->used_iptbl_num > 0) + vport->used_iptbl_num--; + } else { + if (vport->used_iptbl_num >= iptbl_info->priv_iptbl_size && + iptbl_info->share_iptbl_size > 0) + iptbl_info->share_iptbl_size--; + vport->used_iptbl_num++; + } +} + +int hclge_unic_update_ip_list(struct hclge_vport *vport, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const struct sockaddr *addr) +{ + struct hclge_dev *hdev = vport->back; + struct in6_addr ip_addr; + int ret; + + hclge_comm_unic_convert_ip_addr(addr, &ip_addr); + + ret = hclge_comm_unic_update_addr_list(&vport->ip_list, + &vport->ip_list_lock, + state, + (const unsigned char *)&ip_addr); + + if (ret == -ENOENT) + dev_err(&hdev->pdev->dev, + "failed to delete ip %pI6c from ip list\n", + ip_addr.s6_addr); + + if (!ret) + set_bit(HCLGE_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + + return ret; +} + +static int hclge_unic_add_ip_addr_common(struct hclge_vport *vport, + struct in6_addr *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_ip_tbl_entry_cmd req; + struct hclge_desc desc; + u16 dip_ad = 0; + int ret; + + memset(&req, 0, sizeof(req)); + + hnae3_set_field(dip_ad, HCLGE_IP_PORT_VFID_M, + HCLGE_IP_PORT_VFID_S, vport->vport_id); + + req.dip_ad = cpu_to_le16(dip_ad); + memcpy(req.ipaddr, addr->s6_addr, sizeof(req.ipaddr)); + + /* Lookup the ip address in the ip address table, and add + * it if the entry is inexistent. Repeated unicast entry + * is not allowed in the ip address table. + */ + ret = hclge_unic_lookup_ip_tbl(vport, &req, &desc); + if (ret == -ENOENT) { + mutex_lock(&hdev->vport_lock); + if (!hclge_unic_is_iptbl_space_full(vport, false)) { + ret = hclge_unic_add_ip_tbl(vport, &req); + if (!ret) + hclge_unic_update_iptbl_space(vport, false); + mutex_unlock(&hdev->vport_lock); + return ret; + } + mutex_unlock(&hdev->vport_lock); + + if (!(vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE)) + dev_err(&hdev->pdev->dev, "IP table full(%u)\n", + hdev->iptbl_info.priv_iptbl_size); + + return -ENOSPC; + } + + /* check if we just hit the duplicate */ + if (!ret) + return -EEXIST; + + return ret; +} + +static int hclge_unic_rm_ip_addr_common(struct hclge_vport *vport, + struct in6_addr *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_ip_tbl_entry_cmd req; + int ret; + + memset(&req, 0, sizeof(req)); + memcpy(req.ipaddr, addr->s6_addr, sizeof(req.ipaddr)); + ret = hclge_unic_remove_ip_tbl(vport, &req); + if (!ret || ret == -ENOENT) { + mutex_lock(&hdev->vport_lock); + hclge_unic_update_iptbl_space(vport, true); + mutex_unlock(&hdev->vport_lock); + return 0; + } + + return ret; +} + +static void hclge_unic_sync_vport_ip_list(struct hnae3_handle *h, + struct list_head *list) +{ + struct hclge_vport *vport = container_of(h, struct hclge_vport, nic); + struct hclge_comm_unic_addr_node *ip_node, *tmp; + int ret; + + list_for_each_entry_safe(ip_node, tmp, list, node) { + ret = hclge_unic_add_ip_addr_common(vport, &ip_node->ip_addr); + if (!ret) { + ip_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + } else { + set_bit(HCLGE_VPORT_STATE_IP_TBL_CHANGE, + &vport->state); + + if (ret != -EEXIST) + break; + } + } +} + +static void hclge_unic_unsync_vport_ip_list(struct hnae3_handle *h, + struct list_head *list) +{ + struct hclge_vport *vport = container_of(h, struct hclge_vport, nic); + struct hclge_comm_unic_addr_node *ip_node, *tmp; + int ret; + + list_for_each_entry_safe(ip_node, tmp, list, node) { + ret = hclge_unic_rm_ip_addr_common(vport, &ip_node->ip_addr); + if (!ret || ret == -ENOENT) { + list_del(&ip_node->node); + kfree(ip_node); + } else { + set_bit(HCLGE_VPORT_STATE_IP_TBL_CHANGE, + &vport->state); + break; + } + } +} + +static void hclge_unic_sync_vport_ip_table(struct hclge_vport *vport) +{ + void (*unsync)(struct hnae3_handle *h, struct list_head *list); + void (*sync)(struct hnae3_handle *h, struct list_head *list); + bool all_added; + + sync = hclge_unic_sync_vport_ip_list; + unsync = hclge_unic_unsync_vport_ip_list; + all_added = hclge_comm_unic_sync_addr_table(&vport->nic, + &vport->ip_list, + &vport->ip_list_lock, + sync, unsync); + + if (all_added) + vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_MPE; + else + vport->overflow_promisc_flags |= HNAE3_OVERFLOW_MPE; +} + +static bool hclge_unic_need_sync_ip_table(struct hclge_vport *vport) +{ + struct hclge_dev *hdev = vport->back; + + if (test_bit(vport->vport_id, hdev->vport_config_block)) + return false; + + if (test_and_clear_bit(HCLGE_VPORT_STATE_IP_TBL_CHANGE, &vport->state)) + return true; + + return false; +} + +void hclge_unic_sync_ip_table(struct hclge_dev *hdev) +{ + int i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + struct hclge_vport *vport = &hdev->vport[i]; + + if (!hclge_unic_need_sync_ip_table(vport)) + continue; + + hclge_unic_sync_vport_ip_table(vport); + } +} + +/* For global reset and imp reset, hardware will clear the ip table, + * so we change the ip state from ACTIVE to TO_ADD, then they + * can be restored in the service task after reset completed. Furtherly, + * the ip address with state TO_DEL are unnecessary to be restored + * after reset, so just remove these ip nodes from ip_list. + */ +void hclge_unic_restore_ip_table(struct hclge_vport *vport) +{ + struct hclge_comm_unic_addr_node *ip_node, *tmp; + struct list_head *list = &vport->ip_list; + + spin_lock_bh(&vport->ip_list_lock); + + list_for_each_entry_safe(ip_node, tmp, list, node) { + if (ip_node->state == HCLGE_COMM_UNIC_ADDR_ACTIVE) { + ip_node->state = HCLGE_COMM_UNIC_ADDR_TO_ADD; + } else if (ip_node->state == HCLGE_COMM_UNIC_ADDR_TO_DEL) { + list_del(&ip_node->node); + kfree(ip_node); + } + } + set_bit(HCLGE_VPORT_STATE_IP_TBL_CHANGE, &vport->state); + + spin_unlock_bh(&vport->ip_list_lock); +} + +static void hclge_unic_build_ip_del_list(struct list_head *list, + bool is_del_list, + struct list_head *tmp_del_list) +{ + struct hclge_comm_unic_addr_node *ip_cfg, *tmp; + + list_for_each_entry_safe(ip_cfg, tmp, list, node) { + switch (ip_cfg->state) { + case HCLGE_COMM_UNIC_ADDR_TO_DEL: + case HCLGE_COMM_UNIC_ADDR_ACTIVE: + list_move_tail(&ip_cfg->node, tmp_del_list); + break; + case HCLGE_COMM_UNIC_ADDR_TO_ADD: + if (is_del_list) { + list_del(&ip_cfg->node); + kfree(ip_cfg); + } + break; + } + } +} + +static void hclge_unic_unsync_ip_del_list(struct hclge_vport *vport, + int (*unsync)(struct hclge_vport *vport, + struct in6_addr *addr), + bool is_del_list, + struct list_head *tmp_del_list) +{ + struct hclge_comm_unic_addr_node *ip_cfg, *tmp; + int ret; + + list_for_each_entry_safe(ip_cfg, tmp, tmp_del_list, node) { + ret = unsync(vport, &ip_cfg->ip_addr); + if (!ret || ret == -ENOENT) { + /* clear all ip addr from hardware, but remain these + * ip addr in the ip list, and restore them after + * vf reset finished. + */ + if (!is_del_list && + ip_cfg->state == HCLGE_COMM_UNIC_ADDR_ACTIVE) { + ip_cfg->state = HCLGE_COMM_UNIC_ADDR_TO_ADD; + } else { + list_del(&ip_cfg->node); + kfree(ip_cfg); + } + } else if (is_del_list) { + ip_cfg->state = HCLGE_COMM_UNIC_ADDR_TO_DEL; + } + } +} + +void hclge_unic_rm_vport_all_ip_table(struct hclge_vport *vport, + bool is_del_list) +{ + int (*unsync)(struct hclge_vport *vport, struct in6_addr *addr); + struct hclge_dev *hdev = vport->back; + struct list_head tmp_del_list, *list; + + list = &vport->ip_list; + unsync = hclge_unic_rm_ip_addr_common; + INIT_LIST_HEAD(&tmp_del_list); + + if (!is_del_list) + set_bit(vport->vport_id, hdev->vport_config_block); + + spin_lock_bh(&vport->ip_list_lock); + hclge_unic_build_ip_del_list(list, is_del_list, &tmp_del_list); + spin_unlock_bh(&vport->ip_list_lock); + + hclge_unic_unsync_ip_del_list(vport, unsync, is_del_list, + &tmp_del_list); + + spin_lock_bh(&vport->ip_list_lock); + hclge_comm_unic_sync_from_addr_del_list(&tmp_del_list, list); + spin_unlock_bh(&vport->ip_list_lock); +} + +/* remove all ip address when uninitailize */ +static void hclge_unic_uninit_vport_ip_list(struct hclge_vport *vport) +{ + struct hclge_comm_unic_addr_node *ip_node, *tmp; + struct hclge_dev *hdev = vport->back; + struct list_head tmp_del_list, *list; + + INIT_LIST_HEAD(&tmp_del_list); + + list = &vport->ip_list; + + spin_lock_bh(&vport->ip_list_lock); + + list_for_each_entry_safe(ip_node, tmp, list, node) { + switch (ip_node->state) { + case HCLGE_COMM_UNIC_ADDR_TO_DEL: + case HCLGE_COMM_UNIC_ADDR_ACTIVE: + list_move_tail(&ip_node->node, &tmp_del_list); + break; + case HCLGE_COMM_UNIC_ADDR_TO_ADD: + list_del(&ip_node->node); + kfree(ip_node); + break; + } + } + + spin_unlock_bh(&vport->ip_list_lock); + + hclge_unic_unsync_vport_ip_list(&vport->nic, &tmp_del_list); + + if (!list_empty(&tmp_del_list)) + dev_warn(&hdev->pdev->dev, + "uninit ip list for vport %u not completely.\n", + vport->vport_id); + + list_for_each_entry_safe(ip_node, tmp, &tmp_del_list, node) { + list_del(&ip_node->node); + kfree(ip_node); + } +} + +void hclge_unic_uninit_ip_table(struct hclge_dev *hdev) +{ + struct hclge_vport *vport; + int i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + vport = &hdev->vport[i]; + hclge_unic_uninit_vport_ip_list(vport); + } +} + +static int hclge_unic_iptbl_parse_subcode(u8 msg_subcode, + struct hclge_vport *vport, + struct sockaddr *ip_addr) +{ + struct hclge_dev *hdev = vport->back; + + switch (msg_subcode) { + case HCLGE_UNIC_MBX_IP_TABLE_ADD: + return hclge_unic_update_ip_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_ADD, + (const struct sockaddr *)ip_addr); + case HCLGE_UNIC_MBX_IP_TABLE_REMOVE: + return hclge_unic_update_ip_list(vport, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + (const struct sockaddr *)ip_addr); + default: + dev_err(&hdev->pdev->dev, + "failed to set ip addr, unknown subcode %u\n", + msg_subcode); + return -EIO; + } +} + +int hclge_unic_set_vf_ip_addr(struct hclge_vport *vport, + struct hclge_mbx_vf_to_pf_cmd *mbx_req) +{ + struct hclge_dev *hdev = vport->back; + struct unic_ip_table_info *iptbl_info = &hdev->iptbl_info; + struct sockaddr_in6 *ip_addr = &iptbl_info->ipaddr_to_assemble; + char *lower_ip_addr = &ip_addr->sin6_addr.s6_addr[HCLGE_COMM_UNIC_IPV6_UPPER_LEN]; + char *msg_ip_addr = &mbx_req->msg.data[HCLGE_COMM_UNIC_MSG_IPADDR_POS]; + int ret; + + if (iptbl_info->upper_ip_addr_state == HCLGE_UNIC_IP_ADDR_NOTSET && + mbx_req->msg.data[0] == HCLGE_COMM_UNIC_IPV6_UPPER_LEN) { + memcpy(&ip_addr->sin6_addr.s6_addr, msg_ip_addr, + sizeof(u8) * HCLGE_COMM_UNIC_IPV6_UPPER_LEN); + iptbl_info->upper_ip_addr_state = mbx_req->msg.subcode; + + return 0; + } else if (mbx_req->msg.subcode == iptbl_info->upper_ip_addr_state && + mbx_req->msg.data[0] == HCLGE_COMM_UNIC_IPV6_LOWER_LEN) { + memcpy(lower_ip_addr, msg_ip_addr, + sizeof(u8) * HCLGE_COMM_UNIC_IPV6_LOWER_LEN); + + ip_addr->sin6_family = AF_INET6; + ret = hclge_unic_iptbl_parse_subcode(mbx_req->msg.subcode, + vport, + (struct sockaddr *)ip_addr); + } else { + dev_err(&hdev->pdev->dev, + "failed to configure ip table, unknown subcode %u or different ip addr\n", + mbx_req->msg.subcode); + ret = -EIO; + } + memset(ip_addr, 0, sizeof(struct sockaddr_in6)); + iptbl_info->upper_ip_addr_state = HCLGE_UNIC_IP_ADDR_NOTSET; + + return ret; +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_ip.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_ip.h new file mode 100644 index 0000000000000000000000000000000000000000..f5f6339b7db2e5a5c38628116c576718ef3f68d2 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_unic_ip.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGE_UNIC_IP_H +#define __HCLGE_UNIC_IP_H + +#include +#include +#include + +struct hclge_dev; + +#define HCLGE_UNIC_IP_ADDR_NOTSET 255 + +int hclge_unic_init_iptbl_info(struct hclge_dev *hdev); +void hclge_unic_reset_iptbl_space(struct hclge_dev *hdev); +void hclge_unic_sync_ip_table(struct hclge_dev *hdev); +void hclge_unic_restore_ip_table(struct hclge_vport *vport); +void hclge_unic_rm_vport_all_ip_table(struct hclge_vport *vport, + bool is_del_list); +void hclge_unic_uninit_ip_table(struct hclge_dev *hdev); +int hclge_unic_set_vf_ip_addr(struct hclge_vport *vport, + struct hclge_mbx_vf_to_pf_cmd *mbx_req); +int hclge_unic_update_ip_list(struct hclge_vport *vport, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const struct sockaddr *addr); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 970354cebea4a88b4ae1adb3b26181ba5308152a..0a381de67749839f81b3bcd33ec05a3d76d9e865 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -11,6 +11,11 @@ #include "hnae3.h" #include "hclgevf_devlink.h" #include "hclge_comm_rss.h" +#include "hclgevf_udma.h" +#include "hclge_comm_unic_addr.h" +#include "hclgevf_unic_ip.h" +#include "hclgevf_unic_guid.h" +#include "hclgevf_unic_addr.h" #define HCLGEVF_NAME "hclgevf" @@ -28,6 +33,14 @@ static const struct pci_device_id ae_algovf_pci_tbl[] = { {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_VF), 0}, {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_DCB_PFC_VF), HNAE3_DEV_SUPPORT_ROCE_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA_VF), + HNAE3_DEV_SUPPORT_UDMA_DCB_BITS}, +#ifdef CONFIG_HNS3_UBL + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_UDMA_OVER_UBL_VF), + HNAE3_DEV_SUPPORT_UDMA_OVER_UBL_DCB_BITS}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_RDMA_OVER_UBL_VF), + HNAE3_DEV_SUPPORT_ROCE_OVER_UBL_DCB_BITS}, +#endif /* required last entry */ {0, } }; @@ -66,6 +79,8 @@ struct hclgevf_dev *hclgevf_ae_get_hdev(struct hnae3_handle *handle) return container_of(handle, struct hclgevf_dev, nic); else if (handle->client->type == HNAE3_CLIENT_ROCE) return container_of(handle, struct hclgevf_dev, roce); + else if (handle->client->type == HNAE3_CLIENT_UDMA) + return container_of(handle, struct hclgevf_dev, udma); else return container_of(handle, struct hclgevf_dev, nic); } @@ -106,8 +121,8 @@ static void hclgevf_get_stats(struct hnae3_handle *handle, u64 *data) hclge_comm_tqps_get_stats(handle, data); } -static void hclgevf_build_send_msg(struct hclge_vf_to_pf_msg *msg, u8 code, - u8 subcode) +void hclgevf_build_send_msg(struct hclge_vf_to_pf_msg *msg, u8 code, + u8 subcode) { if (msg) { memset(msg, 0, sizeof(struct hclge_vf_to_pf_msg)); @@ -437,8 +452,10 @@ static void hclgevf_request_link_info(struct hclgevf_dev *hdev) void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state) { struct hnae3_handle *rhandle = &hdev->roce; + struct hnae3_handle *uhandle = &hdev->udma; struct hnae3_handle *handle = &hdev->nic; struct hnae3_client *rclient; + struct hnae3_client *uclient; struct hnae3_client *client; if (test_and_set_bit(HCLGEVF_STATE_LINK_UPDATING, &hdev->state)) @@ -446,6 +463,7 @@ void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state) client = handle->client; rclient = hdev->roce_client; + uclient = hdev->udma_client; link_state = test_bit(HCLGEVF_STATE_DOWN, &hdev->state) ? 0 : link_state; @@ -454,6 +472,8 @@ void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state) client->ops->link_status_change(handle, !!link_state); if (rclient && rclient->ops->link_status_change) rclient->ops->link_status_change(rhandle, !!link_state); + if (uclient && uclient->ops->link_status_change) + uclient->ops->link_status_change(uhandle, !!link_state); } clear_bit(HCLGEVF_STATE_LINK_UPDATING, &hdev->state); @@ -1589,6 +1609,10 @@ static int hclgevf_reset_prepare(struct hclgevf_dev *hdev) if (ret) return ret; + ret = hclgevf_notify_udma_client(hdev, HNAE3_DOWN_CLIENT); + if (ret) + return ret; + rtnl_lock(); /* bring down the nic to stop any ongoing TX/RX */ ret = hclgevf_notify_client(hdev, HNAE3_DOWN_CLIENT); @@ -1608,6 +1632,10 @@ static int hclgevf_reset_rebuild(struct hclgevf_dev *hdev) if (ret) return ret; + ret = hclgevf_notify_udma_client(hdev, HNAE3_UNINIT_CLIENT); + if (ret) + return ret; + rtnl_lock(); /* now, re-initialize the nic client and ae device */ ret = hclgevf_reset_stack(hdev); @@ -1629,6 +1657,18 @@ static int hclgevf_reset_rebuild(struct hclgevf_dev *hdev) if (ret) return ret; + ret = hclgevf_notify_udma_client(hdev, HNAE3_INIT_CLIENT); + /* ignore UDMA notify error if it fails HCLGEVF_RESET_MAX_FAIL_CNT - 1 + * times + */ + if (ret && + hdev->rst_stats.rst_fail_cnt < HCLGEVF_RESET_MAX_FAIL_CNT - 1) + return ret; + + ret = hclgevf_notify_udma_client(hdev, HNAE3_UP_CLIENT); + if (ret) + return ret; + hdev->last_reset_time = jiffies; hdev->rst_stats.rst_done_cnt++; hdev->rst_stats.rst_fail_cnt = 0; @@ -1959,6 +1999,12 @@ static void hclgevf_periodic_service_task(struct hclgevf_dev *hdev) hclgevf_sync_mac_table(hdev); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclgevf_unic_sync_mc_guid_list(hdev); + hclgevf_unic_sync_ip_list(hdev); + } +#endif hclgevf_sync_promisc_mode(hdev); hclgevf_update_fd_qb_state(hdev); @@ -2310,6 +2356,14 @@ static void hclgevf_state_init(struct hclgevf_dev *hdev) spin_lock_init(&hdev->mac_table.mac_list_lock); INIT_LIST_HEAD(&hdev->mac_table.uc_mac_list); INIT_LIST_HEAD(&hdev->mac_table.mc_mac_list); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + spin_lock_init(&hdev->mguid_list_lock); + INIT_LIST_HEAD(&hdev->mc_guid_list); + spin_lock_init(&hdev->ip_table.ip_list_lock); + INIT_LIST_HEAD(&hdev->ip_table.ip_list); + } +#endif /* bring the device down */ set_bit(HCLGEVF_STATE_DOWN, &hdev->state); @@ -2328,11 +2382,12 @@ static void hclgevf_state_uninit(struct hclgevf_dev *hdev) static int hclgevf_init_msi(struct hclgevf_dev *hdev) { + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); struct pci_dev *pdev = hdev->pdev; int vectors; int i; - if (hnae3_dev_roce_supported(hdev)) + if (hnae3_dev_roce_supported(hdev) || hnae3_dev_udma_supported(ae_dev)) vectors = pci_alloc_irq_vectors(pdev, hdev->roce_base_msix_offset + 1, hdev->num_msi, @@ -2508,6 +2563,11 @@ static int hclgevf_init_client_instance(struct hnae3_client *client, if (ret) goto clear_roce; + ret = hclgevf_init_udma_client_instance(ae_dev, + hdev->udma_client); + if (ret) + goto clear_udma; + break; case HNAE3_CLIENT_ROCE: if (hnae3_dev_roce_supported(hdev)) { @@ -2519,6 +2579,17 @@ static int hclgevf_init_client_instance(struct hnae3_client *client, if (ret) goto clear_roce; + break; + case HNAE3_CLIENT_UDMA: + if (hnae3_dev_udma_supported(ae_dev)) { + hdev->udma_client = client; + hdev->udma.client = client; + } + + ret = hclgevf_init_udma_client_instance(ae_dev, client); + if (ret) + goto clear_udma; + break; default: return -EINVAL; @@ -2534,6 +2605,10 @@ static int hclgevf_init_client_instance(struct hnae3_client *client, hdev->roce_client = NULL; hdev->roce.client = NULL; return ret; +clear_udma: + hdev->udma_client = NULL; + hdev->udma.client = NULL; + return ret; } static void hclgevf_uninit_client_instance(struct hnae3_client *client, @@ -2541,6 +2616,20 @@ static void hclgevf_uninit_client_instance(struct hnae3_client *client, { struct hclgevf_dev *hdev = ae_dev->priv; + /* un-init udma, if it exists and called by nic or udma client */ + if (hdev->udma_client && (client->type == HNAE3_CLIENT_UDMA || + client->type == HNAE3_CLIENT_KNIC)) { + while (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) + msleep(HCLGEVF_WAIT_RESET_DONE); + clear_bit(HCLGEVF_STATE_UDMA_REGISTERED, &hdev->state); + + hdev->udma_client->ops->uninit_instance(&hdev->udma, 0); + hdev->udma_client = NULL; + hdev->udma.client = NULL; + } + if (client->type == HNAE3_CLIENT_UDMA) + return; + /* un-init roce, if it exists */ if (hdev->roce_client) { while (test_bit(HCLGEVF_STATE_RST_HANDLING, &hdev->state)) @@ -2651,6 +2740,7 @@ static void hclgevf_pci_uninit(struct hclgevf_dev *hdev) static int hclgevf_query_vf_resource(struct hclgevf_dev *hdev) { + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev); struct hclgevf_query_res_cmd *req; struct hclge_desc desc; int ret; @@ -2682,6 +2772,23 @@ static int hclgevf_query_vf_resource(struct hclgevf_dev *hdev) */ hdev->num_msi = hdev->num_roce_msix + hdev->roce_base_msix_offset; + } else if (hnae3_dev_udma_supported(ae_dev)) { + hdev->roce_base_msix_offset = + hnae3_get_field(le16_to_cpu(req->msixcap_localid_ba_rocee), + HCLGEVF_MSIX_OFT_ROCEE_M, + HCLGEVF_MSIX_OFT_ROCEE_S); + hdev->num_udma_msix = + hnae3_get_field(le16_to_cpu(req->vf_intr_vector_number), + HCLGEVF_VEC_NUM_M, HCLGEVF_VEC_NUM_S); + + /* nic's msix numbers is always equals to the udma's. */ + hdev->num_nic_msix = hdev->num_udma_msix; + + /* VF should have NIC vectors and UDMA vectors, NIC vectors + * are queued before UDMA vectors. The offset is fixed to 64. + */ + hdev->num_msi = hdev->num_udma_msix + + hdev->roce_base_msix_offset; } else { hdev->num_msi = hnae3_get_field(le16_to_cpu(req->vf_intr_vector_number), @@ -3040,6 +3147,12 @@ static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev) hclgevf_devlink_uninit(hdev); hclgevf_pci_uninit(hdev); hclgevf_uninit_mac_list(hdev); +#ifdef CONFIG_HNS3_UBL + if (hnae3_dev_ubl_supported(hdev->ae_dev)) { + hclgevf_unic_uninit_mc_guid_list(hdev); + hclgevf_unic_clear_ip_list(hdev); + } +#endif } static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev) @@ -3379,6 +3492,12 @@ static const struct hnae3_ae_ops hclgevf_ops = { .get_cmdq_stat = hclgevf_get_cmdq_stat, .request_flush_qb_config = hclgevf_set_fd_qb, .query_fd_qb_state = hclgevf_query_fd_qb_state, +#ifdef CONFIG_HNS3_UBL + .add_addr = hclgevf_unic_add_addr, + .rm_addr = hclgevf_unic_rm_addr, + .get_func_guid = hclgevf_unic_get_func_guid, + .set_func_guid = hclgevf_unic_set_func_guid, +#endif }; static struct hnae3_ae_algo ae_algovf = { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index a450b4df5b1c45913a1fad9006565131ea4fbee5..699a2afaef0ec646abc85907803c24ff60a5d1f7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -128,6 +128,7 @@ enum hclgevf_states { HCLGEVF_STATE_REMOVING, HCLGEVF_STATE_NIC_REGISTERED, HCLGEVF_STATE_ROCE_REGISTERED, + HCLGEVF_STATE_UDMA_REGISTERED, HCLGEVF_STATE_SERVICE_INITED, /* task states */ HCLGEVF_STATE_RST_SERVICE_SCHED, @@ -206,6 +207,11 @@ struct hclgevf_mac_table_cfg { struct list_head mc_mac_list; }; +struct hclgevf_ip_table_cfg { + spinlock_t ip_list_lock; /* protect ip address need to add/detele */ + struct list_head ip_list; +}; + struct hclgevf_qb_cfg { bool pf_support_qb; bool hw_qb_en; @@ -252,6 +258,7 @@ struct hclgevf_dev { u16 num_msi_used; u16 num_nic_msix; /* Num of nic vectors for this VF */ u16 num_roce_msix; /* Num of roce vectors for this VF */ + u16 num_udma_msix; /* Num of udma vectors for this VF */ u16 roce_base_msix_offset; u16 *vector_status; int *vector_irq; @@ -262,6 +269,8 @@ struct hclgevf_dev { struct hclgevf_mac_table_cfg mac_table; + struct hclgevf_ip_table_cfg ip_table; + struct hclgevf_mbx_resp_status mbx_resp; /* mailbox response */ struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */ @@ -271,15 +280,20 @@ struct hclgevf_dev { struct hnae3_handle nic; struct hnae3_handle roce; + struct hnae3_handle udma; struct hnae3_client *nic_client; struct hnae3_client *roce_client; + struct hnae3_client *udma_client; u32 flag; unsigned long serv_processed_cnt; unsigned long last_serv_processed; struct hclgevf_qb_cfg qb_cfg; struct devlink *devlink; + + spinlock_t mguid_list_lock; /* protect mc guid need to add/detele */ + struct list_head mc_guid_list; /* Store VF mc guid table */ }; static inline bool hclgevf_is_reset_pending(struct hclgevf_dev *hdev) @@ -301,4 +315,6 @@ void hclgevf_mbx_task_schedule(struct hclgevf_dev *hdev); void hclgevf_update_port_base_vlan_info(struct hclgevf_dev *hdev, u16 state, struct hclge_mbx_port_base_vlan *port_base_vlan); struct hclgevf_dev *hclgevf_ae_get_hdev(struct hnae3_handle *handle); +void hclgevf_build_send_msg(struct hclge_vf_to_pf_msg *msg, u8 code, + u8 subcode); #endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_udma.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_udma.c new file mode 100644 index 0000000000000000000000000000000000000000..6e839a54040eb36e1853d4a4df1874803d520bb4 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_udma.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include "hclgevf_main.h" +#include "hclgevf_udma.h" + +int hclgevf_notify_udma_client(struct hclgevf_dev *hdev, + enum hnae3_reset_notify_type type) +{ + struct hnae3_client *client = hdev->udma_client; + struct hnae3_handle *handle = &hdev->udma; + int ret; + + if (!test_bit(HCLGEVF_STATE_UDMA_REGISTERED, &hdev->state) || !client) + return 0; + + if (!client->ops->reset_notify) + return -EOPNOTSUPP; + + ret = client->ops->reset_notify(handle, type); + if (ret) + dev_err(&hdev->pdev->dev, "notify udma client failed %d(%d)", + type, ret); + return ret; +} + +static int hclgevf_init_udma_base_info(struct hclgevf_dev *hdev) +{ + struct hnae3_handle *udma = &hdev->udma; + struct hnae3_handle *nic = &hdev->nic; + + udma->udmainfo.num_vectors = hdev->num_udma_msix; + + if (hdev->num_msi_left < udma->udmainfo.num_vectors || + hdev->num_msi_left == 0) + return -EINVAL; + + udma->udmainfo.base_vector = hdev->roce_base_msix_offset; + + udma->udmainfo.netdev = nic->kinfo.netdev; + udma->udmainfo.udma_io_base = hdev->hw.hw.io_base; + udma->udmainfo.udma_mem_base = hdev->hw.hw.mem_base; + + udma->pdev = nic->pdev; + udma->ae_algo = nic->ae_algo; + udma->numa_node_mask = nic->numa_node_mask; + + return 0; +} + +int hclgevf_init_udma_client_instance(struct hnae3_ae_dev *ae_dev, + struct hnae3_client *client) +{ + struct hclgevf_dev *hdev = ae_dev->priv; + int ret; + + if (!hnae3_dev_udma_supported(ae_dev) || !hdev->udma_client || + !hdev->nic_client) + return 0; + + ret = hclgevf_init_udma_base_info(hdev); + if (ret) + return ret; + + ret = client->ops->init_instance(&hdev->udma); + if (ret) + return ret; + + set_bit(HCLGEVF_STATE_UDMA_REGISTERED, &hdev->state); + hnae3_set_client_init_flag(client, ae_dev, 1); + + return 0; +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_udma.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_udma.h new file mode 100644 index 0000000000000000000000000000000000000000..e39d38d29d708004e1fec2eece7b20ea027cc0a8 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_udma.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Hisilicon UNIC Linux driver + * Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGEVF_UDMA_H +#define __HCLGEVF_UDMA_H + +#include + +int hclgevf_notify_udma_client(struct hclgevf_dev *hdev, + enum hnae3_reset_notify_type type); +int hclgevf_init_udma_client_instance(struct hnae3_ae_dev *ae_dev, + struct hnae3_client *client); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_addr.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_addr.c new file mode 100644 index 0000000000000000000000000000000000000000..5359563851a5a5f57cfe3a31d8c7584abd8fbef9 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_addr.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include "hclgevf_main.h" +#include "hclge_comm_unic_addr.h" +#include "hclgevf_unic_guid.h" +#include "hclgevf_unic_ip.h" +#include "hclgevf_unic_addr.h" + +int hclgevf_unic_add_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type) +{ + switch (addr_type) { + case HNAE3_UNIC_IP_ADDR: + return hclgevf_unic_update_ip_list(handle, + HCLGE_COMM_UNIC_ADDR_TO_ADD, + (const struct sockaddr *)addr); + case HNAE3_UNIC_MCGUID_ADDR: + return hclgevf_unic_update_guid_list(handle, + HCLGE_COMM_UNIC_ADDR_TO_ADD, + addr); + default: + return -EINVAL; + } +} + +int hclgevf_unic_rm_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type) +{ + switch (addr_type) { + case HNAE3_UNIC_IP_ADDR: + return hclgevf_unic_update_ip_list(handle, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + (const struct sockaddr *)addr); + case HNAE3_UNIC_MCGUID_ADDR: + return hclgevf_unic_update_guid_list(handle, + HCLGE_COMM_UNIC_ADDR_TO_DEL, + addr); + default: + return -EINVAL; + } +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_addr.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_addr.h new file mode 100644 index 0000000000000000000000000000000000000000..d3c757d6b5d7c95bc7c494e602ddadc4967cb478 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_addr.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGEVF_UNIC_ADDR_H +#define __HCLGEVF_UNIC_ADDR_H + +#include "hnae3.h" + +int hclgevf_unic_add_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type); +int hclgevf_unic_rm_addr(struct hnae3_handle *handle, const unsigned char *addr, + enum hnae3_unic_addr_type addr_type); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_guid.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_guid.c new file mode 100644 index 0000000000000000000000000000000000000000..53b2b206e74566e97b8611f6134c8d01cb1d554b --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_guid.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2023 Hisilicon Limited. + +#include + +#include "ubl.h" +#include "hclgevf_main.h" +#include "hclge_comm_unic_addr.h" +#include "hclge_comm_cmd.h" +#include "hclge_mbx.h" +#include "hclgevf_unic_guid.h" + +int hclgevf_unic_update_guid_list(struct hnae3_handle *handle, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const unsigned char *addr) +{ + struct hclgevf_dev *hdev = container_of(handle, struct hclgevf_dev, nic); + char format_guid_addr[HCLGE_COMM_FORMAT_GUID_ADDR_LEN]; + int ret; + + ret = hclge_comm_unic_update_addr_list(&hdev->mc_guid_list, + &hdev->mguid_list_lock, + state, addr); + + if (ret == -ENOENT) { + hclge_comm_format_guid_addr(format_guid_addr, addr); + dev_err(&hdev->pdev->dev, + "failed to delete guid %s from mc guid list\n", + format_guid_addr); + } + + return ret; +} + +static int +hclgevf_unic_add_del_mc_guid(struct hclgevf_dev *hdev, + struct hclge_comm_unic_addr_node *guid_node) +{ + struct hclge_vf_to_pf_msg send_msg = {0}; + + send_msg.code = HCLGE_MBX_SET_MGUID; + if (guid_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) + send_msg.subcode = HCLGE_MBX_MC_GUID_MC_ADD; + else + send_msg.subcode = HCLGE_MBX_MC_GUID_MC_DELETE; + + memcpy(send_msg.data, &guid_node->proto, sizeof(__le16)); + return hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0); +} + +static void hclgevf_unic_config_mguid_list(struct hnae3_handle *h, + struct list_head *list) +{ + struct hclgevf_dev *hdev = container_of(h, struct hclgevf_dev, nic); + char format_guid_addr[HCLGE_COMM_FORMAT_GUID_ADDR_LEN]; + struct hclge_comm_unic_addr_node *guid_node, *tmp; + int ret; + + list_for_each_entry_safe(guid_node, tmp, list, node) { + ret = hclgevf_unic_add_del_mc_guid(hdev, guid_node); + if (ret) { + hclge_comm_format_guid_addr(format_guid_addr, + guid_node->mguid); + dev_err(&hdev->pdev->dev, + "failed to configure mc guid %s, state = %d, ret = %d\n", + format_guid_addr, guid_node->state, ret); + return; + } + if (guid_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) { + guid_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + } else { + list_del(&guid_node->node); + kfree(guid_node); + } + } +} + +void hclgevf_unic_sync_mc_guid_list(struct hclgevf_dev *hdev) +{ + (void)hclge_comm_unic_sync_addr_table(&hdev->nic, + &hdev->mc_guid_list, + &hdev->mguid_list_lock, + hclgevf_unic_config_mguid_list, + hclgevf_unic_config_mguid_list); +} + +static void hclgevf_unic_clear_guid_list(struct list_head *list) +{ + struct hclge_comm_unic_addr_node *guid_node, *tmp; + + list_for_each_entry_safe(guid_node, tmp, list, node) { + list_del(&guid_node->node); + kfree(guid_node); + } +} + +void hclgevf_unic_uninit_mc_guid_list(struct hclgevf_dev *hdev) +{ + spin_lock_bh(&hdev->mguid_list_lock); + + hclgevf_unic_clear_guid_list(&hdev->mc_guid_list); + + spin_unlock_bh(&hdev->mguid_list_lock); +} + +int hclgevf_unic_set_func_guid(struct hnae3_handle *handle, u8 *guid) +{ + struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); + + return hclge_comm_unic_set_func_guid(&hdev->hw.hw, guid); +} + +int hclgevf_unic_get_func_guid(struct hnae3_handle *handle, u8 *guid) +{ + struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); + + return hclge_comm_unic_get_func_guid(&hdev->hw.hw, guid); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_guid.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_guid.h new file mode 100644 index 0000000000000000000000000000000000000000..faf9c90106d905a07116b5ad36688a50ecfb2a97 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_guid.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +// Copyright (c) 2023 Hisilicon Limited. + +#ifndef __HCLGEVF_UNIC_GUID_H +#define __HCLGEVF_UNIC_GUID_H + +#include + +#include "ubl.h" + +void hclgevf_unic_sync_mc_guid_list(struct hclgevf_dev *hdev); +void hclgevf_unic_uninit_mc_guid_list(struct hclgevf_dev *hdev); +int hclgevf_unic_update_guid_list(struct hnae3_handle *handle, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const unsigned char *addr); +int hclgevf_unic_set_func_guid(struct hnae3_handle *handle, u8 *guid); +int hclgevf_unic_get_func_guid(struct hnae3_handle *handle, u8 *guid); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_ip.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_ip.c new file mode 100644 index 0000000000000000000000000000000000000000..08ee7fc154a123aaa3e537ca87ad3235ced8ba19 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_ip.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include +#include +#include + +#include "hclgevf_main.h" +#include "hclge_comm_unic_addr.h" +#include "hclge_mbx.h" +#include "hclgevf_unic_ip.h" + +int hclgevf_unic_update_ip_list(struct hnae3_handle *handle, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const struct sockaddr *addr) +{ + struct hclgevf_dev *hdev = container_of(handle, struct hclgevf_dev, nic); + struct in6_addr ip_addr; + int ret; + + hclge_comm_unic_convert_ip_addr(addr, &ip_addr); + + ret = hclge_comm_unic_update_addr_list(&hdev->ip_table.ip_list, + &hdev->ip_table.ip_list_lock, + state, + (const unsigned char *)&ip_addr); + + if (ret == -ENOENT) + dev_err(&hdev->pdev->dev, + "failed to delete ip %pI6c from ip list\n", + ip_addr.s6_addr); + + return ret; +} + +static void +hclgevf_unic_prepare_ip_msg(u8 code, int index, + struct hclge_comm_unic_addr_node *ip_node, + struct hclge_vf_to_pf_msg *send_msg) +{ + memset(send_msg, 0, sizeof(struct hclge_vf_to_pf_msg)); + send_msg->code = code; + + if (ip_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) + send_msg->subcode = HCLGE_UNIC_MBX_IP_TABLE_ADD; + else + send_msg->subcode = HCLGE_UNIC_MBX_IP_TABLE_REMOVE; + + if (index == 0) { + send_msg->data[0] = HCLGE_COMM_UNIC_IPV6_UPPER_LEN; + memcpy(&send_msg->data[HCLGE_COMM_UNIC_MSG_IPADDR_POS], + &ip_node->ip_addr.s6_addr, + sizeof(u8) * HCLGE_COMM_UNIC_IPV6_UPPER_LEN); + } else { + send_msg->data[0] = HCLGE_COMM_UNIC_IPV6_LOWER_LEN; + memcpy(&send_msg->data[HCLGE_COMM_UNIC_MSG_IPADDR_POS], + &ip_node->ip_addr.s6_addr[HCLGE_COMM_UNIC_IPV6_UPPER_LEN], + sizeof(u8) * HCLGE_COMM_UNIC_IPV6_LOWER_LEN); + } +} + +static int +hclgevf_unic_add_del_ip_addr(struct hclgevf_dev *hdev, + struct hclge_comm_unic_addr_node *ip_node) +{ + struct hclge_vf_to_pf_msg send_msg; + int ret; + + hclgevf_unic_prepare_ip_msg(HCLGE_UNIC_MBX_SET_IP, 0, ip_node, + &send_msg); + ret = hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0); + if (ret) { + dev_err(&hdev->pdev->dev, + "VF send ip address to PF failed, ret=%d", ret); + return ret; + } + + hclgevf_unic_prepare_ip_msg(HCLGE_UNIC_MBX_SET_IP, 1, ip_node, + &send_msg); + ret = hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0); + if (ret) { + dev_err(&hdev->pdev->dev, + "VF send ip address to PF failed, ret=%d", ret); + return ret; + } + return 0; +} + +static void hclgevf_unic_config_ip_list(struct hnae3_handle *h, + struct list_head *list) +{ + struct hclgevf_dev *hdev = container_of(h, struct hclgevf_dev, nic); + struct hclge_comm_unic_addr_node *ip_node, *tmp; + int ret; + + list_for_each_entry_safe(ip_node, tmp, list, node) { + ret = hclgevf_unic_add_del_ip_addr(hdev, ip_node); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to configure ip %pI6c, state = %d, ret = %d\n", + ip_node->ip_addr.s6_addr, ip_node->state, ret); + return; + } + if (ip_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) { + ip_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE; + } else { + list_del(&ip_node->node); + kfree(ip_node); + } + } +} + +void hclgevf_unic_clear_ip_list(struct hclgevf_dev *hdev) +{ + struct hclge_comm_unic_addr_node *ip_node, *tmp; + struct list_head *list; + + list = &hdev->ip_table.ip_list; + + spin_lock_bh(&hdev->ip_table.ip_list_lock); + list_for_each_entry_safe(ip_node, tmp, list, node) { + list_del(&ip_node->node); + kfree(ip_node); + } + spin_unlock_bh(&hdev->ip_table.ip_list_lock); +} + +void hclgevf_unic_sync_ip_list(struct hclgevf_dev *hdev) +{ + (void)hclge_comm_unic_sync_addr_table(&hdev->nic, + &hdev->ip_table.ip_list, + &hdev->ip_table.ip_list_lock, + hclgevf_unic_config_ip_list, + hclgevf_unic_config_ip_list); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_ip.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_ip.h new file mode 100644 index 0000000000000000000000000000000000000000..1bbbe24ed5decae4c4c21c27381f408cd1708052 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_unic_ip.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __HCLGEVF_UNIC_IP_H +#define __HCLGEVF_UNIC_IP_H + +#include +#include +#include + +void hclgevf_unic_sync_ip_list(struct hclgevf_dev *hdev); +void hclgevf_unic_clear_ip_list(struct hclgevf_dev *hdev); +int hclgevf_unic_update_ip_list(struct hnae3_handle *handle, + enum HCLGE_COMM_ADDR_NODE_STATE state, + const struct sockaddr *addr); + +#endif diff --git a/drivers/net/ub/Kconfig b/drivers/net/ub/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..d49376ec21ee063fc5c1580517ff9e8c298f4a97 --- /dev/null +++ b/drivers/net/ub/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# UBL configuration +# + +config UBL + default n + tristate "UB link support" + help + This enables UB link support. Say 'Y' or 'm' if your + device works in UB. diff --git a/drivers/net/ub/Makefile b/drivers/net/ub/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e9013cb9790fba0d6494ccfde2ecd356a88b6f30 --- /dev/null +++ b/drivers/net/ub/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for the HISILICON network device drivers. +# + +#### compile ubl +obj-$(CONFIG_UBL) += dev/ diff --git a/drivers/net/ub/dev/Makefile b/drivers/net/ub/dev/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e93bfcca64ba40b8323c54f5346f4201e194b80b --- /dev/null +++ b/drivers/net/ub/dev/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Makefile for the HISILICON network device drivers. +# + +#### compile ubl +obj-$(CONFIG_UBL) += ubl.o diff --git a/drivers/net/ub/dev/ubl.c b/drivers/net/ub/dev/ubl.c new file mode 100644 index 0000000000000000000000000000000000000000..f8d4bb86ff255bdc52327caad00a0cccec9582ce --- /dev/null +++ b/drivers/net/ub/dev/ubl.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * UBL An implementation of the UB protocol suite for the LINUX + * operating system. + * + * UB link device handling. + * + * Version: @(#)ubl.c 1.0.1 23/02/14 + * + */ + +#include +#include + +#include "ubl.h" + +static __be16 ubl_type_to_proto(u8 type) +{ + __be16 proto; + + switch (type) { + case UB_IPV4_CFG_TYPE: + proto = htons(ETH_P_IP); + break; + case UB_IPV6_CFG_TYPE: + proto = htons(ETH_P_IPV6); + break; + case UB_NOIP_CFG_TYPE: + default: + proto = htons(ETH_P_UB); + break; + } + + return proto; +} + +/** + * ubl_create_header - create the ubl header + * @skb: buffer to alter + * @dev: source device + * @type: ubl type field + * @daddr: not used in ubl + * @saddr: not used in ubl + * @len: packet length (<= skb->len) + * + */ +int ubl_create_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) +{ + u8 ctype = UB_NOIP_CFG_TYPE; + int ret = -UBL_HLEN; + struct ublhdr *ubl; + + if (type == ETH_P_IP || type == ETH_P_IPV6) { + ubl = (struct ublhdr *)skb_push(skb, UBL_HLEN); + memset(ubl, 0, sizeof(struct ublhdr)); + ubl->h_npi = htonl(UB_DEFAULT_NPI); + ctype = (type == ETH_P_IP) ? UB_IPV4_CFG_TYPE : UB_IPV6_CFG_TYPE; + ret = UBL_HLEN; + } else if (type == ETH_P_UB) { + /* if type is ETH_P_UB, then do nothing. */ + ret = 0; + } + ubl_add_sw_ctype(skb, ctype); + + return ret; +} +EXPORT_SYMBOL(ubl_create_header); + +/** + * ubl_header_parse_protocol - parse packets protocol before send it to driver. + * @skb: buffer to alter + * + * parse packets based on packet data if skb->protocol is ETH_P_ALL or 0. + */ +static __be16 ubl_header_parse_protocol(const struct sk_buff *skb) +{ + return ubl_type_to_proto(skb->data[0]); +} + +const static struct header_ops ubl_header_ops ____cacheline_aligned = { + .create = ubl_create_header, + .parse_protocol = ubl_header_parse_protocol, +}; + +/** + * ubl_setup - setup ub link network device + * @dev: network device + * + * Fill in the fields of the device structure with ubl-generic values. + */ +void ubl_setup(struct net_device *dev) +{ + dev->header_ops = &ubl_header_ops; + dev->type = ARPHRD_UB; + dev->hard_header_len = UBL_HLEN; + dev->min_header_len = UBL_HLEN; + dev->mtu = UB_DATA_LEN; + dev->min_mtu = UB_MIN_MTU; + dev->max_mtu = UB_DATA_LEN; + dev->addr_len = UBL_ALEN; + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; + dev->flags = (IFF_NOARP | IFF_POINTOPOINT); + dev->priv_flags |= IFF_TX_SKB_SHARING; +} +EXPORT_SYMBOL(ubl_setup); + +/** + * alloc_ubndev_mqs - Allocates and sets up an ub-n device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this ubn device + * @txqs: The number of TX queues this device has. + * @rxqs: The number of RX queues this device has. + * + * Fill in the fields of the device structure with ubn-generic + * values. Basically does everything except registering the device. + * + * Constructs a new net device, complete with a private data area of + * size (sizeof_priv). A 32-byte (not bit) alignment is enforced for + * this private data area. + */ + +struct net_device *alloc_ubndev_mqs(int sizeof_priv, unsigned int txqs, + unsigned int rxqs) +{ + return alloc_netdev_mqs(sizeof_priv, "ubn%d", NET_NAME_UNKNOWN, + ubl_setup, txqs, rxqs); +} +EXPORT_SYMBOL(alloc_ubndev_mqs); + +/** + * ubl_type_trans - obtains skb->protocol and adds sw_ptype to the packet + * @skb: buffer to alter + * @dev: source device + * @type: packet type + * + * Obtains the packet type and translates it to skb->protocol and adds sw_ptype + * to the packet data. + */ +__be16 ubl_type_trans(struct sk_buff *skb, struct net_device *dev, u8 type) +{ + skb->dev = dev; + ubl_add_sw_ctype(skb, type); + skb_reset_mac_header(skb); + if (type == UB_IPV4_CFG_TYPE || type == UB_IPV6_CFG_TYPE) + skb_pull_inline(skb, UBL_HLEN + 1); + else if (type != UB_NOIP_CFG_TYPE) + net_warn_ratelimited("An unknown packet is received by %s, type is %u\n", + dev->name, type); + + return ubl_type_to_proto(type); +} +EXPORT_SYMBOL(ubl_type_trans); + +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UB link level"); +MODULE_VERSION(UBL_MOD_VERSION); diff --git a/drivers/net/ub/dev/ubl.h b/drivers/net/ub/dev/ubl.h new file mode 100644 index 0000000000000000000000000000000000000000..02424918b01fc163479735c8582e6070606d5078 --- /dev/null +++ b/drivers/net/ub/dev/ubl.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Copyright (c) 2023-2023 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef __LINUX_UBL_H +#define __LINUX_UBL_H + +#include +#include +#include +#include + +#define UBL_MOD_VERSION "1.0.0" + +#define UBL_HARD_HLEN 4 +#define UBL_HLEN 6 +#define UBL_ALEN 16 + +#define UBL_LCRC_LEN 2 +#define UBL_BCRC_LEN 4 +#define UBL_LINK_LEN 2 + +#define UB_DEFAULT_NPI 1 + +#define UB_MAX_BLOCK_NUM 16 +#define UB_FLIT_NUM_OF_BLOCK 32 +#define UB_BYTE_OF_FLIT 20 +#define UB_BYTE_OF_BLK (UB_BYTE_OF_FLIT * UB_FLIT_NUM_OF_BLOCK) + +#define UB_BLOCK_CRC_LEN (UBL_BCRC_LEN + UBL_LCRC_LEN) +#define UB_MAX_MTU_PER_BLK (UB_BYTE_OF_BLK - UBL_LINK_LEN - UB_BLOCK_CRC_LEN) +#define UB_DATA_LEN 1500 +#define UB_MAX_MTU ((UB_MAX_BLOCK_NUM * UB_MAX_MTU_PER_BLK) - \ + UBL_HLEN - UBL_HARD_HLEN + UBL_LINK_LEN) +#define UB_MIN_MTU 68 + +#define UB_IPV4_CFG_TYPE 3 +#define UB_IPV6_CFG_TYPE 4 +#define UB_NOIP_CFG_TYPE 5 +#define UB_UNKNOWN_CFG_TYPE 255 + +/** + * struct ublhdr - ub link header + * @h_cc: cc + * @h_npi: npi + */ +struct ublhdr { + __be16 h_cc; + __be32 h_npi; +} __packed; + +/** + * ubl_add_sw_ctype - add software packet type for skb->data + * @skb: buffer to alter + * @ctype: indicates the packet type + * + * The packet type cannot be known by parsing packe from user, + * which leads to restrictions on the use of socket. + * Add cs_type field to indicate the packet type. And sw_ctype + * exists only during software prcessing. + * +----------+----+-----+-----------+ + * | sw_ctype | CC | NPI | L3 Packet | + * +----------+----+-----+-----------+ + */ +static inline void ubl_add_sw_ctype(struct sk_buff *skb, u8 ctype) +{ + u8 *pkt_cfg = (u8 *)skb_push(skb, sizeof(u8)); + + *pkt_cfg = ctype; +} + +/** + * ubl_rmv_sw_ctype - delete software packet type for skb->data + * @skb: buffer to alter + * + * Before the packet is sent to the hardware, remove sw_ctype field + * and restore the original packet. + */ +static inline void ubl_rmv_sw_ctype(struct sk_buff *skb) +{ + skb_pull_inline(skb, sizeof(u8)); +} + +int ubl_create_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len); +void ubl_setup(struct net_device *dev); +__be16 ubl_type_trans(struct sk_buff *skb, struct net_device *dev, u8 type); +struct net_device *alloc_ubndev_mqs(int sizeof_priv, unsigned int txqs, + unsigned int rxqs); +#define alloc_ubndev_mq(sizeof_priv, count) \ + alloc_ubndev_mqs((sizeof_priv), (count), (count)) + +#endif diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h index c3cc5a9e5eaf064a945f7d96a5b4226cb243619c..0ca98140376b6a912f4e14938f8b8f83c86ae517 100644 --- a/include/uapi/linux/if_arp.h +++ b/include/uapi/linux/if_arp.h @@ -43,6 +43,8 @@ #define ARPHRD_EUI64 27 /* EUI-64 */ #define ARPHRD_INFINIBAND 32 /* InfiniBand */ +#define ARPHRD_UB 38 /* Unified bus */ + /* Dummy types for non ARP hardware */ #define ARPHRD_SLIP 256 #define ARPHRD_CSLIP 257 diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index d6de2b167448fe592b73da2dda68e99f355078e5..7309563a2e47c114bf26b6775e2fc0465e128a7a 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -150,6 +150,7 @@ #define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and * aggregation protocol */ +#define ETH_P_UB 0x0100 /* Network control packet of Unified Bus */ /* * This is an Ethernet frame header.