linux-stable/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c

12811 lines
345 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0+
// Copyright (c) 2016-2017 Hisilicon Limited.
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/if_vlan.h>
#include <linux/crash_dump.h>
#include <net/ipv6.h>
#include <net/rtnetlink.h>
#include "hclge_cmd.h"
#include "hclge_dcb.h"
#include "hclge_main.h"
#include "hclge_mbx.h"
#include "hclge_mdio.h"
#include "hclge_regs.h"
#include "hclge_tm.h"
#include "hclge_err.h"
#include "hnae3.h"
#include "hclge_devlink.h"
#include "hclge_comm_cmd.h"
#define HCLGE_NAME "hclge"
#define HCLGE_BUF_SIZE_UNIT 256U
#define HCLGE_BUF_MUL_BY 2
#define HCLGE_BUF_DIV_BY 2
#define NEED_RESERVE_TC_NUM 2
#define BUF_MAX_PERCENT 100
#define BUF_RESERVE_PERCENT 90
#define HCLGE_RESET_MAX_FAIL_CNT 5
#define HCLGE_RESET_SYNC_TIME 100
#define HCLGE_PF_RESET_SYNC_TIME 20
#define HCLGE_PF_RESET_SYNC_CNT 1500
#define HCLGE_LINK_STATUS_MS 10
static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps);
static int hclge_init_vlan_config(struct hclge_dev *hdev);
static void hclge_sync_vlan_filter(struct hclge_dev *hdev);
static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle);
static void hclge_rfs_filter_expire(struct hclge_dev *hdev);
static int hclge_clear_arfs_rules(struct hclge_dev *hdev);
static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev,
unsigned long *addr);
static int hclge_set_default_loopback(struct hclge_dev *hdev);
static void hclge_sync_mac_table(struct hclge_dev *hdev);
static void hclge_restore_hw_table(struct hclge_dev *hdev);
static void hclge_sync_promisc_mode(struct hclge_dev *hdev);
static void hclge_sync_fd_table(struct hclge_dev *hdev);
static void hclge_update_fec_stats(struct hclge_dev *hdev);
static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret,
int wait_cnt);
static int hclge_update_port_info(struct hclge_dev *hdev);
static struct hnae3_ae_algo ae_algo;
static struct workqueue_struct *hclge_wq;
static const struct pci_device_id ae_algo_pci_tbl[] = {
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0},
{PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_200G_RDMA), 0},
/* required last entry */
{0, }
};
MODULE_DEVICE_TABLE(pci, ae_algo_pci_tbl);
static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = {
"External Loopback test",
"App Loopback test",
"Serdes serial Loopback test",
"Serdes parallel Loopback test",
"Phy Loopback test"
};
static const struct hclge_comm_stats_str g_mac_stats_string[] = {
{"mac_tx_mac_pause_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_mac_pause_num)},
{"mac_rx_mac_pause_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_mac_pause_num)},
{"mac_tx_pause_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pause_xoff_time)},
{"mac_rx_pause_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pause_xoff_time)},
{"mac_tx_control_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_ctrl_pkt_num)},
{"mac_rx_control_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_ctrl_pkt_num)},
{"mac_tx_pfc_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pause_pkt_num)},
{"mac_tx_pfc_pri0_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri0_pkt_num)},
{"mac_tx_pfc_pri1_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri1_pkt_num)},
{"mac_tx_pfc_pri2_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri2_pkt_num)},
{"mac_tx_pfc_pri3_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri3_pkt_num)},
{"mac_tx_pfc_pri4_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri4_pkt_num)},
{"mac_tx_pfc_pri5_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri5_pkt_num)},
{"mac_tx_pfc_pri6_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri6_pkt_num)},
{"mac_tx_pfc_pri7_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri7_pkt_num)},
{"mac_tx_pfc_pri0_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri0_xoff_time)},
{"mac_tx_pfc_pri1_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri1_xoff_time)},
{"mac_tx_pfc_pri2_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri2_xoff_time)},
{"mac_tx_pfc_pri3_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri3_xoff_time)},
{"mac_tx_pfc_pri4_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri4_xoff_time)},
{"mac_tx_pfc_pri5_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri5_xoff_time)},
{"mac_tx_pfc_pri6_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri6_xoff_time)},
{"mac_tx_pfc_pri7_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri7_xoff_time)},
{"mac_rx_pfc_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pause_pkt_num)},
{"mac_rx_pfc_pri0_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri0_pkt_num)},
{"mac_rx_pfc_pri1_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri1_pkt_num)},
{"mac_rx_pfc_pri2_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri2_pkt_num)},
{"mac_rx_pfc_pri3_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri3_pkt_num)},
{"mac_rx_pfc_pri4_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri4_pkt_num)},
{"mac_rx_pfc_pri5_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri5_pkt_num)},
{"mac_rx_pfc_pri6_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri6_pkt_num)},
{"mac_rx_pfc_pri7_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri7_pkt_num)},
{"mac_rx_pfc_pri0_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri0_xoff_time)},
{"mac_rx_pfc_pri1_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri1_xoff_time)},
{"mac_rx_pfc_pri2_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri2_xoff_time)},
{"mac_rx_pfc_pri3_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri3_xoff_time)},
{"mac_rx_pfc_pri4_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri4_xoff_time)},
{"mac_rx_pfc_pri5_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri5_xoff_time)},
{"mac_rx_pfc_pri6_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri6_xoff_time)},
{"mac_rx_pfc_pri7_xoff_time", HCLGE_MAC_STATS_MAX_NUM_V2,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri7_xoff_time)},
{"mac_tx_total_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_total_pkt_num)},
{"mac_tx_total_oct_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_total_oct_num)},
{"mac_tx_good_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_good_pkt_num)},
{"mac_tx_bad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_bad_pkt_num)},
{"mac_tx_good_oct_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_good_oct_num)},
{"mac_tx_bad_oct_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_bad_oct_num)},
{"mac_tx_uni_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_uni_pkt_num)},
{"mac_tx_multi_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_multi_pkt_num)},
{"mac_tx_broad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_broad_pkt_num)},
{"mac_tx_undersize_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_undersize_pkt_num)},
{"mac_tx_oversize_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_oversize_pkt_num)},
{"mac_tx_64_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_64_oct_pkt_num)},
{"mac_tx_65_127_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_65_127_oct_pkt_num)},
{"mac_tx_128_255_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_128_255_oct_pkt_num)},
{"mac_tx_256_511_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_256_511_oct_pkt_num)},
{"mac_tx_512_1023_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_512_1023_oct_pkt_num)},
{"mac_tx_1024_1518_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1024_1518_oct_pkt_num)},
{"mac_tx_1519_2047_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1519_2047_oct_pkt_num)},
{"mac_tx_2048_4095_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_2048_4095_oct_pkt_num)},
{"mac_tx_4096_8191_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_4096_8191_oct_pkt_num)},
{"mac_tx_8192_9216_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_8192_9216_oct_pkt_num)},
{"mac_tx_9217_12287_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_9217_12287_oct_pkt_num)},
{"mac_tx_12288_16383_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_12288_16383_oct_pkt_num)},
{"mac_tx_1519_max_good_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1519_max_good_oct_pkt_num)},
{"mac_tx_1519_max_bad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1519_max_bad_oct_pkt_num)},
{"mac_rx_total_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_total_pkt_num)},
{"mac_rx_total_oct_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_total_oct_num)},
{"mac_rx_good_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_good_pkt_num)},
{"mac_rx_bad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_bad_pkt_num)},
{"mac_rx_good_oct_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_good_oct_num)},
{"mac_rx_bad_oct_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_bad_oct_num)},
{"mac_rx_uni_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_uni_pkt_num)},
{"mac_rx_multi_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_multi_pkt_num)},
{"mac_rx_broad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_broad_pkt_num)},
{"mac_rx_undersize_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_undersize_pkt_num)},
{"mac_rx_oversize_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_oversize_pkt_num)},
{"mac_rx_64_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_64_oct_pkt_num)},
{"mac_rx_65_127_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_65_127_oct_pkt_num)},
{"mac_rx_128_255_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_128_255_oct_pkt_num)},
{"mac_rx_256_511_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_256_511_oct_pkt_num)},
{"mac_rx_512_1023_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_512_1023_oct_pkt_num)},
{"mac_rx_1024_1518_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1024_1518_oct_pkt_num)},
{"mac_rx_1519_2047_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1519_2047_oct_pkt_num)},
{"mac_rx_2048_4095_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_2048_4095_oct_pkt_num)},
{"mac_rx_4096_8191_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_4096_8191_oct_pkt_num)},
{"mac_rx_8192_9216_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_8192_9216_oct_pkt_num)},
{"mac_rx_9217_12287_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_9217_12287_oct_pkt_num)},
{"mac_rx_12288_16383_oct_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_12288_16383_oct_pkt_num)},
{"mac_rx_1519_max_good_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1519_max_good_oct_pkt_num)},
{"mac_rx_1519_max_bad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1519_max_bad_oct_pkt_num)},
{"mac_tx_fragment_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_fragment_pkt_num)},
{"mac_tx_undermin_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_undermin_pkt_num)},
{"mac_tx_jabber_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_jabber_pkt_num)},
{"mac_tx_err_all_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_err_all_pkt_num)},
{"mac_tx_from_app_good_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_from_app_good_pkt_num)},
{"mac_tx_from_app_bad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_tx_from_app_bad_pkt_num)},
{"mac_rx_fragment_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_fragment_pkt_num)},
{"mac_rx_undermin_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_undermin_pkt_num)},
{"mac_rx_jabber_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_jabber_pkt_num)},
{"mac_rx_fcs_err_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_fcs_err_pkt_num)},
{"mac_rx_send_app_good_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_send_app_good_pkt_num)},
{"mac_rx_send_app_bad_pkt_num", HCLGE_MAC_STATS_MAX_NUM_V1,
HCLGE_MAC_STATS_FIELD_OFF(mac_rx_send_app_bad_pkt_num)}
};
static const struct hclge_mac_mgr_tbl_entry_cmd hclge_mgr_table[] = {
{
.flags = HCLGE_MAC_MGR_MASK_VLAN_B,
.ethter_type = cpu_to_le16(ETH_P_LLDP),
.mac_addr = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e},
.i_port_bitmap = 0x1,
},
};
static const struct key_info meta_data_key_info[] = {
{ PACKET_TYPE_ID, 6 },
{ IP_FRAGEMENT, 1 },
{ ROCE_TYPE, 1 },
{ NEXT_KEY, 5 },
{ VLAN_NUMBER, 2 },
{ SRC_VPORT, 12 },
{ DST_VPORT, 12 },
{ TUNNEL_PACKET, 1 },
};
static const struct key_info tuple_key_info[] = {
{ OUTER_DST_MAC, 48, KEY_OPT_MAC, -1, -1 },
{ OUTER_SRC_MAC, 48, KEY_OPT_MAC, -1, -1 },
{ OUTER_VLAN_TAG_FST, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_ETH_TYPE, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_L2_RSV, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_IP_TOS, 8, KEY_OPT_U8, -1, -1 },
{ OUTER_IP_PROTO, 8, KEY_OPT_U8, -1, -1 },
{ OUTER_SRC_IP, 32, KEY_OPT_IP, -1, -1 },
{ OUTER_DST_IP, 32, KEY_OPT_IP, -1, -1 },
{ OUTER_L3_RSV, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_SRC_PORT, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_DST_PORT, 16, KEY_OPT_LE16, -1, -1 },
{ OUTER_L4_RSV, 32, KEY_OPT_LE32, -1, -1 },
{ OUTER_TUN_VNI, 24, KEY_OPT_VNI, -1, -1 },
{ OUTER_TUN_FLOW_ID, 8, KEY_OPT_U8, -1, -1 },
{ INNER_DST_MAC, 48, KEY_OPT_MAC,
offsetof(struct hclge_fd_rule, tuples.dst_mac),
offsetof(struct hclge_fd_rule, tuples_mask.dst_mac) },
{ INNER_SRC_MAC, 48, KEY_OPT_MAC,
offsetof(struct hclge_fd_rule, tuples.src_mac),
offsetof(struct hclge_fd_rule, tuples_mask.src_mac) },
{ INNER_VLAN_TAG_FST, 16, KEY_OPT_LE16,
offsetof(struct hclge_fd_rule, tuples.vlan_tag1),
offsetof(struct hclge_fd_rule, tuples_mask.vlan_tag1) },
{ INNER_VLAN_TAG_SEC, 16, KEY_OPT_LE16, -1, -1 },
{ INNER_ETH_TYPE, 16, KEY_OPT_LE16,
offsetof(struct hclge_fd_rule, tuples.ether_proto),
offsetof(struct hclge_fd_rule, tuples_mask.ether_proto) },
{ INNER_L2_RSV, 16, KEY_OPT_LE16,
offsetof(struct hclge_fd_rule, tuples.l2_user_def),
offsetof(struct hclge_fd_rule, tuples_mask.l2_user_def) },
{ INNER_IP_TOS, 8, KEY_OPT_U8,
offsetof(struct hclge_fd_rule, tuples.ip_tos),
offsetof(struct hclge_fd_rule, tuples_mask.ip_tos) },
{ INNER_IP_PROTO, 8, KEY_OPT_U8,
offsetof(struct hclge_fd_rule, tuples.ip_proto),
offsetof(struct hclge_fd_rule, tuples_mask.ip_proto) },
{ INNER_SRC_IP, 32, KEY_OPT_IP,
offsetof(struct hclge_fd_rule, tuples.src_ip),
offsetof(struct hclge_fd_rule, tuples_mask.src_ip) },
{ INNER_DST_IP, 32, KEY_OPT_IP,
offsetof(struct hclge_fd_rule, tuples.dst_ip),
offsetof(struct hclge_fd_rule, tuples_mask.dst_ip) },
{ INNER_L3_RSV, 16, KEY_OPT_LE16,
offsetof(struct hclge_fd_rule, tuples.l3_user_def),
offsetof(struct hclge_fd_rule, tuples_mask.l3_user_def) },
{ INNER_SRC_PORT, 16, KEY_OPT_LE16,
offsetof(struct hclge_fd_rule, tuples.src_port),
offsetof(struct hclge_fd_rule, tuples_mask.src_port) },
{ INNER_DST_PORT, 16, KEY_OPT_LE16,
offsetof(struct hclge_fd_rule, tuples.dst_port),
offsetof(struct hclge_fd_rule, tuples_mask.dst_port) },
{ INNER_L4_RSV, 32, KEY_OPT_LE32,
offsetof(struct hclge_fd_rule, tuples.l4_user_def),
offsetof(struct hclge_fd_rule, tuples_mask.l4_user_def) },
};
/**
* hclge_cmd_send - send command to command queue
* @hw: pointer to the hw struct
* @desc: prefilled descriptor for describing the command
* @num : the number of descriptors to be sent
*
* This is the main send command for command queue, it
* sends the queue, cleans the queue, etc
**/
int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num)
{
return hclge_comm_cmd_send(&hw->hw, desc, num);
}
static int hclge_mac_update_stats_defective(struct hclge_dev *hdev)
{
#define HCLGE_MAC_CMD_NUM 21
u64 *data = (u64 *)(&hdev->mac_stats);
struct hclge_desc desc[HCLGE_MAC_CMD_NUM];
__le64 *desc_data;
u32 data_size;
int ret;
u32 i;
hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_MAC, true);
ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_MAC_CMD_NUM);
if (ret) {
dev_err(&hdev->pdev->dev,
"Get MAC pkt stats fail, status = %d.\n", ret);
return ret;
}
/* The first desc has a 64-bit header, so data size need to minus 1 */
data_size = sizeof(desc) / (sizeof(u64)) - 1;
desc_data = (__le64 *)(&desc[0].data[0]);
for (i = 0; i < data_size; i++) {
/* data memory is continuous becase only the first desc has a
* header in this command
*/
*data += le64_to_cpu(*desc_data);
data++;
desc_data++;
}
return 0;
}
static int hclge_mac_update_stats_complete(struct hclge_dev *hdev)
{
#define HCLGE_REG_NUM_PER_DESC 4
u32 reg_num = hdev->ae_dev->dev_specs.mac_stats_num;
u64 *data = (u64 *)(&hdev->mac_stats);
struct hclge_desc *desc;
__le64 *desc_data;
u32 data_size;
u32 desc_num;
int ret;
u32 i;
/* The first desc has a 64-bit header, so need to consider it */
desc_num = reg_num / HCLGE_REG_NUM_PER_DESC + 1;
net: hns3: fix GFP flag error in hclge_mac_update_stats() When CONFIG_DEBUG_ATOMIC_SLEEP on, calling kzalloc with GFP_KERNEL in hclge_mac_update_stats() will get below warning: [ 52.514677] BUG: sleeping function called from invalid context at mm/slab.h:501 [ 52.522051] in_atomic(): 0, irqs_disabled(): 0, pid: 1015, name: ifconfig [ 52.528827] 2 locks held by ifconfig/1015: [ 52.532921] #0: (____ptrval____) (&p->lock){....}, at: seq_read+0x54/0x748 [ 52.539878] #1: (____ptrval____) (rcu_read_lock){....}, at: dev_seq_start+0x0/0x140 [ 52.547610] CPU: 16 PID: 1015 Comm: ifconfig Not tainted 5.3.0-rc3-00697-g20b80be #98 [ 52.555408] Hardware name: Huawei TaiShan 2280 V2/BC82AMDC, BIOS 2280-V2 CS V3.B050.01 08/08/2019 [ 52.564242] Call trace: [ 52.566687] dump_backtrace+0x0/0x1f8 [ 52.570338] show_stack+0x14/0x20 [ 52.573646] dump_stack+0xb4/0xec [ 52.576950] ___might_sleep+0x178/0x198 [ 52.580773] __might_sleep+0x74/0xe0 [ 52.584338] __kmalloc+0x244/0x2d8 [ 52.587744] hclge_mac_update_stats+0xc8/0x1f8 [hclge] [ 52.592870] hclge_update_stats+0xe0/0x170 [hclge] [ 52.597651] hns3_nic_get_stats64+0xa0/0x458 [hns3] [ 52.602514] dev_get_stats+0x58/0x138 [ 52.606165] dev_seq_printf_stats+0x8c/0x280 [ 52.610420] dev_seq_show+0x14/0x40 [ 52.613898] seq_read+0x574/0x748 [ 52.617205] proc_reg_read+0xb4/0x108 [ 52.620857] __vfs_read+0x54/0xa8 [ 52.624162] vfs_read+0xa0/0x190 [ 52.627380] ksys_read+0xc8/0x178 [ 52.630685] __arm64_sys_read+0x40/0x50 [ 52.634509] el0_svc_common.constprop.0+0x120/0x1e0 [ 52.639369] el0_svc_handler+0x50/0x90 [ 52.643106] el0_svc+0x8/0xc So this patch uses GFP_ATOMIC instead of GFP_KERNEL to fix it. Fixes: d174ea75c96a ("net: hns3: add statistics for PFC frames and MAC control frames") Signed-off-by: Zhongzhu Liu <liuzhongzhu@huawei.com> Reviewed-by: Yunsheng Lin <linyunsheng@huawei.com> Reviewed-by: Peng Li <lipeng321@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2019-08-09 02:31:07 +00:00
/* This may be called inside atomic sections,
* so GFP_ATOMIC is more suitalbe here
*/
desc = kcalloc(desc_num, sizeof(struct hclge_desc), GFP_ATOMIC);
if (!desc)
return -ENOMEM;
net: hns3: fix GFP flag error in hclge_mac_update_stats() When CONFIG_DEBUG_ATOMIC_SLEEP on, calling kzalloc with GFP_KERNEL in hclge_mac_update_stats() will get below warning: [ 52.514677] BUG: sleeping function called from invalid context at mm/slab.h:501 [ 52.522051] in_atomic(): 0, irqs_disabled(): 0, pid: 1015, name: ifconfig [ 52.528827] 2 locks held by ifconfig/1015: [ 52.532921] #0: (____ptrval____) (&p->lock){....}, at: seq_read+0x54/0x748 [ 52.539878] #1: (____ptrval____) (rcu_read_lock){....}, at: dev_seq_start+0x0/0x140 [ 52.547610] CPU: 16 PID: 1015 Comm: ifconfig Not tainted 5.3.0-rc3-00697-g20b80be #98 [ 52.555408] Hardware name: Huawei TaiShan 2280 V2/BC82AMDC, BIOS 2280-V2 CS V3.B050.01 08/08/2019 [ 52.564242] Call trace: [ 52.566687] dump_backtrace+0x0/0x1f8 [ 52.570338] show_stack+0x14/0x20 [ 52.573646] dump_stack+0xb4/0xec [ 52.576950] ___might_sleep+0x178/0x198 [ 52.580773] __might_sleep+0x74/0xe0 [ 52.584338] __kmalloc+0x244/0x2d8 [ 52.587744] hclge_mac_update_stats+0xc8/0x1f8 [hclge] [ 52.592870] hclge_update_stats+0xe0/0x170 [hclge] [ 52.597651] hns3_nic_get_stats64+0xa0/0x458 [hns3] [ 52.602514] dev_get_stats+0x58/0x138 [ 52.606165] dev_seq_printf_stats+0x8c/0x280 [ 52.610420] dev_seq_show+0x14/0x40 [ 52.613898] seq_read+0x574/0x748 [ 52.617205] proc_reg_read+0xb4/0x108 [ 52.620857] __vfs_read+0x54/0xa8 [ 52.624162] vfs_read+0xa0/0x190 [ 52.627380] ksys_read+0xc8/0x178 [ 52.630685] __arm64_sys_read+0x40/0x50 [ 52.634509] el0_svc_common.constprop.0+0x120/0x1e0 [ 52.639369] el0_svc_handler+0x50/0x90 [ 52.643106] el0_svc+0x8/0xc So this patch uses GFP_ATOMIC instead of GFP_KERNEL to fix it. Fixes: d174ea75c96a ("net: hns3: add statistics for PFC frames and MAC control frames") Signed-off-by: Zhongzhu Liu <liuzhongzhu@huawei.com> Reviewed-by: Yunsheng Lin <linyunsheng@huawei.com> Reviewed-by: Peng Li <lipeng321@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2019-08-09 02:31:07 +00:00
hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_MAC_ALL, true);
ret = hclge_cmd_send(&hdev->hw, desc, desc_num);
if (ret) {
kfree(desc);
return ret;
}
data_size = min_t(u32, sizeof(hdev->mac_stats) / sizeof(u64), reg_num);
desc_data = (__le64 *)(&desc[0].data[0]);
for (i = 0; i < data_size; i++) {
/* data memory is continuous becase only the first desc has a
* header in this command
*/
*data += le64_to_cpu(*desc_data);
data++;
desc_data++;
}
kfree(desc);
return 0;
}
static int hclge_mac_query_reg_num(struct hclge_dev *hdev, u32 *reg_num)
{
struct hclge_desc desc;
int ret;
/* Driver needs total register number of both valid registers and
* reserved registers, but the old firmware only returns number
* of valid registers in device V2. To be compatible with these
* devices, driver uses a fixed value.
*/
if (hdev->ae_dev->dev_version == HNAE3_DEVICE_VERSION_V2) {
*reg_num = HCLGE_MAC_STATS_MAX_NUM_V1;
return 0;
}
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_MAC_REG_NUM, true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to query mac statistic reg number, ret = %d\n",
ret);
return ret;
}
*reg_num = le32_to_cpu(desc.data[0]);
if (*reg_num == 0) {
dev_err(&hdev->pdev->dev,
"mac statistic reg number is invalid!\n");
return -ENODATA;
}
return 0;
}
int hclge_mac_update_stats(struct hclge_dev *hdev)
{
/* The firmware supports the new statistics acquisition method */
if (hdev->ae_dev->dev_specs.mac_stats_num)
return hclge_mac_update_stats_complete(hdev);
else
return hclge_mac_update_stats_defective(hdev);
}
static int hclge_comm_get_count(struct hclge_dev *hdev,
const struct hclge_comm_stats_str strs[],
u32 size)
{
int count = 0;
u32 i;
for (i = 0; i < size; i++)
if (strs[i].stats_num <= hdev->ae_dev->dev_specs.mac_stats_num)
count++;
return count;
}
static u64 *hclge_comm_get_stats(struct hclge_dev *hdev,
const struct hclge_comm_stats_str strs[],
int size, u64 *data)
{
u64 *buf = data;
u32 i;
for (i = 0; i < size; i++) {
if (strs[i].stats_num > hdev->ae_dev->dev_specs.mac_stats_num)
continue;
*buf = HCLGE_STATS_READ(&hdev->mac_stats, strs[i].offset);
buf++;
}
return buf;
}
static u8 *hclge_comm_get_strings(struct hclge_dev *hdev, u32 stringset,
const struct hclge_comm_stats_str strs[],
int size, u8 *data)
{
char *buff = (char *)data;
u32 i;
if (stringset != ETH_SS_STATS)
return buff;
for (i = 0; i < size; i++) {
if (strs[i].stats_num > hdev->ae_dev->dev_specs.mac_stats_num)
continue;
snprintf(buff, ETH_GSTRING_LEN, "%s", strs[i].desc);
buff = buff + ETH_GSTRING_LEN;
}
return (u8 *)buff;
}
static void hclge_update_stats_for_all(struct hclge_dev *hdev)
{
struct hnae3_handle *handle;
int status;
handle = &hdev->vport[0].nic;
if (handle->client) {
status = hclge_comm_tqps_update_stats(handle, &hdev->hw.hw);
if (status) {
dev_err(&hdev->pdev->dev,
"Update TQPS stats fail, status = %d.\n",
status);
}
}
hclge_update_fec_stats(hdev);
status = hclge_mac_update_stats(hdev);
if (status)
dev_err(&hdev->pdev->dev,
"Update MAC stats fail, status = %d.\n", status);
}
static void hclge_update_stats(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int status;
if (test_and_set_bit(HCLGE_STATE_STATISTICS_UPDATING, &hdev->state))
return;
status = hclge_mac_update_stats(hdev);
if (status)
dev_err(&hdev->pdev->dev,
"Update MAC stats fail, status = %d.\n",
status);
status = hclge_comm_tqps_update_stats(handle, &hdev->hw.hw);
if (status)
dev_err(&hdev->pdev->dev,
"Update TQPS stats fail, status = %d.\n",
status);
clear_bit(HCLGE_STATE_STATISTICS_UPDATING, &hdev->state);
}
static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset)
{
#define HCLGE_LOOPBACK_TEST_FLAGS (HNAE3_SUPPORT_APP_LOOPBACK | \
HNAE3_SUPPORT_PHY_LOOPBACK | \
HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK | \
HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK | \
HNAE3_SUPPORT_EXTERNAL_LOOPBACK)
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int count = 0;
/* Loopback test support rules:
* mac: only GE mode support
* serdes: all mac mode will support include GE/XGE/LGE/CGE
* phy: only support when phy device exist on board
*/
if (stringset == ETH_SS_TEST) {
/* clear loopback bit flags at first */
handle->flags = (handle->flags & (~HCLGE_LOOPBACK_TEST_FLAGS));
if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2 ||
hdev->hw.mac.speed == HCLGE_MAC_SPEED_10M ||
hdev->hw.mac.speed == HCLGE_MAC_SPEED_100M ||
hdev->hw.mac.speed == HCLGE_MAC_SPEED_1G) {
count += 1;
handle->flags |= HNAE3_SUPPORT_APP_LOOPBACK;
}
if (hdev->ae_dev->dev_specs.hilink_version !=
HCLGE_HILINK_H60) {
count += 1;
handle->flags |= HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK;
}
count += 1;
handle->flags |= HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK;
count += 1;
handle->flags |= HNAE3_SUPPORT_EXTERNAL_LOOPBACK;
if ((hdev->hw.mac.phydev && hdev->hw.mac.phydev->drv &&
hdev->hw.mac.phydev->drv->set_loopback) ||
hnae3_dev_phy_imp_supported(hdev)) {
count += 1;
handle->flags |= HNAE3_SUPPORT_PHY_LOOPBACK;
}
} else if (stringset == ETH_SS_STATS) {
count = hclge_comm_get_count(hdev, g_mac_stats_string,
ARRAY_SIZE(g_mac_stats_string)) +
hclge_comm_tqps_get_sset_count(handle);
}
return count;
}
static void hclge_get_strings(struct hnae3_handle *handle, u32 stringset,
u8 *data)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u8 *p = (char *)data;
int size;
if (stringset == ETH_SS_STATS) {
size = ARRAY_SIZE(g_mac_stats_string);
p = hclge_comm_get_strings(hdev, stringset, g_mac_stats_string,
size, p);
p = hclge_comm_tqps_get_strings(handle, p);
} else if (stringset == ETH_SS_TEST) {
if (handle->flags & HNAE3_SUPPORT_EXTERNAL_LOOPBACK) {
memcpy(p, hns3_nic_test_strs[HNAE3_LOOP_EXTERNAL],
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
if (handle->flags & HNAE3_SUPPORT_APP_LOOPBACK) {
memcpy(p, hns3_nic_test_strs[HNAE3_LOOP_APP],
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
if (handle->flags & HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK) {
memcpy(p, hns3_nic_test_strs[HNAE3_LOOP_SERIAL_SERDES],
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
if (handle->flags & HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK) {
memcpy(p,
hns3_nic_test_strs[HNAE3_LOOP_PARALLEL_SERDES],
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
if (handle->flags & HNAE3_SUPPORT_PHY_LOOPBACK) {
memcpy(p, hns3_nic_test_strs[HNAE3_LOOP_PHY],
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
}
}
static void hclge_get_stats(struct hnae3_handle *handle, u64 *data)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u64 *p;
p = hclge_comm_get_stats(hdev, g_mac_stats_string,
ARRAY_SIZE(g_mac_stats_string), data);
p = hclge_comm_tqps_get_stats(handle, p);
}
static void hclge_get_mac_stat(struct hnae3_handle *handle,
struct hns3_mac_stats *mac_stats)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
hclge_update_stats(handle);
mac_stats->tx_pause_cnt = hdev->mac_stats.mac_tx_mac_pause_num;
mac_stats->rx_pause_cnt = hdev->mac_stats.mac_rx_mac_pause_num;
}
static int hclge_parse_func_status(struct hclge_dev *hdev,
struct hclge_func_status_cmd *status)
{
#define HCLGE_MAC_ID_MASK 0xF
if (!(status->pf_state & HCLGE_PF_STATE_DONE))
return -EINVAL;
/* Set the pf to main pf */
if (status->pf_state & HCLGE_PF_STATE_MAIN)
hdev->flag |= HCLGE_FLAG_MAIN;
else
hdev->flag &= ~HCLGE_FLAG_MAIN;
hdev->hw.mac.mac_id = status->mac_id & HCLGE_MAC_ID_MASK;
return 0;
}
static int hclge_query_function_status(struct hclge_dev *hdev)
{
#define HCLGE_QUERY_MAX_CNT 5
struct hclge_func_status_cmd *req;
struct hclge_desc desc;
int timeout = 0;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_FUNC_STATUS, true);
req = (struct hclge_func_status_cmd *)desc.data;
do {
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"query function status failed %d.\n", ret);
return ret;
}
/* Check pf reset is done */
if (req->pf_state)
break;
usleep_range(1000, 2000);
} while (timeout++ < HCLGE_QUERY_MAX_CNT);
return hclge_parse_func_status(hdev, req);
}
static int hclge_query_pf_resource(struct hclge_dev *hdev)
{
struct hclge_pf_res_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_PF_RSRC, true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"query pf resource failed %d.\n", ret);
return ret;
}
req = (struct hclge_pf_res_cmd *)desc.data;
hdev->num_tqps = le16_to_cpu(req->tqp_num) +
le16_to_cpu(req->ext_tqp_num);
hdev->pkt_buf_size = le16_to_cpu(req->buf_size) << HCLGE_BUF_UNIT_S;
if (req->tx_buf_size)
hdev->tx_buf_size =
le16_to_cpu(req->tx_buf_size) << HCLGE_BUF_UNIT_S;
else
hdev->tx_buf_size = HCLGE_DEFAULT_TX_BUF;
hdev->tx_buf_size = roundup(hdev->tx_buf_size, HCLGE_BUF_SIZE_UNIT);
if (req->dv_buf_size)
hdev->dv_buf_size =
le16_to_cpu(req->dv_buf_size) << HCLGE_BUF_UNIT_S;
else
hdev->dv_buf_size = HCLGE_DEFAULT_DV;
hdev->dv_buf_size = roundup(hdev->dv_buf_size, HCLGE_BUF_SIZE_UNIT);
hdev->num_nic_msi = le16_to_cpu(req->msixcap_localid_number_nic);
if (hdev->num_nic_msi < HNAE3_MIN_VECTOR_NUM) {
dev_err(&hdev->pdev->dev,
"only %u msi resources available, not enough for pf(min:2).\n",
hdev->num_nic_msi);
return -EINVAL;
}
if (hnae3_dev_roce_supported(hdev)) {
hdev->num_roce_msi =
le16_to_cpu(req->pf_intr_vector_number_roce);
/* PF should have NIC vectors and Roce vectors,
* NIC vectors are queued before Roce vectors.
*/
hdev->num_msi = hdev->num_nic_msi + hdev->num_roce_msi;
} else {
hdev->num_msi = hdev->num_nic_msi;
}
return 0;
}
static int hclge_parse_speed(u8 speed_cmd, u32 *speed)
{
switch (speed_cmd) {
case HCLGE_FW_MAC_SPEED_10M:
*speed = HCLGE_MAC_SPEED_10M;
break;
case HCLGE_FW_MAC_SPEED_100M:
*speed = HCLGE_MAC_SPEED_100M;
break;
case HCLGE_FW_MAC_SPEED_1G:
*speed = HCLGE_MAC_SPEED_1G;
break;
case HCLGE_FW_MAC_SPEED_10G:
*speed = HCLGE_MAC_SPEED_10G;
break;
case HCLGE_FW_MAC_SPEED_25G:
*speed = HCLGE_MAC_SPEED_25G;
break;
case HCLGE_FW_MAC_SPEED_40G:
*speed = HCLGE_MAC_SPEED_40G;
break;
case HCLGE_FW_MAC_SPEED_50G:
*speed = HCLGE_MAC_SPEED_50G;
break;
case HCLGE_FW_MAC_SPEED_100G:
*speed = HCLGE_MAC_SPEED_100G;
break;
case HCLGE_FW_MAC_SPEED_200G:
*speed = HCLGE_MAC_SPEED_200G;
break;
default:
return -EINVAL;
}
return 0;
}
static const struct hclge_speed_bit_map speed_bit_map[] = {
{HCLGE_MAC_SPEED_10M, HCLGE_SUPPORT_10M_BIT},
{HCLGE_MAC_SPEED_100M, HCLGE_SUPPORT_100M_BIT},
{HCLGE_MAC_SPEED_1G, HCLGE_SUPPORT_1G_BIT},
{HCLGE_MAC_SPEED_10G, HCLGE_SUPPORT_10G_BIT},
{HCLGE_MAC_SPEED_25G, HCLGE_SUPPORT_25G_BIT},
{HCLGE_MAC_SPEED_40G, HCLGE_SUPPORT_40G_BIT},
{HCLGE_MAC_SPEED_50G, HCLGE_SUPPORT_50G_BITS},
{HCLGE_MAC_SPEED_100G, HCLGE_SUPPORT_100G_BITS},
{HCLGE_MAC_SPEED_200G, HCLGE_SUPPORT_200G_BITS},
};
static int hclge_get_speed_bit(u32 speed, u32 *speed_bit)
{
u16 i;
for (i = 0; i < ARRAY_SIZE(speed_bit_map); i++) {
if (speed == speed_bit_map[i].speed) {
*speed_bit = speed_bit_map[i].speed_bit;
return 0;
}
}
return -EINVAL;
}
static int hclge_check_port_speed(struct hnae3_handle *handle, u32 speed)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u32 speed_ability = hdev->hw.mac.speed_ability;
u32 speed_bit = 0;
int ret;
ret = hclge_get_speed_bit(speed, &speed_bit);
if (ret)
return ret;
if (speed_bit & speed_ability)
return 0;
return -EINVAL;
}
static void hclge_update_fec_support(struct hclge_mac *mac)
{
linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, mac->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, mac->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, mac->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported);
if (mac->fec_ability & BIT(HNAE3_FEC_BASER))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT,
mac->supported);
if (mac->fec_ability & BIT(HNAE3_FEC_RS))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT,
mac->supported);
if (mac->fec_ability & BIT(HNAE3_FEC_LLRS))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
mac->supported);
if (mac->fec_ability & BIT(HNAE3_FEC_NONE))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT,
mac->supported);
}
static const struct hclge_link_mode_bmap hclge_sr_link_mode_bmap[] = {
{HCLGE_SUPPORT_10G_BIT, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT},
{HCLGE_SUPPORT_25G_BIT, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT},
{HCLGE_SUPPORT_40G_BIT, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT},
{HCLGE_SUPPORT_50G_R2_BIT, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT},
{HCLGE_SUPPORT_50G_R1_BIT, ETHTOOL_LINK_MODE_50000baseSR_Full_BIT},
{HCLGE_SUPPORT_100G_R4_BIT, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT},
{HCLGE_SUPPORT_100G_R2_BIT, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT},
{HCLGE_SUPPORT_200G_R4_EXT_BIT,
ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT},
{HCLGE_SUPPORT_200G_R4_BIT, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT},
};
static const struct hclge_link_mode_bmap hclge_lr_link_mode_bmap[] = {
{HCLGE_SUPPORT_10G_BIT, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT},
{HCLGE_SUPPORT_40G_BIT, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT},
{HCLGE_SUPPORT_50G_R1_BIT, ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT},
{HCLGE_SUPPORT_100G_R4_BIT,
ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT},
{HCLGE_SUPPORT_100G_R2_BIT,
ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT},
{HCLGE_SUPPORT_200G_R4_EXT_BIT,
ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT},
{HCLGE_SUPPORT_200G_R4_BIT,
ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT},
};
static const struct hclge_link_mode_bmap hclge_cr_link_mode_bmap[] = {
{HCLGE_SUPPORT_10G_BIT, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT},
{HCLGE_SUPPORT_25G_BIT, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT},
{HCLGE_SUPPORT_40G_BIT, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT},
{HCLGE_SUPPORT_50G_R2_BIT, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT},
{HCLGE_SUPPORT_50G_R1_BIT, ETHTOOL_LINK_MODE_50000baseCR_Full_BIT},
{HCLGE_SUPPORT_100G_R4_BIT, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT},
{HCLGE_SUPPORT_100G_R2_BIT, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT},
{HCLGE_SUPPORT_200G_R4_EXT_BIT,
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT},
{HCLGE_SUPPORT_200G_R4_BIT, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT},
};
static const struct hclge_link_mode_bmap hclge_kr_link_mode_bmap[] = {
{HCLGE_SUPPORT_1G_BIT, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT},
{HCLGE_SUPPORT_10G_BIT, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT},
{HCLGE_SUPPORT_25G_BIT, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT},
{HCLGE_SUPPORT_40G_BIT, ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT},
{HCLGE_SUPPORT_50G_R2_BIT, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT},
{HCLGE_SUPPORT_50G_R1_BIT, ETHTOOL_LINK_MODE_50000baseKR_Full_BIT},
{HCLGE_SUPPORT_100G_R4_BIT, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT},
{HCLGE_SUPPORT_100G_R2_BIT, ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT},
{HCLGE_SUPPORT_200G_R4_EXT_BIT,
ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT},
{HCLGE_SUPPORT_200G_R4_BIT, ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT},
};
static void hclge_convert_setting_sr(u16 speed_ability,
unsigned long *link_mode)
{
int i;
for (i = 0; i < ARRAY_SIZE(hclge_sr_link_mode_bmap); i++) {
if (speed_ability & hclge_sr_link_mode_bmap[i].support_bit)
linkmode_set_bit(hclge_sr_link_mode_bmap[i].link_mode,
link_mode);
}
}
static void hclge_convert_setting_lr(u16 speed_ability,
unsigned long *link_mode)
{
int i;
for (i = 0; i < ARRAY_SIZE(hclge_lr_link_mode_bmap); i++) {
if (speed_ability & hclge_lr_link_mode_bmap[i].support_bit)
linkmode_set_bit(hclge_lr_link_mode_bmap[i].link_mode,
link_mode);
}
}
static void hclge_convert_setting_cr(u16 speed_ability,
unsigned long *link_mode)
{
int i;
for (i = 0; i < ARRAY_SIZE(hclge_cr_link_mode_bmap); i++) {
if (speed_ability & hclge_cr_link_mode_bmap[i].support_bit)
linkmode_set_bit(hclge_cr_link_mode_bmap[i].link_mode,
link_mode);
}
}
static void hclge_convert_setting_kr(u16 speed_ability,
unsigned long *link_mode)
{
int i;
for (i = 0; i < ARRAY_SIZE(hclge_kr_link_mode_bmap); i++) {
if (speed_ability & hclge_kr_link_mode_bmap[i].support_bit)
linkmode_set_bit(hclge_kr_link_mode_bmap[i].link_mode,
link_mode);
}
}
static void hclge_convert_setting_fec(struct hclge_mac *mac)
{
/* If firmware has reported fec_ability, don't need to convert by speed */
if (mac->fec_ability)
goto out;
switch (mac->speed) {
case HCLGE_MAC_SPEED_10G:
case HCLGE_MAC_SPEED_40G:
mac->fec_ability = BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_AUTO) |
BIT(HNAE3_FEC_NONE);
break;
case HCLGE_MAC_SPEED_25G:
case HCLGE_MAC_SPEED_50G:
mac->fec_ability = BIT(HNAE3_FEC_BASER) | BIT(HNAE3_FEC_RS) |
BIT(HNAE3_FEC_AUTO) | BIT(HNAE3_FEC_NONE);
break;
case HCLGE_MAC_SPEED_100G:
mac->fec_ability = BIT(HNAE3_FEC_RS) | BIT(HNAE3_FEC_AUTO) |
BIT(HNAE3_FEC_NONE);
break;
case HCLGE_MAC_SPEED_200G:
mac->fec_ability = BIT(HNAE3_FEC_RS) | BIT(HNAE3_FEC_AUTO) |
BIT(HNAE3_FEC_LLRS);
break;
default:
mac->fec_ability = 0;
break;
}
out:
hclge_update_fec_support(mac);
}
static void hclge_parse_fiber_link_mode(struct hclge_dev *hdev,
u16 speed_ability)
{
struct hclge_mac *mac = &hdev->hw.mac;
if (speed_ability & HCLGE_SUPPORT_1G_BIT)
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
mac->supported);
hclge_convert_setting_sr(speed_ability, mac->supported);
hclge_convert_setting_lr(speed_ability, mac->supported);
hclge_convert_setting_cr(speed_ability, mac->supported);
if (hnae3_dev_fec_supported(hdev))
hclge_convert_setting_fec(mac);
if (hnae3_dev_pause_supported(hdev))
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mac->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mac->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported);
}
static void hclge_parse_backplane_link_mode(struct hclge_dev *hdev,
u16 speed_ability)
{
struct hclge_mac *mac = &hdev->hw.mac;
hclge_convert_setting_kr(speed_ability, mac->supported);
if (hnae3_dev_fec_supported(hdev))
hclge_convert_setting_fec(mac);
if (hnae3_dev_pause_supported(hdev))
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, mac->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, mac->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, mac->supported);
}
static void hclge_parse_copper_link_mode(struct hclge_dev *hdev,
u16 speed_ability)
{
unsigned long *supported = hdev->hw.mac.supported;
/* default to support all speed for GE port */
if (!speed_ability)
speed_ability = HCLGE_SUPPORT_GE;
if (speed_ability & HCLGE_SUPPORT_1G_BIT)
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
supported);
if (speed_ability & HCLGE_SUPPORT_100M_BIT) {
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
supported);
}
if (speed_ability & HCLGE_SUPPORT_10M_BIT) {
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported);
}
if (hnae3_dev_pause_supported(hdev)) {
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported);
}
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, supported);
}
static void hclge_parse_link_mode(struct hclge_dev *hdev, u16 speed_ability)
{
u8 media_type = hdev->hw.mac.media_type;
if (media_type == HNAE3_MEDIA_TYPE_FIBER)
hclge_parse_fiber_link_mode(hdev, speed_ability);
else if (media_type == HNAE3_MEDIA_TYPE_COPPER)
hclge_parse_copper_link_mode(hdev, speed_ability);
else if (media_type == HNAE3_MEDIA_TYPE_BACKPLANE)
hclge_parse_backplane_link_mode(hdev, speed_ability);
}
static u32 hclge_get_max_speed(u16 speed_ability)
{
if (speed_ability & HCLGE_SUPPORT_200G_BITS)
return HCLGE_MAC_SPEED_200G;
if (speed_ability & HCLGE_SUPPORT_100G_BITS)
return HCLGE_MAC_SPEED_100G;
if (speed_ability & HCLGE_SUPPORT_50G_BITS)
return HCLGE_MAC_SPEED_50G;
if (speed_ability & HCLGE_SUPPORT_40G_BIT)
return HCLGE_MAC_SPEED_40G;
if (speed_ability & HCLGE_SUPPORT_25G_BIT)
return HCLGE_MAC_SPEED_25G;
if (speed_ability & HCLGE_SUPPORT_10G_BIT)
return HCLGE_MAC_SPEED_10G;
if (speed_ability & HCLGE_SUPPORT_1G_BIT)
return HCLGE_MAC_SPEED_1G;
if (speed_ability & HCLGE_SUPPORT_100M_BIT)
return HCLGE_MAC_SPEED_100M;
if (speed_ability & HCLGE_SUPPORT_10M_BIT)
return HCLGE_MAC_SPEED_10M;
return HCLGE_MAC_SPEED_1G;
}
static void hclge_parse_cfg(struct hclge_cfg *cfg, struct hclge_desc *desc)
{
#define HCLGE_TX_SPARE_SIZE_UNIT 4096
#define SPEED_ABILITY_EXT_SHIFT 8
struct hclge_cfg_param_cmd *req;
u64 mac_addr_tmp_high;
u16 speed_ability_ext;
u64 mac_addr_tmp;
unsigned int i;
req = (struct hclge_cfg_param_cmd *)desc[0].data;
/* get the configuration */
cfg->tc_num = hnae3_get_field(__le32_to_cpu(req->param[0]),
HCLGE_CFG_TC_NUM_M, HCLGE_CFG_TC_NUM_S);
cfg->tqp_desc_num = hnae3_get_field(__le32_to_cpu(req->param[0]),
HCLGE_CFG_TQP_DESC_N_M,
HCLGE_CFG_TQP_DESC_N_S);
cfg->phy_addr = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_PHY_ADDR_M,
HCLGE_CFG_PHY_ADDR_S);
cfg->media_type = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_MEDIA_TP_M,
HCLGE_CFG_MEDIA_TP_S);
cfg->rx_buf_len = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_RX_BUF_LEN_M,
HCLGE_CFG_RX_BUF_LEN_S);
/* get mac_address */
mac_addr_tmp = __le32_to_cpu(req->param[2]);
mac_addr_tmp_high = hnae3_get_field(__le32_to_cpu(req->param[3]),
HCLGE_CFG_MAC_ADDR_H_M,
HCLGE_CFG_MAC_ADDR_H_S);
mac_addr_tmp |= (mac_addr_tmp_high << 31) << 1;
cfg->default_speed = hnae3_get_field(__le32_to_cpu(req->param[3]),
HCLGE_CFG_DEFAULT_SPEED_M,
HCLGE_CFG_DEFAULT_SPEED_S);
cfg->vf_rss_size_max = hnae3_get_field(__le32_to_cpu(req->param[3]),
HCLGE_CFG_RSS_SIZE_M,
HCLGE_CFG_RSS_SIZE_S);
for (i = 0; i < ETH_ALEN; i++)
cfg->mac_addr[i] = (mac_addr_tmp >> (8 * i)) & 0xff;
req = (struct hclge_cfg_param_cmd *)desc[1].data;
cfg->numa_node_map = __le32_to_cpu(req->param[0]);
cfg->speed_ability = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_SPEED_ABILITY_M,
HCLGE_CFG_SPEED_ABILITY_S);
speed_ability_ext = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_SPEED_ABILITY_EXT_M,
HCLGE_CFG_SPEED_ABILITY_EXT_S);
cfg->speed_ability |= speed_ability_ext << SPEED_ABILITY_EXT_SHIFT;
cfg->vlan_fliter_cap = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_VLAN_FLTR_CAP_M,
HCLGE_CFG_VLAN_FLTR_CAP_S);
cfg->umv_space = hnae3_get_field(__le32_to_cpu(req->param[1]),
HCLGE_CFG_UMV_TBL_SPACE_M,
HCLGE_CFG_UMV_TBL_SPACE_S);
cfg->pf_rss_size_max = hnae3_get_field(__le32_to_cpu(req->param[2]),
HCLGE_CFG_PF_RSS_SIZE_M,
HCLGE_CFG_PF_RSS_SIZE_S);
/* HCLGE_CFG_PF_RSS_SIZE_M is the PF max rss size, which is a
* power of 2, instead of reading out directly. This would
* be more flexible for future changes and expansions.
* When VF max rss size field is HCLGE_CFG_RSS_SIZE_S,
* it does not make sense if PF's field is 0. In this case, PF and VF
* has the same max rss size filed: HCLGE_CFG_RSS_SIZE_S.
*/
cfg->pf_rss_size_max = cfg->pf_rss_size_max ?
1U << cfg->pf_rss_size_max :
cfg->vf_rss_size_max;
/* The unit of the tx spare buffer size queried from configuration
* file is HCLGE_TX_SPARE_SIZE_UNIT(4096) bytes, so a conversion is
* needed here.
*/
cfg->tx_spare_buf_size = hnae3_get_field(__le32_to_cpu(req->param[2]),
HCLGE_CFG_TX_SPARE_BUF_SIZE_M,
HCLGE_CFG_TX_SPARE_BUF_SIZE_S);
cfg->tx_spare_buf_size *= HCLGE_TX_SPARE_SIZE_UNIT;
}
/* hclge_get_cfg: query the static parameter from flash
* @hdev: pointer to struct hclge_dev
* @hcfg: the config structure to be getted
*/
static int hclge_get_cfg(struct hclge_dev *hdev, struct hclge_cfg *hcfg)
{
struct hclge_desc desc[HCLGE_PF_CFG_DESC_NUM];
struct hclge_cfg_param_cmd *req;
unsigned int i;
int ret;
for (i = 0; i < HCLGE_PF_CFG_DESC_NUM; i++) {
u32 offset = 0;
req = (struct hclge_cfg_param_cmd *)desc[i].data;
hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_GET_CFG_PARAM,
true);
hnae3_set_field(offset, HCLGE_CFG_OFFSET_M,
HCLGE_CFG_OFFSET_S, i * HCLGE_CFG_RD_LEN_BYTES);
/* Len should be united by 4 bytes when send to hardware */
hnae3_set_field(offset, HCLGE_CFG_RD_LEN_M, HCLGE_CFG_RD_LEN_S,
HCLGE_CFG_RD_LEN_BYTES / HCLGE_CFG_RD_LEN_UNIT);
req->offset = cpu_to_le32(offset);
}
ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_PF_CFG_DESC_NUM);
if (ret) {
dev_err(&hdev->pdev->dev, "get config failed %d.\n", ret);
return ret;
}
hclge_parse_cfg(hcfg, desc);
return 0;
}
static void hclge_set_default_dev_specs(struct hclge_dev *hdev)
{
#define HCLGE_MAX_NON_TSO_BD_NUM 8U
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
ae_dev->dev_specs.max_non_tso_bd_num = HCLGE_MAX_NON_TSO_BD_NUM;
ae_dev->dev_specs.rss_ind_tbl_size = HCLGE_RSS_IND_TBL_SIZE;
ae_dev->dev_specs.rss_key_size = HCLGE_COMM_RSS_KEY_SIZE;
ae_dev->dev_specs.max_tm_rate = HCLGE_ETHER_MAX_RATE;
ae_dev->dev_specs.max_int_gl = HCLGE_DEF_MAX_INT_GL;
ae_dev->dev_specs.max_frm_size = HCLGE_MAC_MAX_FRAME;
ae_dev->dev_specs.max_qset_num = HCLGE_MAX_QSET_NUM;
ae_dev->dev_specs.umv_size = HCLGE_DEFAULT_UMV_SPACE_PER_PF;
ae_dev->dev_specs.tnl_num = 0;
}
static void hclge_parse_dev_specs(struct hclge_dev *hdev,
struct hclge_desc *desc)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
struct hclge_dev_specs_0_cmd *req0;
struct hclge_dev_specs_1_cmd *req1;
req0 = (struct hclge_dev_specs_0_cmd *)desc[0].data;
req1 = (struct hclge_dev_specs_1_cmd *)desc[1].data;
ae_dev->dev_specs.max_non_tso_bd_num = req0->max_non_tso_bd_num;
ae_dev->dev_specs.rss_ind_tbl_size =
le16_to_cpu(req0->rss_ind_tbl_size);
ae_dev->dev_specs.int_ql_max = le16_to_cpu(req0->int_ql_max);
ae_dev->dev_specs.rss_key_size = le16_to_cpu(req0->rss_key_size);
ae_dev->dev_specs.max_tm_rate = le32_to_cpu(req0->max_tm_rate);
ae_dev->dev_specs.max_qset_num = le16_to_cpu(req1->max_qset_num);
ae_dev->dev_specs.max_int_gl = le16_to_cpu(req1->max_int_gl);
ae_dev->dev_specs.max_frm_size = le16_to_cpu(req1->max_frm_size);
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;
ae_dev->dev_specs.hilink_version = req1->hilink_version;
}
static void hclge_check_dev_specs(struct hclge_dev *hdev)
{
struct hnae3_dev_specs *dev_specs = &hdev->ae_dev->dev_specs;
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)
dev_specs->rss_ind_tbl_size = HCLGE_RSS_IND_TBL_SIZE;
if (!dev_specs->rss_key_size)
dev_specs->rss_key_size = HCLGE_COMM_RSS_KEY_SIZE;
if (!dev_specs->max_tm_rate)
dev_specs->max_tm_rate = HCLGE_ETHER_MAX_RATE;
if (!dev_specs->max_qset_num)
dev_specs->max_qset_num = HCLGE_MAX_QSET_NUM;
if (!dev_specs->max_int_gl)
dev_specs->max_int_gl = HCLGE_DEF_MAX_INT_GL;
if (!dev_specs->max_frm_size)
dev_specs->max_frm_size = HCLGE_MAC_MAX_FRAME;
if (!dev_specs->umv_size)
dev_specs->umv_size = HCLGE_DEFAULT_UMV_SPACE_PER_PF;
}
static int hclge_query_mac_stats_num(struct hclge_dev *hdev)
{
u32 reg_num = 0;
int ret;
ret = hclge_mac_query_reg_num(hdev, &reg_num);
if (ret && ret != -EOPNOTSUPP)
return ret;
hdev->ae_dev->dev_specs.mac_stats_num = reg_num;
return 0;
}
static int hclge_query_dev_specs(struct hclge_dev *hdev)
{
struct hclge_desc desc[HCLGE_QUERY_DEV_SPECS_BD_NUM];
int ret;
int i;
ret = hclge_query_mac_stats_num(hdev);
if (ret)
return ret;
/* set default specifications as devices lower than version V3 do not
* support querying specifications from firmware.
*/
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3) {
hclge_set_default_dev_specs(hdev);
return 0;
}
for (i = 0; i < HCLGE_QUERY_DEV_SPECS_BD_NUM - 1; i++) {
hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_QUERY_DEV_SPECS,
true);
desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
}
hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_QUERY_DEV_SPECS, true);
ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_QUERY_DEV_SPECS_BD_NUM);
if (ret)
return ret;
hclge_parse_dev_specs(hdev, desc);
hclge_check_dev_specs(hdev);
return 0;
}
static int hclge_get_cap(struct hclge_dev *hdev)
{
int ret;
ret = hclge_query_function_status(hdev);
if (ret) {
dev_err(&hdev->pdev->dev,
"query function status error %d.\n", ret);
return ret;
}
/* get pf resource */
return hclge_query_pf_resource(hdev);
}
static void hclge_init_kdump_kernel_config(struct hclge_dev *hdev)
{
#define HCLGE_MIN_TX_DESC 64
#define HCLGE_MIN_RX_DESC 64
if (!is_kdump_kernel())
return;
dev_info(&hdev->pdev->dev,
"Running kdump kernel. Using minimal resources\n");
/* minimal queue pairs equals to the number of vports */
hdev->num_tqps = hdev->num_req_vfs + 1;
hdev->num_tx_desc = HCLGE_MIN_TX_DESC;
hdev->num_rx_desc = HCLGE_MIN_RX_DESC;
}
static void hclge_init_tc_config(struct hclge_dev *hdev)
{
unsigned int i;
if (hdev->tc_max > HNAE3_MAX_TC ||
hdev->tc_max < 1) {
dev_warn(&hdev->pdev->dev, "TC num = %u.\n",
hdev->tc_max);
hdev->tc_max = 1;
}
/* Dev does not support DCB */
if (!hnae3_dev_dcb_supported(hdev)) {
hdev->tc_max = 1;
hdev->pfc_max = 0;
} else {
hdev->pfc_max = hdev->tc_max;
}
hdev->tm_info.num_tc = 1;
/* Currently not support uncontiuous tc */
for (i = 0; i < hdev->tm_info.num_tc; i++)
hnae3_set_bit(hdev->hw_tc_map, i, 1);
hdev->tx_sch_mode = HCLGE_FLAG_TC_BASE_SCH_MODE;
}
static int hclge_configure(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
struct hclge_cfg cfg;
int ret;
ret = hclge_get_cfg(hdev, &cfg);
if (ret)
return ret;
hdev->base_tqp_pid = 0;
hdev->vf_rss_size_max = cfg.vf_rss_size_max;
hdev->pf_rss_size_max = cfg.pf_rss_size_max;
hdev->rx_buf_len = cfg.rx_buf_len;
ether_addr_copy(hdev->hw.mac.mac_addr, cfg.mac_addr);
hdev->hw.mac.media_type = cfg.media_type;
hdev->hw.mac.phy_addr = cfg.phy_addr;
hdev->num_tx_desc = cfg.tqp_desc_num;
hdev->num_rx_desc = cfg.tqp_desc_num;
hdev->tm_info.num_pg = 1;
hdev->tc_max = cfg.tc_num;
hdev->tm_info.hw_pfc_map = 0;
if (cfg.umv_space)
hdev->wanted_umv_size = cfg.umv_space;
else
hdev->wanted_umv_size = hdev->ae_dev->dev_specs.umv_size;
hdev->tx_spare_buf_size = cfg.tx_spare_buf_size;
hdev->gro_en = true;
if (cfg.vlan_fliter_cap == HCLGE_VLAN_FLTR_CAN_MDF)
set_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, ae_dev->caps);
if (hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
hdev->fd_en = true;
hdev->fd_active_type = HCLGE_FD_RULE_NONE;
}
ret = hclge_parse_speed(cfg.default_speed, &hdev->hw.mac.speed);
if (ret) {
dev_err(&hdev->pdev->dev, "failed to parse speed %u, ret = %d\n",
cfg.default_speed, ret);
return ret;
}
hclge_parse_link_mode(hdev, cfg.speed_ability);
hdev->hw.mac.max_speed = hclge_get_max_speed(cfg.speed_ability);
hclge_init_tc_config(hdev);
hclge_init_kdump_kernel_config(hdev);
return ret;
}
static int hclge_config_tso(struct hclge_dev *hdev, u16 tso_mss_min,
u16 tso_mss_max)
{
struct hclge_cfg_tso_status_cmd *req;
struct hclge_desc desc;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TSO_GENERIC_CONFIG, false);
req = (struct hclge_cfg_tso_status_cmd *)desc.data;
req->tso_mss_min = cpu_to_le16(tso_mss_min);
req->tso_mss_max = cpu_to_le16(tso_mss_max);
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
static int hclge_config_gro(struct hclge_dev *hdev)
{
struct hclge_cfg_gro_status_cmd *req;
struct hclge_desc desc;
int ret;
if (!hnae3_ae_dev_gro_supported(hdev->ae_dev))
return 0;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, false);
req = (struct hclge_cfg_gro_status_cmd *)desc.data;
req->gro_en = hdev->gro_en ? 1 : 0;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"GRO hardware config cmd failed, ret = %d\n", ret);
return ret;
}
static int hclge_alloc_tqps(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
struct hclge_comm_tqp *tqp;
int i;
hdev->htqp = devm_kcalloc(&hdev->pdev->dev, hdev->num_tqps,
sizeof(struct hclge_comm_tqp), GFP_KERNEL);
if (!hdev->htqp)
return -ENOMEM;
tqp = hdev->htqp;
for (i = 0; i < hdev->num_tqps; i++) {
tqp->dev = &hdev->pdev->dev;
tqp->index = i;
tqp->q.ae_algo = &ae_algo;
tqp->q.buf_size = hdev->rx_buf_len;
tqp->q.tx_desc_num = hdev->num_tx_desc;
tqp->q.rx_desc_num = hdev->num_rx_desc;
/* need an extended offset to configure queues >=
* HCLGE_TQP_MAX_SIZE_DEV_V2
*/
if (i < HCLGE_TQP_MAX_SIZE_DEV_V2)
tqp->q.io_base = hdev->hw.hw.io_base +
HCLGE_TQP_REG_OFFSET +
i * HCLGE_TQP_REG_SIZE;
else
tqp->q.io_base = hdev->hw.hw.io_base +
HCLGE_TQP_REG_OFFSET +
HCLGE_TQP_EXT_REG_OFFSET +
(i - HCLGE_TQP_MAX_SIZE_DEV_V2) *
HCLGE_TQP_REG_SIZE;
/* when device supports tx push and has device memory,
* the queue can execute push mode or doorbell mode on
* device memory.
*/
if (test_bit(HNAE3_DEV_SUPPORT_TX_PUSH_B, ae_dev->caps))
tqp->q.mem_base = hdev->hw.hw.mem_base +
HCLGE_TQP_MEM_OFFSET(hdev, i);
tqp++;
}
return 0;
}
static int hclge_map_tqps_to_func(struct hclge_dev *hdev, u16 func_id,
u16 tqp_pid, u16 tqp_vid, bool is_pf)
{
struct hclge_tqp_map_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SET_TQP_MAP, false);
req = (struct hclge_tqp_map_cmd *)desc.data;
req->tqp_id = cpu_to_le16(tqp_pid);
req->tqp_vf = func_id;
req->tqp_flag = 1U << HCLGE_TQP_MAP_EN_B;
if (!is_pf)
req->tqp_flag |= 1U << HCLGE_TQP_MAP_TYPE_B;
req->tqp_vid = cpu_to_le16(tqp_vid);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "TQP map failed %d.\n", ret);
return ret;
}
static int hclge_assign_tqp(struct hclge_vport *vport, u16 num_tqps)
{
struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
struct hclge_dev *hdev = vport->back;
int i, alloced;
for (i = 0, alloced = 0; i < hdev->num_tqps &&
alloced < num_tqps; i++) {
if (!hdev->htqp[i].alloced) {
hdev->htqp[i].q.handle = &vport->nic;
hdev->htqp[i].q.tqp_index = alloced;
hdev->htqp[i].q.tx_desc_num = kinfo->num_tx_desc;
hdev->htqp[i].q.rx_desc_num = kinfo->num_rx_desc;
kinfo->tqp[alloced] = &hdev->htqp[i].q;
hdev->htqp[i].alloced = true;
alloced++;
}
}
vport->alloc_tqps = alloced;
kinfo->rss_size = min_t(u16, hdev->pf_rss_size_max,
vport->alloc_tqps / hdev->tm_info.num_tc);
/* ensure one to one mapping between irq and queue at default */
kinfo->rss_size = min_t(u16, kinfo->rss_size,
(hdev->num_nic_msi - 1) / hdev->tm_info.num_tc);
return 0;
}
static int hclge_knic_setup(struct hclge_vport *vport, u16 num_tqps,
u16 num_tx_desc, u16 num_rx_desc)
{
struct hnae3_handle *nic = &vport->nic;
struct hnae3_knic_private_info *kinfo = &nic->kinfo;
struct hclge_dev *hdev = vport->back;
int ret;
kinfo->num_tx_desc = num_tx_desc;
kinfo->num_rx_desc = num_rx_desc;
kinfo->rx_buf_len = hdev->rx_buf_len;
kinfo->tx_spare_buf_size = hdev->tx_spare_buf_size;
kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, num_tqps,
sizeof(struct hnae3_queue *), GFP_KERNEL);
if (!kinfo->tqp)
return -ENOMEM;
ret = hclge_assign_tqp(vport, num_tqps);
if (ret)
dev_err(&hdev->pdev->dev, "fail to assign TQPs %d.\n", ret);
return ret;
}
static int hclge_map_tqp_to_vport(struct hclge_dev *hdev,
struct hclge_vport *vport)
{
struct hnae3_handle *nic = &vport->nic;
struct hnae3_knic_private_info *kinfo;
u16 i;
kinfo = &nic->kinfo;
for (i = 0; i < vport->alloc_tqps; i++) {
struct hclge_comm_tqp *q =
container_of(kinfo->tqp[i], struct hclge_comm_tqp, q);
bool is_pf;
int ret;
is_pf = !(vport->vport_id);
ret = hclge_map_tqps_to_func(hdev, vport->vport_id, q->index,
i, is_pf);
if (ret)
return ret;
}
return 0;
}
static int hclge_map_tqp(struct hclge_dev *hdev)
{
struct hclge_vport *vport = hdev->vport;
u16 i, num_vport;
num_vport = hdev->num_req_vfs + 1;
for (i = 0; i < num_vport; i++) {
int ret;
ret = hclge_map_tqp_to_vport(hdev, vport);
if (ret)
return ret;
vport++;
}
return 0;
}
static int hclge_vport_setup(struct hclge_vport *vport, u16 num_tqps)
{
struct hnae3_handle *nic = &vport->nic;
struct hclge_dev *hdev = vport->back;
int ret;
nic->pdev = hdev->pdev;
nic->ae_algo = &ae_algo;
nic->numa_node_mask = hdev->numa_node_mask;
nic->kinfo.io_base = hdev->hw.hw.io_base;
ret = hclge_knic_setup(vport, num_tqps,
hdev->num_tx_desc, hdev->num_rx_desc);
if (ret)
dev_err(&hdev->pdev->dev, "knic setup failed %d\n", ret);
return ret;
}
static int hclge_alloc_vport(struct hclge_dev *hdev)
{
struct pci_dev *pdev = hdev->pdev;
struct hclge_vport *vport;
u32 tqp_main_vport;
u32 tqp_per_vport;
int num_vport, i;
int ret;
/* We need to alloc a vport for main NIC of PF */
num_vport = hdev->num_req_vfs + 1;
if (hdev->num_tqps < num_vport) {
dev_err(&hdev->pdev->dev, "tqps(%u) is less than vports(%d)",
hdev->num_tqps, num_vport);
return -EINVAL;
}
/* Alloc the same number of TQPs for every vport */
tqp_per_vport = hdev->num_tqps / num_vport;
tqp_main_vport = tqp_per_vport + hdev->num_tqps % num_vport;
vport = devm_kcalloc(&pdev->dev, num_vport, sizeof(struct hclge_vport),
GFP_KERNEL);
if (!vport)
return -ENOMEM;
hdev->vport = vport;
hdev->num_alloc_vport = num_vport;
if (IS_ENABLED(CONFIG_PCI_IOV))
hdev->num_alloc_vfs = hdev->num_req_vfs;
for (i = 0; i < num_vport; i++) {
vport->back = hdev;
vport->vport_id = i;
vport->vf_info.link_state = IFLA_VF_LINK_STATE_AUTO;
vport->mps = HCLGE_MAC_DEFAULT_FRAME;
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;
vport->req_vlan_fltr_en = true;
INIT_LIST_HEAD(&vport->vlan_list);
INIT_LIST_HEAD(&vport->uc_mac_list);
INIT_LIST_HEAD(&vport->mc_mac_list);
spin_lock_init(&vport->mac_list_lock);
if (i == 0)
ret = hclge_vport_setup(vport, tqp_main_vport);
else
ret = hclge_vport_setup(vport, tqp_per_vport);
if (ret) {
dev_err(&pdev->dev,
"vport setup failed for vport %d, %d\n",
i, ret);
return ret;
}
vport++;
}
return 0;
}
static int hclge_cmd_alloc_tx_buff(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
/* TX buffer size is unit by 128 byte */
#define HCLGE_BUF_SIZE_UNIT_SHIFT 7
#define HCLGE_BUF_SIZE_UPDATE_EN_MSK BIT(15)
struct hclge_tx_buff_alloc_cmd *req;
struct hclge_desc desc;
int ret;
u8 i;
req = (struct hclge_tx_buff_alloc_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TX_BUFF_ALLOC, 0);
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
u32 buf_size = buf_alloc->priv_buf[i].tx_buf_size;
req->tx_pkt_buff[i] =
cpu_to_le16((buf_size >> HCLGE_BUF_SIZE_UNIT_SHIFT) |
HCLGE_BUF_SIZE_UPDATE_EN_MSK);
}
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "tx buffer alloc cmd failed %d.\n",
ret);
return ret;
}
static int hclge_tx_buffer_alloc(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
int ret = hclge_cmd_alloc_tx_buff(hdev, buf_alloc);
if (ret)
dev_err(&hdev->pdev->dev, "tx buffer alloc failed %d\n", ret);
return ret;
}
static u32 hclge_get_tc_num(struct hclge_dev *hdev)
{
unsigned int i;
u32 cnt = 0;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++)
if (hdev->hw_tc_map & BIT(i))
cnt++;
return cnt;
}
/* Get the number of pfc enabled TCs, which have private buffer */
static int hclge_get_pfc_priv_num(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_priv_buf *priv;
unsigned int i;
int cnt = 0;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
priv = &buf_alloc->priv_buf[i];
if ((hdev->tm_info.hw_pfc_map & BIT(i)) &&
priv->enable)
cnt++;
}
return cnt;
}
/* Get the number of pfc disabled TCs, which have private buffer */
static int hclge_get_no_pfc_priv_num(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_priv_buf *priv;
unsigned int i;
int cnt = 0;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
priv = &buf_alloc->priv_buf[i];
if (hdev->hw_tc_map & BIT(i) &&
!(hdev->tm_info.hw_pfc_map & BIT(i)) &&
priv->enable)
cnt++;
}
return cnt;
}
static u32 hclge_get_rx_priv_buff_alloced(struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_priv_buf *priv;
u32 rx_priv = 0;
int i;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
priv = &buf_alloc->priv_buf[i];
if (priv->enable)
rx_priv += priv->buf_size;
}
return rx_priv;
}
static u32 hclge_get_tx_buff_alloced(struct hclge_pkt_buf_alloc *buf_alloc)
{
u32 i, total_tx_size = 0;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++)
total_tx_size += buf_alloc->priv_buf[i].tx_buf_size;
return total_tx_size;
}
static bool hclge_is_rx_buf_ok(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc,
u32 rx_all)
{
u32 shared_buf_min, shared_buf_tc, shared_std, hi_thrd, lo_thrd;
u32 tc_num = hclge_get_tc_num(hdev);
u32 shared_buf, aligned_mps;
u32 rx_priv;
int i;
aligned_mps = roundup(hdev->mps, HCLGE_BUF_SIZE_UNIT);
if (hnae3_dev_dcb_supported(hdev))
shared_buf_min = HCLGE_BUF_MUL_BY * aligned_mps +
hdev->dv_buf_size;
else
shared_buf_min = aligned_mps + HCLGE_NON_DCB_ADDITIONAL_BUF
+ hdev->dv_buf_size;
shared_buf_tc = tc_num * aligned_mps + aligned_mps;
shared_std = roundup(max_t(u32, shared_buf_min, shared_buf_tc),
HCLGE_BUF_SIZE_UNIT);
rx_priv = hclge_get_rx_priv_buff_alloced(buf_alloc);
if (rx_all < rx_priv + shared_std)
return false;
shared_buf = rounddown(rx_all - rx_priv, HCLGE_BUF_SIZE_UNIT);
buf_alloc->s_buf.buf_size = shared_buf;
if (hnae3_dev_dcb_supported(hdev)) {
buf_alloc->s_buf.self.high = shared_buf - hdev->dv_buf_size;
buf_alloc->s_buf.self.low = buf_alloc->s_buf.self.high
- roundup(aligned_mps / HCLGE_BUF_DIV_BY,
HCLGE_BUF_SIZE_UNIT);
} else {
buf_alloc->s_buf.self.high = aligned_mps +
HCLGE_NON_DCB_ADDITIONAL_BUF;
buf_alloc->s_buf.self.low = aligned_mps;
}
if (hnae3_dev_dcb_supported(hdev)) {
hi_thrd = shared_buf - hdev->dv_buf_size;
if (tc_num <= NEED_RESERVE_TC_NUM)
hi_thrd = hi_thrd * BUF_RESERVE_PERCENT
/ BUF_MAX_PERCENT;
if (tc_num)
hi_thrd = hi_thrd / tc_num;
hi_thrd = max_t(u32, hi_thrd, HCLGE_BUF_MUL_BY * aligned_mps);
hi_thrd = rounddown(hi_thrd, HCLGE_BUF_SIZE_UNIT);
lo_thrd = hi_thrd - aligned_mps / HCLGE_BUF_DIV_BY;
} else {
hi_thrd = aligned_mps + HCLGE_NON_DCB_ADDITIONAL_BUF;
lo_thrd = aligned_mps;
}
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
buf_alloc->s_buf.tc_thrd[i].low = lo_thrd;
buf_alloc->s_buf.tc_thrd[i].high = hi_thrd;
}
return true;
}
static int hclge_tx_buffer_calc(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
u32 i, total_size;
total_size = hdev->pkt_buf_size;
/* alloc tx buffer for all enabled tc */
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
if (hdev->hw_tc_map & BIT(i)) {
if (total_size < hdev->tx_buf_size)
return -ENOMEM;
priv->tx_buf_size = hdev->tx_buf_size;
} else {
priv->tx_buf_size = 0;
}
total_size -= priv->tx_buf_size;
}
return 0;
}
static bool hclge_rx_buf_calc_all(struct hclge_dev *hdev, bool max,
struct hclge_pkt_buf_alloc *buf_alloc)
{
u32 rx_all = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
u32 aligned_mps = round_up(hdev->mps, HCLGE_BUF_SIZE_UNIT);
unsigned int i;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
priv->enable = 0;
priv->wl.low = 0;
priv->wl.high = 0;
priv->buf_size = 0;
if (!(hdev->hw_tc_map & BIT(i)))
continue;
priv->enable = 1;
if (hdev->tm_info.hw_pfc_map & BIT(i)) {
priv->wl.low = max ? aligned_mps : HCLGE_BUF_SIZE_UNIT;
priv->wl.high = roundup(priv->wl.low + aligned_mps,
HCLGE_BUF_SIZE_UNIT);
} else {
priv->wl.low = 0;
priv->wl.high = max ? (aligned_mps * HCLGE_BUF_MUL_BY) :
aligned_mps;
}
priv->buf_size = priv->wl.high + hdev->dv_buf_size;
}
return hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all);
}
static bool hclge_drop_nopfc_buf_till_fit(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
u32 rx_all = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
int no_pfc_priv_num = hclge_get_no_pfc_priv_num(hdev, buf_alloc);
int i;
/* let the last to be cleared first */
for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) {
struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
unsigned int mask = BIT((unsigned int)i);
if (hdev->hw_tc_map & mask &&
!(hdev->tm_info.hw_pfc_map & mask)) {
/* Clear the no pfc TC private buffer */
priv->wl.low = 0;
priv->wl.high = 0;
priv->buf_size = 0;
priv->enable = 0;
no_pfc_priv_num--;
}
if (hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all) ||
no_pfc_priv_num == 0)
break;
}
return hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all);
}
static bool hclge_drop_pfc_buf_till_fit(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
u32 rx_all = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
int pfc_priv_num = hclge_get_pfc_priv_num(hdev, buf_alloc);
int i;
/* let the last to be cleared first */
for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) {
struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
unsigned int mask = BIT((unsigned int)i);
if (hdev->hw_tc_map & mask &&
hdev->tm_info.hw_pfc_map & mask) {
/* Reduce the number of pfc TC with private buffer */
priv->wl.low = 0;
priv->enable = 0;
priv->wl.high = 0;
priv->buf_size = 0;
pfc_priv_num--;
}
if (hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all) ||
pfc_priv_num == 0)
break;
}
return hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all);
}
static int hclge_only_alloc_priv_buff(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
#define COMPENSATE_BUFFER 0x3C00
#define COMPENSATE_HALF_MPS_NUM 5
#define PRIV_WL_GAP 0x1800
u32 rx_priv = hdev->pkt_buf_size - hclge_get_tx_buff_alloced(buf_alloc);
u32 tc_num = hclge_get_tc_num(hdev);
u32 half_mps = hdev->mps >> 1;
u32 min_rx_priv;
unsigned int i;
if (tc_num)
rx_priv = rx_priv / tc_num;
if (tc_num <= NEED_RESERVE_TC_NUM)
rx_priv = rx_priv * BUF_RESERVE_PERCENT / BUF_MAX_PERCENT;
min_rx_priv = hdev->dv_buf_size + COMPENSATE_BUFFER +
COMPENSATE_HALF_MPS_NUM * half_mps;
min_rx_priv = round_up(min_rx_priv, HCLGE_BUF_SIZE_UNIT);
rx_priv = round_down(rx_priv, HCLGE_BUF_SIZE_UNIT);
if (rx_priv < min_rx_priv)
return false;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
priv->enable = 0;
priv->wl.low = 0;
priv->wl.high = 0;
priv->buf_size = 0;
if (!(hdev->hw_tc_map & BIT(i)))
continue;
priv->enable = 1;
priv->buf_size = rx_priv;
priv->wl.high = rx_priv - hdev->dv_buf_size;
priv->wl.low = priv->wl.high - PRIV_WL_GAP;
}
buf_alloc->s_buf.buf_size = 0;
return true;
}
/* hclge_rx_buffer_calc: calculate the rx private buffer size for all TCs
* @hdev: pointer to struct hclge_dev
* @buf_alloc: pointer to buffer calculation data
* @return: 0: calculate successful, negative: fail
*/
static int hclge_rx_buffer_calc(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
/* When DCB is not supported, rx private buffer is not allocated. */
if (!hnae3_dev_dcb_supported(hdev)) {
u32 rx_all = hdev->pkt_buf_size;
rx_all -= hclge_get_tx_buff_alloced(buf_alloc);
if (!hclge_is_rx_buf_ok(hdev, buf_alloc, rx_all))
return -ENOMEM;
return 0;
}
if (hclge_only_alloc_priv_buff(hdev, buf_alloc))
return 0;
if (hclge_rx_buf_calc_all(hdev, true, buf_alloc))
return 0;
/* try to decrease the buffer size */
if (hclge_rx_buf_calc_all(hdev, false, buf_alloc))
return 0;
if (hclge_drop_nopfc_buf_till_fit(hdev, buf_alloc))
return 0;
if (hclge_drop_pfc_buf_till_fit(hdev, buf_alloc))
return 0;
return -ENOMEM;
}
static int hclge_rx_priv_buf_alloc(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_rx_priv_buff_cmd *req;
struct hclge_desc desc;
int ret;
int i;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RX_PRIV_BUFF_ALLOC, false);
req = (struct hclge_rx_priv_buff_cmd *)desc.data;
/* Alloc private buffer TCs */
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
struct hclge_priv_buf *priv = &buf_alloc->priv_buf[i];
req->buf_num[i] =
cpu_to_le16(priv->buf_size >> HCLGE_BUF_UNIT_S);
req->buf_num[i] |=
cpu_to_le16(1 << HCLGE_TC0_PRI_BUF_EN_B);
}
req->shared_buf =
cpu_to_le16((buf_alloc->s_buf.buf_size >> HCLGE_BUF_UNIT_S) |
(1 << HCLGE_TC0_PRI_BUF_EN_B));
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"rx private buffer alloc cmd failed %d\n", ret);
return ret;
}
static int hclge_rx_priv_wl_config(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_rx_priv_wl_buf *req;
struct hclge_priv_buf *priv;
struct hclge_desc desc[2];
int i, j;
int ret;
for (i = 0; i < 2; i++) {
hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_RX_PRIV_WL_ALLOC,
false);
req = (struct hclge_rx_priv_wl_buf *)desc[i].data;
/* The first descriptor set the NEXT bit to 1 */
if (i == 0)
desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
else
desc[i].flag &= ~cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
for (j = 0; j < HCLGE_TC_NUM_ONE_DESC; j++) {
u32 idx = i * HCLGE_TC_NUM_ONE_DESC + j;
priv = &buf_alloc->priv_buf[idx];
req->tc_wl[j].high =
cpu_to_le16(priv->wl.high >> HCLGE_BUF_UNIT_S);
req->tc_wl[j].high |=
cpu_to_le16(BIT(HCLGE_RX_PRIV_EN_B));
req->tc_wl[j].low =
cpu_to_le16(priv->wl.low >> HCLGE_BUF_UNIT_S);
req->tc_wl[j].low |=
cpu_to_le16(BIT(HCLGE_RX_PRIV_EN_B));
}
}
/* Send 2 descriptor at one time */
ret = hclge_cmd_send(&hdev->hw, desc, 2);
if (ret)
dev_err(&hdev->pdev->dev,
"rx private waterline config cmd failed %d\n",
ret);
return ret;
}
static int hclge_common_thrd_config(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_shared_buf *s_buf = &buf_alloc->s_buf;
struct hclge_rx_com_thrd *req;
struct hclge_desc desc[2];
struct hclge_tc_thrd *tc;
int i, j;
int ret;
for (i = 0; i < 2; i++) {
hclge_cmd_setup_basic_desc(&desc[i],
HCLGE_OPC_RX_COM_THRD_ALLOC, false);
req = (struct hclge_rx_com_thrd *)&desc[i].data;
/* The first descriptor set the NEXT bit to 1 */
if (i == 0)
desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
else
desc[i].flag &= ~cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
for (j = 0; j < HCLGE_TC_NUM_ONE_DESC; j++) {
tc = &s_buf->tc_thrd[i * HCLGE_TC_NUM_ONE_DESC + j];
req->com_thrd[j].high =
cpu_to_le16(tc->high >> HCLGE_BUF_UNIT_S);
req->com_thrd[j].high |=
cpu_to_le16(BIT(HCLGE_RX_PRIV_EN_B));
req->com_thrd[j].low =
cpu_to_le16(tc->low >> HCLGE_BUF_UNIT_S);
req->com_thrd[j].low |=
cpu_to_le16(BIT(HCLGE_RX_PRIV_EN_B));
}
}
/* Send 2 descriptors at one time */
ret = hclge_cmd_send(&hdev->hw, desc, 2);
if (ret)
dev_err(&hdev->pdev->dev,
"common threshold config cmd failed %d\n", ret);
return ret;
}
static int hclge_common_wl_config(struct hclge_dev *hdev,
struct hclge_pkt_buf_alloc *buf_alloc)
{
struct hclge_shared_buf *buf = &buf_alloc->s_buf;
struct hclge_rx_com_wl *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RX_COM_WL_ALLOC, false);
req = (struct hclge_rx_com_wl *)desc.data;
req->com_wl.high = cpu_to_le16(buf->self.high >> HCLGE_BUF_UNIT_S);
req->com_wl.high |= cpu_to_le16(BIT(HCLGE_RX_PRIV_EN_B));
req->com_wl.low = cpu_to_le16(buf->self.low >> HCLGE_BUF_UNIT_S);
req->com_wl.low |= cpu_to_le16(BIT(HCLGE_RX_PRIV_EN_B));
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"common waterline config cmd failed %d\n", ret);
return ret;
}
int hclge_buffer_alloc(struct hclge_dev *hdev)
{
struct hclge_pkt_buf_alloc *pkt_buf;
int ret;
pkt_buf = kzalloc(sizeof(*pkt_buf), GFP_KERNEL);
if (!pkt_buf)
return -ENOMEM;
ret = hclge_tx_buffer_calc(hdev, pkt_buf);
if (ret) {
dev_err(&hdev->pdev->dev,
"could not calc tx buffer size for all TCs %d\n", ret);
goto out;
}
ret = hclge_tx_buffer_alloc(hdev, pkt_buf);
if (ret) {
dev_err(&hdev->pdev->dev,
"could not alloc tx buffers %d\n", ret);
goto out;
}
ret = hclge_rx_buffer_calc(hdev, pkt_buf);
if (ret) {
dev_err(&hdev->pdev->dev,
"could not calc rx priv buffer size for all TCs %d\n",
ret);
goto out;
}
ret = hclge_rx_priv_buf_alloc(hdev, pkt_buf);
if (ret) {
dev_err(&hdev->pdev->dev, "could not alloc rx priv buffer %d\n",
ret);
goto out;
}
if (hnae3_dev_dcb_supported(hdev)) {
ret = hclge_rx_priv_wl_config(hdev, pkt_buf);
if (ret) {
dev_err(&hdev->pdev->dev,
"could not configure rx private waterline %d\n",
ret);
goto out;
}
ret = hclge_common_thrd_config(hdev, pkt_buf);
if (ret) {
dev_err(&hdev->pdev->dev,
"could not configure common threshold %d\n",
ret);
goto out;
}
}
ret = hclge_common_wl_config(hdev, pkt_buf);
if (ret)
dev_err(&hdev->pdev->dev,
"could not configure common waterline %d\n", ret);
out:
kfree(pkt_buf);
return ret;
}
static int hclge_init_roce_base_info(struct hclge_vport *vport)
{
struct hnae3_handle *roce = &vport->roce;
struct hnae3_handle *nic = &vport->nic;
struct hclge_dev *hdev = vport->back;
roce->rinfo.num_vectors = vport->back->num_roce_msi;
if (hdev->num_msi < hdev->num_nic_msi + hdev->num_roce_msi)
return -EINVAL;
roce->rinfo.base_vector = hdev->num_nic_msi;
roce->rinfo.netdev = nic->kinfo.netdev;
roce->rinfo.roce_io_base = hdev->hw.hw.io_base;
roce->rinfo.roce_mem_base = hdev->hw.hw.mem_base;
roce->pdev = nic->pdev;
roce->ae_algo = nic->ae_algo;
roce->numa_node_mask = nic->numa_node_mask;
return 0;
}
static int hclge_init_msi(struct hclge_dev *hdev)
{
struct pci_dev *pdev = hdev->pdev;
int vectors;
int i;
vectors = pci_alloc_irq_vectors(pdev, HNAE3_MIN_VECTOR_NUM,
hdev->num_msi,
PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (vectors < 0) {
dev_err(&pdev->dev,
"failed(%d) to allocate MSI/MSI-X vectors\n",
vectors);
return vectors;
}
if (vectors < hdev->num_msi)
dev_warn(&hdev->pdev->dev,
"requested %u MSI/MSI-X, but allocated %d MSI/MSI-X\n",
hdev->num_msi, vectors);
hdev->num_msi = vectors;
hdev->num_msi_left = vectors;
hdev->vector_status = devm_kcalloc(&pdev->dev, hdev->num_msi,
sizeof(u16), GFP_KERNEL);
if (!hdev->vector_status) {
pci_free_irq_vectors(pdev);
return -ENOMEM;
}
for (i = 0; i < hdev->num_msi; i++)
hdev->vector_status[i] = HCLGE_INVALID_VPORT;
hdev->vector_irq = devm_kcalloc(&pdev->dev, hdev->num_msi,
sizeof(int), GFP_KERNEL);
if (!hdev->vector_irq) {
pci_free_irq_vectors(pdev);
return -ENOMEM;
}
return 0;
}
static u8 hclge_check_speed_dup(u8 duplex, int speed)
{
if (!(speed == HCLGE_MAC_SPEED_10M || speed == HCLGE_MAC_SPEED_100M))
duplex = HCLGE_MAC_FULL;
return duplex;
}
static struct hclge_mac_speed_map hclge_mac_speed_map_to_fw[] = {
{HCLGE_MAC_SPEED_10M, HCLGE_FW_MAC_SPEED_10M},
{HCLGE_MAC_SPEED_100M, HCLGE_FW_MAC_SPEED_100M},
{HCLGE_MAC_SPEED_1G, HCLGE_FW_MAC_SPEED_1G},
{HCLGE_MAC_SPEED_10G, HCLGE_FW_MAC_SPEED_10G},
{HCLGE_MAC_SPEED_25G, HCLGE_FW_MAC_SPEED_25G},
{HCLGE_MAC_SPEED_40G, HCLGE_FW_MAC_SPEED_40G},
{HCLGE_MAC_SPEED_50G, HCLGE_FW_MAC_SPEED_50G},
{HCLGE_MAC_SPEED_100G, HCLGE_FW_MAC_SPEED_100G},
{HCLGE_MAC_SPEED_200G, HCLGE_FW_MAC_SPEED_200G},
};
static int hclge_convert_to_fw_speed(u32 speed_drv, u32 *speed_fw)
{
u16 i;
for (i = 0; i < ARRAY_SIZE(hclge_mac_speed_map_to_fw); i++) {
if (hclge_mac_speed_map_to_fw[i].speed_drv == speed_drv) {
*speed_fw = hclge_mac_speed_map_to_fw[i].speed_fw;
return 0;
}
}
return -EINVAL;
}
static int hclge_cfg_mac_speed_dup_hw(struct hclge_dev *hdev, int speed,
u8 duplex, u8 lane_num)
{
struct hclge_config_mac_speed_dup_cmd *req;
struct hclge_desc desc;
u32 speed_fw;
int ret;
req = (struct hclge_config_mac_speed_dup_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_SPEED_DUP, false);
if (duplex)
hnae3_set_bit(req->speed_dup, HCLGE_CFG_DUPLEX_B, 1);
ret = hclge_convert_to_fw_speed(speed, &speed_fw);
if (ret) {
dev_err(&hdev->pdev->dev, "invalid speed (%d)\n", speed);
return ret;
}
hnae3_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, HCLGE_CFG_SPEED_S,
speed_fw);
hnae3_set_bit(req->mac_change_fec_en, HCLGE_CFG_MAC_SPEED_CHANGE_EN_B,
1);
req->lane_num = lane_num;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"mac speed/duplex config cmd failed %d.\n", ret);
return ret;
}
return 0;
}
int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex, u8 lane_num)
{
struct hclge_mac *mac = &hdev->hw.mac;
int ret;
duplex = hclge_check_speed_dup(duplex, speed);
if (!mac->support_autoneg && mac->speed == speed &&
mac->duplex == duplex && (mac->lane_num == lane_num || lane_num == 0))
return 0;
ret = hclge_cfg_mac_speed_dup_hw(hdev, speed, duplex, lane_num);
if (ret)
return ret;
hdev->hw.mac.speed = speed;
hdev->hw.mac.duplex = duplex;
if (!lane_num)
hdev->hw.mac.lane_num = lane_num;
return 0;
}
static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed,
u8 duplex, u8 lane_num)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return hclge_cfg_mac_speed_dup(hdev, speed, duplex, lane_num);
}
static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable)
{
struct hclge_config_auto_neg_cmd *req;
struct hclge_desc desc;
u32 flag = 0;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_AN_MODE, false);
req = (struct hclge_config_auto_neg_cmd *)desc.data;
if (enable)
hnae3_set_bit(flag, HCLGE_MAC_CFG_AN_EN_B, 1U);
req->cfg_an_cmd_flag = cpu_to_le32(flag);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "auto neg set cmd failed %d.\n",
ret);
return ret;
}
static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
if (!hdev->hw.mac.support_autoneg) {
if (enable) {
dev_err(&hdev->pdev->dev,
"autoneg is not supported by current port\n");
return -EOPNOTSUPP;
} else {
return 0;
}
}
return hclge_set_autoneg_en(hdev, enable);
}
static int hclge_get_autoneg(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct phy_device *phydev = hdev->hw.mac.phydev;
if (phydev)
return phydev->autoneg;
return hdev->hw.mac.autoneg;
}
static int hclge_restart_autoneg(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int ret;
dev_dbg(&hdev->pdev->dev, "restart autoneg\n");
ret = hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
if (ret)
return ret;
return hclge_notify_client(hdev, HNAE3_UP_CLIENT);
}
static int hclge_halt_autoneg(struct hnae3_handle *handle, bool halt)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
if (hdev->hw.mac.support_autoneg && hdev->hw.mac.autoneg)
return hclge_set_autoneg_en(hdev, !halt);
return 0;
}
static void hclge_parse_fec_stats_lanes(struct hclge_dev *hdev,
struct hclge_desc *desc, u32 desc_len)
{
u32 lane_size = HCLGE_FEC_STATS_MAX_LANES * 2;
u32 desc_index = 0;
u32 data_index = 0;
u32 i;
for (i = 0; i < lane_size; i++) {
if (data_index >= HCLGE_DESC_DATA_LEN) {
desc_index++;
data_index = 0;
}
if (desc_index >= desc_len)
return;
hdev->fec_stats.per_lanes[i] +=
le32_to_cpu(desc[desc_index].data[data_index]);
data_index++;
}
}
static void hclge_parse_fec_stats(struct hclge_dev *hdev,
struct hclge_desc *desc, u32 desc_len)
{
struct hclge_query_fec_stats_cmd *req;
req = (struct hclge_query_fec_stats_cmd *)desc[0].data;
hdev->fec_stats.base_r_lane_num = req->base_r_lane_num;
hdev->fec_stats.rs_corr_blocks +=
le32_to_cpu(req->rs_fec_corr_blocks);
hdev->fec_stats.rs_uncorr_blocks +=
le32_to_cpu(req->rs_fec_uncorr_blocks);
hdev->fec_stats.rs_error_blocks +=
le32_to_cpu(req->rs_fec_error_blocks);
hdev->fec_stats.base_r_corr_blocks +=
le32_to_cpu(req->base_r_fec_corr_blocks);
hdev->fec_stats.base_r_uncorr_blocks +=
le32_to_cpu(req->base_r_fec_uncorr_blocks);
hclge_parse_fec_stats_lanes(hdev, &desc[1], desc_len - 1);
}
static int hclge_update_fec_stats_hw(struct hclge_dev *hdev)
{
struct hclge_desc desc[HCLGE_FEC_STATS_CMD_NUM];
int ret;
u32 i;
for (i = 0; i < HCLGE_FEC_STATS_CMD_NUM; i++) {
hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_QUERY_FEC_STATS,
true);
if (i != (HCLGE_FEC_STATS_CMD_NUM - 1))
desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
}
ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_FEC_STATS_CMD_NUM);
if (ret)
return ret;
hclge_parse_fec_stats(hdev, desc, HCLGE_FEC_STATS_CMD_NUM);
return 0;
}
static void hclge_update_fec_stats(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
int ret;
if (!hnae3_ae_dev_fec_stats_supported(ae_dev) ||
test_and_set_bit(HCLGE_STATE_FEC_STATS_UPDATING, &hdev->state))
return;
ret = hclge_update_fec_stats_hw(hdev);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to update fec stats, ret = %d\n", ret);
clear_bit(HCLGE_STATE_FEC_STATS_UPDATING, &hdev->state);
}
static void hclge_get_fec_stats_total(struct hclge_dev *hdev,
struct ethtool_fec_stats *fec_stats)
{
fec_stats->corrected_blocks.total = hdev->fec_stats.rs_corr_blocks;
fec_stats->uncorrectable_blocks.total =
hdev->fec_stats.rs_uncorr_blocks;
}
static void hclge_get_fec_stats_lanes(struct hclge_dev *hdev,
struct ethtool_fec_stats *fec_stats)
{
u32 i;
if (hdev->fec_stats.base_r_lane_num == 0 ||
hdev->fec_stats.base_r_lane_num > HCLGE_FEC_STATS_MAX_LANES) {
dev_err(&hdev->pdev->dev,
"fec stats lane number(%llu) is invalid\n",
hdev->fec_stats.base_r_lane_num);
return;
}
for (i = 0; i < hdev->fec_stats.base_r_lane_num; i++) {
fec_stats->corrected_blocks.lanes[i] =
hdev->fec_stats.base_r_corr_per_lanes[i];
fec_stats->uncorrectable_blocks.lanes[i] =
hdev->fec_stats.base_r_uncorr_per_lanes[i];
}
}
static void hclge_comm_get_fec_stats(struct hclge_dev *hdev,
struct ethtool_fec_stats *fec_stats)
{
u32 fec_mode = hdev->hw.mac.fec_mode;
switch (fec_mode) {
case BIT(HNAE3_FEC_RS):
case BIT(HNAE3_FEC_LLRS):
hclge_get_fec_stats_total(hdev, fec_stats);
break;
case BIT(HNAE3_FEC_BASER):
hclge_get_fec_stats_lanes(hdev, fec_stats);
break;
default:
dev_err(&hdev->pdev->dev,
"fec stats is not supported by current fec mode(0x%x)\n",
fec_mode);
break;
}
}
static void hclge_get_fec_stats(struct hnae3_handle *handle,
struct ethtool_fec_stats *fec_stats)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u32 fec_mode = hdev->hw.mac.fec_mode;
if (fec_mode == BIT(HNAE3_FEC_NONE) ||
fec_mode == BIT(HNAE3_FEC_AUTO) ||
fec_mode == BIT(HNAE3_FEC_USER_DEF))
return;
hclge_update_fec_stats(hdev);
hclge_comm_get_fec_stats(hdev, fec_stats);
}
static int hclge_set_fec_hw(struct hclge_dev *hdev, u32 fec_mode)
{
struct hclge_config_fec_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_FEC_MODE, false);
req = (struct hclge_config_fec_cmd *)desc.data;
if (fec_mode & BIT(HNAE3_FEC_AUTO))
hnae3_set_bit(req->fec_mode, HCLGE_MAC_CFG_FEC_AUTO_EN_B, 1);
if (fec_mode & BIT(HNAE3_FEC_RS))
hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M,
HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_RS);
if (fec_mode & BIT(HNAE3_FEC_LLRS))
hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M,
HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_LLRS);
if (fec_mode & BIT(HNAE3_FEC_BASER))
hnae3_set_field(req->fec_mode, HCLGE_MAC_CFG_FEC_MODE_M,
HCLGE_MAC_CFG_FEC_MODE_S, HCLGE_MAC_FEC_BASER);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "set fec mode failed %d.\n", ret);
return ret;
}
static int hclge_set_fec(struct hnae3_handle *handle, u32 fec_mode)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_mac *mac = &hdev->hw.mac;
int ret;
if (fec_mode && !(mac->fec_ability & fec_mode)) {
dev_err(&hdev->pdev->dev, "unsupported fec mode\n");
return -EINVAL;
}
ret = hclge_set_fec_hw(hdev, fec_mode);
if (ret)
return ret;
mac->user_fec_mode = fec_mode | BIT(HNAE3_FEC_USER_DEF);
return 0;
}
static void hclge_get_fec(struct hnae3_handle *handle, u8 *fec_ability,
u8 *fec_mode)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_mac *mac = &hdev->hw.mac;
if (fec_ability)
*fec_ability = mac->fec_ability;
if (fec_mode)
*fec_mode = mac->fec_mode;
}
static int hclge_mac_init(struct hclge_dev *hdev)
{
struct hclge_mac *mac = &hdev->hw.mac;
int ret;
hdev->support_sfp_query = true;
if (!test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
hdev->hw.mac.duplex = HCLGE_MAC_FULL;
ret = hclge_cfg_mac_speed_dup_hw(hdev, hdev->hw.mac.speed,
hdev->hw.mac.duplex, hdev->hw.mac.lane_num);
if (ret)
return ret;
if (hdev->hw.mac.support_autoneg) {
ret = hclge_set_autoneg_en(hdev, hdev->hw.mac.autoneg);
if (ret)
return ret;
}
mac->link = 0;
if (mac->user_fec_mode & BIT(HNAE3_FEC_USER_DEF)) {
ret = hclge_set_fec_hw(hdev, mac->user_fec_mode);
if (ret)
return ret;
}
ret = hclge_set_mac_mtu(hdev, hdev->mps);
if (ret) {
dev_err(&hdev->pdev->dev, "set mtu failed ret=%d\n", ret);
return ret;
}
ret = hclge_set_default_loopback(hdev);
if (ret)
return ret;
ret = hclge_buffer_alloc(hdev);
if (ret)
dev_err(&hdev->pdev->dev,
"allocate buffer fail, ret=%d\n", ret);
return ret;
}
static void hclge_mbx_task_schedule(struct hclge_dev *hdev)
{
if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) &&
!test_and_set_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state)) {
hdev->last_mbx_scheduled = jiffies;
mod_delayed_work(hclge_wq, &hdev->service_task, 0);
}
}
static void hclge_reset_task_schedule(struct hclge_dev *hdev)
{
if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) &&
net: hns3: ignore reset event before initialization process is done Currently, if there is a reset event triggered by RAS during device in initialization process, driver may run reset process concurrently with initialization process. In this case, it may cause problem. For example, the RSS indirection table may has not been alloc memory in initialization process yet, but it is used in reset process, it will cause a call trace like this: [61228.744836] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 ... [61228.897677] Workqueue: hclgevf hclgevf_service_task [hclgevf] [61228.911390] pstate: 40400009 (nZcv daif +PAN -UAO -TCO BTYPE=--) [61228.918670] pc : hclgevf_set_rss_indir_table+0xb4/0x190 [hclgevf] [61228.927812] lr : hclgevf_set_rss_indir_table+0x90/0x190 [hclgevf] [61228.937248] sp : ffff8000162ebb50 [61228.941087] x29: ffff8000162ebb50 x28: ffffb77add72dbc0 x27: ffff0820c7dc8080 [61228.949516] x26: 0000000000000000 x25: ffff0820ad4fc880 x24: ffff0820c7dc8080 [61228.958220] x23: ffff0820c7dc8090 x22: 00000000ffffffff x21: 0000000000000040 [61228.966360] x20: ffffb77add72b9c0 x19: 0000000000000000 x18: 0000000000000030 [61228.974646] x17: 0000000000000000 x16: ffffb77ae713feb0 x15: ffff0820ad4fcce8 [61228.982808] x14: ffffffffffffffff x13: ffff8000962eb7f7 x12: 00003834ec70c960 [61228.991990] x11: 00e0fafa8c206982 x10: 9670facc78a8f9a8 x9 : ffffb77add717530 [61229.001123] x8 : ffff0820ad4fd6b8 x7 : 0000000000000000 x6 : 0000000000000011 [61229.010249] x5 : 00000000000cb1b0 x4 : 0000000000002adb x3 : 0000000000000049 [61229.018662] x2 : ffff8000162ebbb8 x1 : 0000000000000000 x0 : 0000000000000480 [61229.027002] Call trace: [61229.030177] hclgevf_set_rss_indir_table+0xb4/0x190 [hclgevf] [61229.039009] hclgevf_rss_init_hw+0x128/0x1b4 [hclgevf] [61229.046809] hclgevf_reset_rebuild+0x17c/0x69c [hclgevf] [61229.053862] hclgevf_reset_service_task+0x4cc/0xa80 [hclgevf] [61229.061306] hclgevf_service_task+0x6c/0x630 [hclgevf] [61229.068491] process_one_work+0x1dc/0x48c [61229.074121] worker_thread+0x15c/0x464 [61229.078562] kthread+0x168/0x16c [61229.082873] ret_from_fork+0x10/0x18 [61229.088221] Code: 7900e7f6 f904a683 d503201f 9101a3e2 (38616b43) [61229.095357] ---[ end trace 153661a538f6768c ]--- To fix this problem, don't schedule reset task before initialization process is done. Signed-off-by: Guangbin Huang <huangguangbin2@huawei.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2021-10-27 12:11:45 +00:00
test_bit(HCLGE_STATE_SERVICE_INITED, &hdev->state) &&
!test_and_set_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state)) {
hdev->last_rst_scheduled = jiffies;
mod_delayed_work(hclge_wq, &hdev->service_task, 0);
}
}
static void hclge_errhand_task_schedule(struct hclge_dev *hdev)
{
if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) &&
!test_and_set_bit(HCLGE_STATE_ERR_SERVICE_SCHED, &hdev->state))
mod_delayed_work(hclge_wq, &hdev->service_task, 0);
}
void hclge_task_schedule(struct hclge_dev *hdev, unsigned long delay_time)
{
if (!test_bit(HCLGE_STATE_REMOVING, &hdev->state) &&
!test_bit(HCLGE_STATE_RST_FAIL, &hdev->state))
mod_delayed_work(hclge_wq, &hdev->service_task, delay_time);
}
static int hclge_get_mac_link_status(struct hclge_dev *hdev, int *link_status)
{
struct hclge_link_status_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_LINK_STATUS, true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev, "get link status cmd failed %d\n",
ret);
return ret;
}
req = (struct hclge_link_status_cmd *)desc.data;
*link_status = (req->status & HCLGE_LINK_STATUS_UP_M) > 0 ?
HCLGE_LINK_STATUS_UP : HCLGE_LINK_STATUS_DOWN;
return 0;
}
static int hclge_get_mac_phy_link(struct hclge_dev *hdev, int *link_status)
{
struct phy_device *phydev = hdev->hw.mac.phydev;
*link_status = HCLGE_LINK_STATUS_DOWN;
if (test_bit(HCLGE_STATE_DOWN, &hdev->state))
return 0;
if (phydev && (phydev->state != PHY_RUNNING || !phydev->link))
return 0;
return hclge_get_mac_link_status(hdev, link_status);
}
static void hclge_push_link_status(struct hclge_dev *hdev)
{
struct hclge_vport *vport;
int ret;
u16 i;
for (i = 0; i < pci_num_vf(hdev->pdev); i++) {
vport = &hdev->vport[i + HCLGE_VF_VPORT_START_NUM];
if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state) ||
vport->vf_info.link_state != IFLA_VF_LINK_STATE_AUTO)
continue;
ret = hclge_push_vf_link_status(vport);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to push link status to vf%u, ret = %d\n",
i, ret);
}
}
}
static void hclge_update_link_status(struct hclge_dev *hdev)
{
struct hnae3_handle *rhandle = &hdev->vport[0].roce;
struct hnae3_handle *handle = &hdev->vport[0].nic;
struct hnae3_client *rclient = hdev->roce_client;
struct hnae3_client *client = hdev->nic_client;
int state;
int ret;
if (!client)
return;
if (test_and_set_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state))
return;
ret = hclge_get_mac_phy_link(hdev, &state);
if (ret) {
clear_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state);
return;
}
if (state != hdev->hw.mac.link) {
hdev->hw.mac.link = state;
if (state == HCLGE_LINK_STATUS_UP)
hclge_update_port_info(hdev);
client->ops->link_status_change(handle, state);
hclge_config_mac_tnl_int(hdev, state);
if (rclient && rclient->ops->link_status_change)
rclient->ops->link_status_change(rhandle, state);
hclge_push_link_status(hdev);
}
clear_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state);
}
static void hclge_update_speed_advertising(struct hclge_mac *mac)
{
u32 speed_ability;
if (hclge_get_speed_bit(mac->speed, &speed_ability))
return;
switch (mac->module_type) {
case HNAE3_MODULE_TYPE_FIBRE_LR:
hclge_convert_setting_lr(speed_ability, mac->advertising);
break;
case HNAE3_MODULE_TYPE_FIBRE_SR:
case HNAE3_MODULE_TYPE_AOC:
hclge_convert_setting_sr(speed_ability, mac->advertising);
break;
case HNAE3_MODULE_TYPE_CR:
hclge_convert_setting_cr(speed_ability, mac->advertising);
break;
case HNAE3_MODULE_TYPE_KR:
hclge_convert_setting_kr(speed_ability, mac->advertising);
break;
default:
break;
}
}
static void hclge_update_fec_advertising(struct hclge_mac *mac)
{
if (mac->fec_mode & BIT(HNAE3_FEC_RS))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT,
mac->advertising);
else if (mac->fec_mode & BIT(HNAE3_FEC_LLRS))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
mac->advertising);
else if (mac->fec_mode & BIT(HNAE3_FEC_BASER))
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT,
mac->advertising);
else
linkmode_set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT,
mac->advertising);
}
static void hclge_update_pause_advertising(struct hclge_dev *hdev)
{
struct hclge_mac *mac = &hdev->hw.mac;
bool rx_en, tx_en;
switch (hdev->fc_mode_last_time) {
case HCLGE_FC_RX_PAUSE:
rx_en = true;
tx_en = false;
break;
case HCLGE_FC_TX_PAUSE:
rx_en = false;
tx_en = true;
break;
case HCLGE_FC_FULL:
rx_en = true;
tx_en = true;
break;
default:
rx_en = false;
tx_en = false;
break;
}
linkmode_set_pause(mac->advertising, tx_en, rx_en);
}
static void hclge_update_advertising(struct hclge_dev *hdev)
{
struct hclge_mac *mac = &hdev->hw.mac;
linkmode_zero(mac->advertising);
hclge_update_speed_advertising(mac);
hclge_update_fec_advertising(mac);
hclge_update_pause_advertising(hdev);
}
static void hclge_update_port_capability(struct hclge_dev *hdev,
struct hclge_mac *mac)
{
if (hnae3_dev_fec_supported(hdev))
hclge_convert_setting_fec(mac);
/* firmware can not identify back plane type, the media type
* read from configuration can help deal it
*/
if (mac->media_type == HNAE3_MEDIA_TYPE_BACKPLANE &&
mac->module_type == HNAE3_MODULE_TYPE_UNKNOWN)
mac->module_type = HNAE3_MODULE_TYPE_KR;
else if (mac->media_type == HNAE3_MEDIA_TYPE_COPPER)
mac->module_type = HNAE3_MODULE_TYPE_TP;
if (mac->support_autoneg) {
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, mac->supported);
linkmode_copy(mac->advertising, mac->supported);
} else {
linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
mac->supported);
hclge_update_advertising(hdev);
}
}
static int hclge_get_sfp_speed(struct hclge_dev *hdev, u32 *speed)
{
struct hclge_sfp_info_cmd *resp;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GET_SFP_INFO, true);
resp = (struct hclge_sfp_info_cmd *)desc.data;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret == -EOPNOTSUPP) {
dev_warn(&hdev->pdev->dev,
"IMP do not support get SFP speed %d\n", ret);
return ret;
} else if (ret) {
dev_err(&hdev->pdev->dev, "get sfp speed failed %d\n", ret);
return ret;
}
*speed = le32_to_cpu(resp->speed);
return 0;
}
static int hclge_get_sfp_info(struct hclge_dev *hdev, struct hclge_mac *mac)
{
struct hclge_sfp_info_cmd *resp;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GET_SFP_INFO, true);
resp = (struct hclge_sfp_info_cmd *)desc.data;
resp->query_type = QUERY_ACTIVE_SPEED;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret == -EOPNOTSUPP) {
dev_warn(&hdev->pdev->dev,
"IMP does not support get SFP info %d\n", ret);
return ret;
} else if (ret) {
dev_err(&hdev->pdev->dev, "get sfp info failed %d\n", ret);
return ret;
}
/* In some case, mac speed get from IMP may be 0, it shouldn't be
* set to mac->speed.
*/
if (!le32_to_cpu(resp->speed))
return 0;
mac->speed = le32_to_cpu(resp->speed);
/* if resp->speed_ability is 0, it means it's an old version
* firmware, do not update these params
*/
if (resp->speed_ability) {
mac->module_type = le32_to_cpu(resp->module_type);
mac->speed_ability = le32_to_cpu(resp->speed_ability);
mac->autoneg = resp->autoneg;
mac->support_autoneg = resp->autoneg_ability;
mac->speed_type = QUERY_ACTIVE_SPEED;
mac->lane_num = resp->lane_num;
if (!resp->active_fec)
mac->fec_mode = 0;
else
mac->fec_mode = BIT(resp->active_fec);
mac->fec_ability = resp->fec_ability;
} else {
mac->speed_type = QUERY_SFP_SPEED;
}
return 0;
}
static int hclge_get_phy_link_ksettings(struct hnae3_handle *handle,
struct ethtool_link_ksettings *cmd)
{
struct hclge_desc desc[HCLGE_PHY_LINK_SETTING_BD_NUM];
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_phy_link_ksetting_0_cmd *req0;
struct hclge_phy_link_ksetting_1_cmd *req1;
u32 supported, advertising, lp_advertising;
struct hclge_dev *hdev = vport->back;
int ret;
hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_PHY_LINK_KSETTING,
true);
desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_PHY_LINK_KSETTING,
true);
ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_PHY_LINK_SETTING_BD_NUM);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to get phy link ksetting, ret = %d.\n", ret);
return ret;
}
req0 = (struct hclge_phy_link_ksetting_0_cmd *)desc[0].data;
cmd->base.autoneg = req0->autoneg;
cmd->base.speed = le32_to_cpu(req0->speed);
cmd->base.duplex = req0->duplex;
cmd->base.port = req0->port;
cmd->base.transceiver = req0->transceiver;
cmd->base.phy_address = req0->phy_address;
cmd->base.eth_tp_mdix = req0->eth_tp_mdix;
cmd->base.eth_tp_mdix_ctrl = req0->eth_tp_mdix_ctrl;
supported = le32_to_cpu(req0->supported);
advertising = le32_to_cpu(req0->advertising);
lp_advertising = le32_to_cpu(req0->lp_advertising);
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
supported);
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
advertising);
ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
lp_advertising);
req1 = (struct hclge_phy_link_ksetting_1_cmd *)desc[1].data;
cmd->base.master_slave_cfg = req1->master_slave_cfg;
cmd->base.master_slave_state = req1->master_slave_state;
return 0;
}
static int
hclge_set_phy_link_ksettings(struct hnae3_handle *handle,
const struct ethtool_link_ksettings *cmd)
{
struct hclge_desc desc[HCLGE_PHY_LINK_SETTING_BD_NUM];
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_phy_link_ksetting_0_cmd *req0;
struct hclge_phy_link_ksetting_1_cmd *req1;
struct hclge_dev *hdev = vport->back;
u32 advertising;
int ret;
if (cmd->base.autoneg == AUTONEG_DISABLE &&
((cmd->base.speed != SPEED_100 && cmd->base.speed != SPEED_10) ||
(cmd->base.duplex != DUPLEX_HALF &&
cmd->base.duplex != DUPLEX_FULL)))
return -EINVAL;
hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_PHY_LINK_KSETTING,
false);
desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_PHY_LINK_KSETTING,
false);
req0 = (struct hclge_phy_link_ksetting_0_cmd *)desc[0].data;
req0->autoneg = cmd->base.autoneg;
req0->speed = cpu_to_le32(cmd->base.speed);
req0->duplex = cmd->base.duplex;
ethtool_convert_link_mode_to_legacy_u32(&advertising,
cmd->link_modes.advertising);
req0->advertising = cpu_to_le32(advertising);
req0->eth_tp_mdix_ctrl = cmd->base.eth_tp_mdix_ctrl;
req1 = (struct hclge_phy_link_ksetting_1_cmd *)desc[1].data;
req1->master_slave_cfg = cmd->base.master_slave_cfg;
ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_PHY_LINK_SETTING_BD_NUM);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to set phy link ksettings, ret = %d.\n", ret);
return ret;
}
hdev->hw.mac.autoneg = cmd->base.autoneg;
hdev->hw.mac.speed = cmd->base.speed;
hdev->hw.mac.duplex = cmd->base.duplex;
linkmode_copy(hdev->hw.mac.advertising, cmd->link_modes.advertising);
return 0;
}
static int hclge_update_tp_port_info(struct hclge_dev *hdev)
{
struct ethtool_link_ksettings cmd;
int ret;
if (!hnae3_dev_phy_imp_supported(hdev))
return 0;
ret = hclge_get_phy_link_ksettings(&hdev->vport->nic, &cmd);
if (ret)
return ret;
hdev->hw.mac.autoneg = cmd.base.autoneg;
hdev->hw.mac.speed = cmd.base.speed;
hdev->hw.mac.duplex = cmd.base.duplex;
linkmode_copy(hdev->hw.mac.advertising, cmd.link_modes.advertising);
return 0;
}
static int hclge_tp_port_init(struct hclge_dev *hdev)
{
struct ethtool_link_ksettings cmd;
if (!hnae3_dev_phy_imp_supported(hdev))
return 0;
cmd.base.autoneg = hdev->hw.mac.autoneg;
cmd.base.speed = hdev->hw.mac.speed;
cmd.base.duplex = hdev->hw.mac.duplex;
linkmode_copy(cmd.link_modes.advertising, hdev->hw.mac.advertising);
return hclge_set_phy_link_ksettings(&hdev->vport->nic, &cmd);
}
static int hclge_update_port_info(struct hclge_dev *hdev)
{
struct hclge_mac *mac = &hdev->hw.mac;
int speed;
int ret;
/* get the port info from SFP cmd if not copper port */
if (mac->media_type == HNAE3_MEDIA_TYPE_COPPER)
return hclge_update_tp_port_info(hdev);
/* if IMP does not support get SFP/qSFP info, return directly */
if (!hdev->support_sfp_query)
return 0;
if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2) {
speed = mac->speed;
ret = hclge_get_sfp_info(hdev, mac);
} else {
speed = HCLGE_MAC_SPEED_UNKNOWN;
ret = hclge_get_sfp_speed(hdev, &speed);
}
if (ret == -EOPNOTSUPP) {
hdev->support_sfp_query = false;
return ret;
} else if (ret) {
return ret;
}
if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2) {
if (mac->speed_type == QUERY_ACTIVE_SPEED) {
hclge_update_port_capability(hdev, mac);
if (mac->speed != speed)
(void)hclge_tm_port_shaper_cfg(hdev);
return 0;
}
return hclge_cfg_mac_speed_dup(hdev, mac->speed,
HCLGE_MAC_FULL, mac->lane_num);
} else {
if (speed == HCLGE_MAC_SPEED_UNKNOWN)
return 0; /* do nothing if no SFP */
/* must config full duplex for SFP */
return hclge_cfg_mac_speed_dup(hdev, speed, HCLGE_MAC_FULL, 0);
}
}
static int hclge_get_status(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
hclge_update_link_status(hdev);
return hdev->hw.mac.link;
}
struct hclge_vport *hclge_get_vf_vport(struct hclge_dev *hdev, int vf)
{
if (!pci_num_vf(hdev->pdev)) {
dev_err(&hdev->pdev->dev,
"SRIOV is disabled, can not get vport(%d) info.\n", vf);
return NULL;
}
if (vf < 0 || vf >= pci_num_vf(hdev->pdev)) {
dev_err(&hdev->pdev->dev,
"vf id(%d) is out of range(0 <= vfid < %d)\n",
vf, pci_num_vf(hdev->pdev));
return NULL;
}
/* VF start from 1 in vport */
vf += HCLGE_VF_VPORT_START_NUM;
return &hdev->vport[vf];
}
static int hclge_get_vf_config(struct hnae3_handle *handle, int vf,
struct ifla_vf_info *ivf)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
ivf->vf = vf;
ivf->linkstate = vport->vf_info.link_state;
ivf->spoofchk = vport->vf_info.spoofchk;
ivf->trusted = vport->vf_info.trusted;
ivf->min_tx_rate = 0;
ivf->max_tx_rate = vport->vf_info.max_tx_rate;
ivf->vlan = vport->port_base_vlan_cfg.vlan_info.vlan_tag;
ivf->vlan_proto = htons(vport->port_base_vlan_cfg.vlan_info.vlan_proto);
ivf->qos = vport->port_base_vlan_cfg.vlan_info.qos;
ether_addr_copy(ivf->mac, vport->vf_info.mac);
return 0;
}
static int hclge_set_vf_link_state(struct hnae3_handle *handle, int vf,
int link_state)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int link_state_old;
int ret;
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
link_state_old = vport->vf_info.link_state;
vport->vf_info.link_state = link_state;
/* return success directly if the VF is unalive, VF will
* query link state itself when it starts work.
*/
if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
return 0;
ret = hclge_push_vf_link_status(vport);
if (ret) {
vport->vf_info.link_state = link_state_old;
dev_err(&hdev->pdev->dev,
"failed to push vf%d link status, ret = %d\n", vf, ret);
}
return ret;
}
static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval)
{
u32 cmdq_src_reg, msix_src_reg, hw_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);
/* Assumption: If by any chance reset and mailbox events are reported
* together then we will only process reset event in this go and will
* defer the processing of the mailbox events. Since, we would have not
* cleared RX CMDQ event this time we would receive again another
* interrupt from H/W just for the mailbox.
*
* check for vector0 reset event sources
*/
if (BIT(HCLGE_VECTOR0_IMPRESET_INT_B) & msix_src_reg) {
dev_info(&hdev->pdev->dev, "IMP reset interrupt\n");
set_bit(HNAE3_IMP_RESET, &hdev->reset_pending);
set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
*clearval = BIT(HCLGE_VECTOR0_IMPRESET_INT_B);
hdev->rst_stats.imp_rst_cnt++;
return HCLGE_VECTOR0_EVENT_RST;
}
if (BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B) & msix_src_reg) {
dev_info(&hdev->pdev->dev, "global reset interrupt\n");
set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
set_bit(HNAE3_GLOBAL_RESET, &hdev->reset_pending);
*clearval = BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B);
hdev->rst_stats.global_rst_cnt++;
return HCLGE_VECTOR0_EVENT_RST;
}
/* 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)
return HCLGE_VECTOR0_EVENT_ERR;
/* check for vector0 ptp event source */
if (BIT(HCLGE_VECTOR0_REG_PTP_INT_B) & msix_src_reg) {
*clearval = msix_src_reg;
return HCLGE_VECTOR0_EVENT_PTP;
}
/* check for vector0 mailbox(=CMDQ RX) event source */
if (BIT(HCLGE_VECTOR0_RX_CMDQ_INT_B) & cmdq_src_reg) {
cmdq_src_reg &= ~BIT(HCLGE_VECTOR0_RX_CMDQ_INT_B);
*clearval = cmdq_src_reg;
return HCLGE_VECTOR0_EVENT_MBX;
}
/* 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);
return HCLGE_VECTOR0_EVENT_OTHER;
}
static void hclge_clear_event_cause(struct hclge_dev *hdev, u32 event_type,
u32 regclr)
{
#define HCLGE_IMP_RESET_DELAY 5
switch (event_type) {
case HCLGE_VECTOR0_EVENT_PTP:
case HCLGE_VECTOR0_EVENT_RST:
if (regclr == BIT(HCLGE_VECTOR0_IMPRESET_INT_B))
mdelay(HCLGE_IMP_RESET_DELAY);
hclge_write_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG, regclr);
break;
case HCLGE_VECTOR0_EVENT_MBX:
hclge_write_dev(&hdev->hw, HCLGE_VECTOR0_CMDQ_SRC_REG, regclr);
break;
default:
break;
}
}
static void hclge_clear_all_event_cause(struct hclge_dev *hdev)
{
hclge_clear_event_cause(hdev, HCLGE_VECTOR0_EVENT_RST,
BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B) |
BIT(HCLGE_VECTOR0_CORERESET_INT_B) |
BIT(HCLGE_VECTOR0_IMPRESET_INT_B));
hclge_clear_event_cause(hdev, HCLGE_VECTOR0_EVENT_MBX, 0);
}
static void hclge_enable_vector(struct hclge_misc_vector *vector, bool enable)
{
writel(enable ? 1 : 0, vector->addr);
}
static irqreturn_t hclge_misc_irq_handle(int irq, void *data)
{
struct hclge_dev *hdev = data;
unsigned long flags;
u32 clearval = 0;
u32 event_cause;
hclge_enable_vector(&hdev->misc_vector, false);
event_cause = hclge_check_event_cause(hdev, &clearval);
/* vector 0 interrupt is shared with reset and mailbox source events. */
switch (event_cause) {
case HCLGE_VECTOR0_EVENT_ERR:
hclge_errhand_task_schedule(hdev);
break;
case HCLGE_VECTOR0_EVENT_RST:
hclge_reset_task_schedule(hdev);
break;
case HCLGE_VECTOR0_EVENT_PTP:
spin_lock_irqsave(&hdev->ptp->lock, flags);
hclge_ptp_clean_tx_hwts(hdev);
spin_unlock_irqrestore(&hdev->ptp->lock, flags);
break;
case HCLGE_VECTOR0_EVENT_MBX:
/* If we are here then,
* 1. Either we are not handling any mbx task and we are not
* scheduled as well
* OR
* 2. We could be handling a mbx task but nothing more is
* scheduled.
* In both cases, we should schedule mbx task as there are more
* mbx messages reported by this interrupt.
*/
hclge_mbx_task_schedule(hdev);
break;
default:
dev_warn(&hdev->pdev->dev,
"received unknown or unhandled event of vector0\n");
break;
}
hclge_clear_event_cause(hdev, event_cause, clearval);
/* Enable interrupt if it is not caused by reset event or error event */
if (event_cause == HCLGE_VECTOR0_EVENT_PTP ||
event_cause == HCLGE_VECTOR0_EVENT_MBX ||
event_cause == HCLGE_VECTOR0_EVENT_OTHER)
hclge_enable_vector(&hdev->misc_vector, true);
return IRQ_HANDLED;
}
static void hclge_free_vector(struct hclge_dev *hdev, int vector_id)
{
if (hdev->vector_status[vector_id] == HCLGE_INVALID_VPORT) {
dev_warn(&hdev->pdev->dev,
"vector(vector_id %d) has been freed.\n", vector_id);
return;
}
hdev->vector_status[vector_id] = HCLGE_INVALID_VPORT;
hdev->num_msi_left += 1;
hdev->num_msi_used -= 1;
}
static void hclge_get_misc_vector(struct hclge_dev *hdev)
{
struct hclge_misc_vector *vector = &hdev->misc_vector;
vector->vector_irq = pci_irq_vector(hdev->pdev, 0);
vector->addr = hdev->hw.hw.io_base + HCLGE_MISC_VECTOR_REG_BASE;
hdev->vector_status[0] = 0;
hdev->num_msi_left -= 1;
hdev->num_msi_used += 1;
}
static int hclge_misc_irq_init(struct hclge_dev *hdev)
{
int ret;
hclge_get_misc_vector(hdev);
/* this would be explicitly freed in the end */
snprintf(hdev->misc_vector.name, HNAE3_INT_NAME_LEN, "%s-misc-%s",
HCLGE_NAME, pci_name(hdev->pdev));
ret = request_irq(hdev->misc_vector.vector_irq, hclge_misc_irq_handle,
0, hdev->misc_vector.name, hdev);
if (ret) {
hclge_free_vector(hdev, 0);
dev_err(&hdev->pdev->dev, "request misc irq(%d) fail\n",
hdev->misc_vector.vector_irq);
}
return ret;
}
static void hclge_misc_irq_uninit(struct hclge_dev *hdev)
{
free_irq(hdev->misc_vector.vector_irq, hdev);
hclge_free_vector(hdev, 0);
}
int hclge_notify_client(struct hclge_dev *hdev,
enum hnae3_reset_notify_type type)
{
struct hnae3_handle *handle = &hdev->vport[0].nic;
struct hnae3_client *client = hdev->nic_client;
int ret;
if (!test_bit(HCLGE_STATE_NIC_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 nic client failed %d(%d)\n",
type, ret);
return ret;
}
static int hclge_notify_roce_client(struct hclge_dev *hdev,
enum hnae3_reset_notify_type type)
{
struct hnae3_handle *handle = &hdev->vport[0].roce;
struct hnae3_client *client = hdev->roce_client;
int ret;
if (!test_bit(HCLGE_STATE_ROCE_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 roce client failed %d(%d)",
type, ret);
return ret;
}
static int hclge_reset_wait(struct hclge_dev *hdev)
{
#define HCLGE_RESET_WATI_MS 100
#define HCLGE_RESET_WAIT_CNT 350
u32 val, reg, reg_bit;
u32 cnt = 0;
switch (hdev->reset_type) {
case HNAE3_IMP_RESET:
reg = HCLGE_GLOBAL_RESET_REG;
reg_bit = HCLGE_IMP_RESET_BIT;
break;
case HNAE3_GLOBAL_RESET:
reg = HCLGE_GLOBAL_RESET_REG;
reg_bit = HCLGE_GLOBAL_RESET_BIT;
break;
case HNAE3_FUNC_RESET:
reg = HCLGE_FUN_RST_ING;
reg_bit = HCLGE_FUN_RST_ING_B;
break;
default:
dev_err(&hdev->pdev->dev,
"Wait for unsupported reset type: %d\n",
hdev->reset_type);
return -EINVAL;
}
val = hclge_read_dev(&hdev->hw, reg);
while (hnae3_get_bit(val, reg_bit) && cnt < HCLGE_RESET_WAIT_CNT) {
msleep(HCLGE_RESET_WATI_MS);
val = hclge_read_dev(&hdev->hw, reg);
cnt++;
}
if (cnt >= HCLGE_RESET_WAIT_CNT) {
dev_warn(&hdev->pdev->dev,
"Wait for reset timeout: %d\n", hdev->reset_type);
return -EBUSY;
}
return 0;
}
static int hclge_set_vf_rst(struct hclge_dev *hdev, int func_id, bool reset)
{
struct hclge_vf_rst_cmd *req;
struct hclge_desc desc;
req = (struct hclge_vf_rst_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GBL_RST_STATUS, false);
req->dest_vfid = func_id;
if (reset)
req->vf_rst = 0x1;
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
static int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
{
int i;
for (i = HCLGE_VF_VPORT_START_NUM; i < hdev->num_alloc_vport; i++) {
struct hclge_vport *vport = &hdev->vport[i];
int ret;
/* Send cmd to set/clear VF's FUNC_RST_ING */
ret = hclge_set_vf_rst(hdev, vport->vport_id, reset);
if (ret) {
dev_err(&hdev->pdev->dev,
"set vf(%u) rst failed %d!\n",
vport->vport_id - HCLGE_VF_VPORT_START_NUM,
ret);
return ret;
}
if (!reset ||
!test_bit(HCLGE_VPORT_STATE_INITED, &vport->state))
continue;
if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state) &&
hdev->reset_type == HNAE3_FUNC_RESET) {
set_bit(HCLGE_VPORT_NEED_NOTIFY_RESET,
&vport->need_notify);
continue;
}
/* Inform VF to process the reset.
* hclge_inform_reset_assert_to_vf may fail if VF
* driver is not loaded.
*/
ret = hclge_inform_reset_assert_to_vf(vport);
if (ret)
dev_warn(&hdev->pdev->dev,
"inform reset to vf(%u) failed %d!\n",
vport->vport_id - HCLGE_VF_VPORT_START_NUM,
ret);
}
return 0;
}
static void hclge_mailbox_service_task(struct hclge_dev *hdev)
{
if (!test_and_clear_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state) ||
test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state) ||
test_and_set_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state))
return;
if (time_is_before_jiffies(hdev->last_mbx_scheduled +
HCLGE_MBX_SCHED_TIMEOUT))
dev_warn(&hdev->pdev->dev,
"mbx service task is scheduled after %ums on cpu%u!\n",
jiffies_to_msecs(jiffies - hdev->last_mbx_scheduled),
smp_processor_id());
hclge_mbx_handler(hdev);
clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state);
}
static void hclge_func_reset_sync_vf(struct hclge_dev *hdev)
{
struct hclge_pf_rst_sync_cmd *req;
struct hclge_desc desc;
int cnt = 0;
int ret;
req = (struct hclge_pf_rst_sync_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_VF_RST_RDY, true);
do {
/* vf need to down netdev by mbx during PF or FLR reset */
hclge_mailbox_service_task(hdev);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
/* for compatible with old firmware, wait
* 100 ms for VF to stop IO
*/
if (ret == -EOPNOTSUPP) {
msleep(HCLGE_RESET_SYNC_TIME);
return;
} else if (ret) {
dev_warn(&hdev->pdev->dev, "sync with VF fail %d!\n",
ret);
return;
} else if (req->all_vf_ready) {
return;
}
msleep(HCLGE_PF_RESET_SYNC_TIME);
hclge_comm_cmd_reuse_desc(&desc, true);
} while (cnt++ < HCLGE_PF_RESET_SYNC_CNT);
dev_warn(&hdev->pdev->dev, "sync with VF timeout!\n");
}
void hclge_report_hw_error(struct hclge_dev *hdev,
enum hnae3_hw_error_type type)
{
struct hnae3_client *client = hdev->nic_client;
if (!client || !client->ops->process_hw_error ||
!test_bit(HCLGE_STATE_NIC_REGISTERED, &hdev->state))
return;
client->ops->process_hw_error(&hdev->vport[0].nic, type);
}
static void hclge_handle_imp_error(struct hclge_dev *hdev)
{
u32 reg_val;
reg_val = hclge_read_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG);
if (reg_val & BIT(HCLGE_VECTOR0_IMP_RD_POISON_B)) {
hclge_report_hw_error(hdev, HNAE3_IMP_RD_POISON_ERROR);
reg_val &= ~BIT(HCLGE_VECTOR0_IMP_RD_POISON_B);
hclge_write_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG, reg_val);
}
if (reg_val & BIT(HCLGE_VECTOR0_IMP_CMDQ_ERR_B)) {
hclge_report_hw_error(hdev, HNAE3_CMDQ_ECC_ERROR);
reg_val &= ~BIT(HCLGE_VECTOR0_IMP_CMDQ_ERR_B);
hclge_write_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG, reg_val);
}
}
int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id)
{
struct hclge_desc desc;
struct hclge_reset_cmd *req = (struct hclge_reset_cmd *)desc.data;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_RST_TRIGGER, false);
hnae3_set_bit(req->mac_func_reset, HCLGE_CFG_RESET_FUNC_B, 1);
req->fun_reset_vfid = func_id;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"send function reset cmd fail, status =%d\n", ret);
return ret;
}
static void hclge_do_reset(struct hclge_dev *hdev)
{
struct hnae3_handle *handle = &hdev->vport[0].nic;
struct pci_dev *pdev = hdev->pdev;
u32 val;
if (hclge_get_hw_reset_stat(handle)) {
dev_info(&pdev->dev, "hardware reset not finish\n");
dev_info(&pdev->dev, "func_rst_reg:0x%x, global_rst_reg:0x%x\n",
hclge_read_dev(&hdev->hw, HCLGE_FUN_RST_ING),
hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG));
return;
}
switch (hdev->reset_type) {
case HNAE3_IMP_RESET:
dev_info(&pdev->dev, "IMP reset requested\n");
val = hclge_read_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG);
hnae3_set_bit(val, HCLGE_TRIGGER_IMP_RESET_B, 1);
hclge_write_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG, val);
break;
case HNAE3_GLOBAL_RESET:
dev_info(&pdev->dev, "global reset requested\n");
val = hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG);
hnae3_set_bit(val, HCLGE_GLOBAL_RESET_BIT, 1);
hclge_write_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG, val);
break;
case HNAE3_FUNC_RESET:
dev_info(&pdev->dev, "PF reset requested\n");
/* schedule again to check later */
set_bit(HNAE3_FUNC_RESET, &hdev->reset_pending);
hclge_reset_task_schedule(hdev);
break;
default:
dev_warn(&pdev->dev,
"unsupported reset type: %d\n", hdev->reset_type);
break;
}
}
static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev,
unsigned long *addr)
{
enum hnae3_reset_type rst_level = HNAE3_NONE_RESET;
struct hclge_dev *hdev = ae_dev->priv;
/* return the highest priority reset level amongst all */
if (test_bit(HNAE3_IMP_RESET, addr)) {
rst_level = HNAE3_IMP_RESET;
clear_bit(HNAE3_IMP_RESET, addr);
clear_bit(HNAE3_GLOBAL_RESET, addr);
clear_bit(HNAE3_FUNC_RESET, addr);
} else if (test_bit(HNAE3_GLOBAL_RESET, addr)) {
rst_level = HNAE3_GLOBAL_RESET;
clear_bit(HNAE3_GLOBAL_RESET, addr);
clear_bit(HNAE3_FUNC_RESET, addr);
} else if (test_bit(HNAE3_FUNC_RESET, addr)) {
rst_level = HNAE3_FUNC_RESET;
clear_bit(HNAE3_FUNC_RESET, addr);
} else if (test_bit(HNAE3_FLR_RESET, addr)) {
rst_level = HNAE3_FLR_RESET;
clear_bit(HNAE3_FLR_RESET, addr);
}
if (hdev->reset_type != HNAE3_NONE_RESET &&
rst_level < hdev->reset_type)
return HNAE3_NONE_RESET;
return rst_level;
}
static void hclge_clear_reset_cause(struct hclge_dev *hdev)
{
u32 clearval = 0;
switch (hdev->reset_type) {
case HNAE3_IMP_RESET:
clearval = BIT(HCLGE_VECTOR0_IMPRESET_INT_B);
break;
case HNAE3_GLOBAL_RESET:
clearval = BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B);
break;
default:
break;
}
if (!clearval)
return;
/* For revision 0x20, the reset interrupt source
* can only be cleared after hardware reset done
*/
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
hclge_write_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG,
clearval);
hclge_enable_vector(&hdev->misc_vector, true);
}
static void hclge_reset_handshake(struct hclge_dev *hdev, bool enable)
{
u32 reg_val;
reg_val = hclge_read_dev(&hdev->hw, HCLGE_COMM_NIC_CSQ_DEPTH_REG);
if (enable)
reg_val |= HCLGE_COMM_NIC_SW_RST_RDY;
else
reg_val &= ~HCLGE_COMM_NIC_SW_RST_RDY;
hclge_write_dev(&hdev->hw, HCLGE_COMM_NIC_CSQ_DEPTH_REG, reg_val);
}
static int hclge_func_reset_notify_vf(struct hclge_dev *hdev)
{
int ret;
ret = hclge_set_all_vf_rst(hdev, true);
if (ret)
return ret;
hclge_func_reset_sync_vf(hdev);
return 0;
}
static int hclge_reset_prepare_wait(struct hclge_dev *hdev)
{
u32 reg_val;
int ret = 0;
switch (hdev->reset_type) {
case HNAE3_FUNC_RESET:
ret = hclge_func_reset_notify_vf(hdev);
if (ret)
return ret;
ret = hclge_func_reset_cmd(hdev, 0);
if (ret) {
dev_err(&hdev->pdev->dev,
"asserting function reset fail %d!\n", ret);
return ret;
}
/* After performaning pf reset, it is not necessary to do the
* mailbox handling or send any command to firmware, because
* any mailbox handling or command to firmware is only valid
* after hclge_comm_cmd_init is called.
*/
set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
hdev->rst_stats.pf_rst_cnt++;
break;
case HNAE3_FLR_RESET:
ret = hclge_func_reset_notify_vf(hdev);
if (ret)
return ret;
break;
case HNAE3_IMP_RESET:
hclge_handle_imp_error(hdev);
reg_val = hclge_read_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG);
hclge_write_dev(&hdev->hw, HCLGE_PF_OTHER_INT_REG,
BIT(HCLGE_VECTOR0_IMP_RESET_INT_B) | reg_val);
break;
default:
break;
}
/* inform hardware that preparatory work is done */
msleep(HCLGE_RESET_SYNC_TIME);
hclge_reset_handshake(hdev, true);
dev_info(&hdev->pdev->dev, "prepare wait ok\n");
return ret;
}
static void hclge_show_rst_info(struct hclge_dev *hdev)
{
char *buf;
buf = kzalloc(HCLGE_DBG_RESET_INFO_LEN, GFP_KERNEL);
if (!buf)
return;
hclge_dbg_dump_rst_info(hdev, buf, HCLGE_DBG_RESET_INFO_LEN);
dev_info(&hdev->pdev->dev, "dump reset info:\n%s", buf);
kfree(buf);
}
static bool hclge_reset_err_handle(struct hclge_dev *hdev)
{
#define MAX_RESET_FAIL_CNT 5
if (hdev->reset_pending) {
dev_info(&hdev->pdev->dev, "Reset pending %lu\n",
hdev->reset_pending);
return true;
} else if (hclge_read_dev(&hdev->hw, HCLGE_MISC_VECTOR_INT_STS) &
HCLGE_RESET_INT_M) {
dev_info(&hdev->pdev->dev,
"reset failed because new reset interrupt\n");
hclge_clear_reset_cause(hdev);
return false;
} else if (hdev->rst_stats.reset_fail_cnt < MAX_RESET_FAIL_CNT) {
hdev->rst_stats.reset_fail_cnt++;
set_bit(hdev->reset_type, &hdev->reset_pending);
dev_info(&hdev->pdev->dev,
"re-schedule reset task(%u)\n",
hdev->rst_stats.reset_fail_cnt);
return true;
}
hclge_clear_reset_cause(hdev);
/* recover the handshake status when reset fail */
hclge_reset_handshake(hdev, true);
dev_err(&hdev->pdev->dev, "Reset fail!\n");
hclge_show_rst_info(hdev);
set_bit(HCLGE_STATE_RST_FAIL, &hdev->state);
return false;
}
static void hclge_update_reset_level(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
enum hnae3_reset_type reset_level;
/* reset request will not be set during reset, so clear
* pending reset request to avoid unnecessary reset
* caused by the same reason.
*/
hclge_get_reset_level(ae_dev, &hdev->reset_request);
/* if default_reset_request has a higher level reset request,
* it should be handled as soon as possible. since some errors
* need this kind of reset to fix.
*/
reset_level = hclge_get_reset_level(ae_dev,
&hdev->default_reset_request);
if (reset_level != HNAE3_NONE_RESET)
set_bit(reset_level, &hdev->reset_request);
}
static int hclge_set_rst_done(struct hclge_dev *hdev)
{
struct hclge_pf_rst_done_cmd *req;
struct hclge_desc desc;
int ret;
req = (struct hclge_pf_rst_done_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PF_RST_DONE, false);
req->pf_rst_done |= HCLGE_PF_RESET_DONE_BIT;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
/* To be compatible with the old firmware, which does not support
* command HCLGE_OPC_PF_RST_DONE, just print a warning and
* return success
*/
if (ret == -EOPNOTSUPP) {
dev_warn(&hdev->pdev->dev,
"current firmware does not support command(0x%x)!\n",
HCLGE_OPC_PF_RST_DONE);
return 0;
} else if (ret) {
dev_err(&hdev->pdev->dev, "assert PF reset done fail %d!\n",
ret);
}
return ret;
}
static int hclge_reset_prepare_up(struct hclge_dev *hdev)
{
int ret = 0;
switch (hdev->reset_type) {
case HNAE3_FUNC_RESET:
case HNAE3_FLR_RESET:
ret = hclge_set_all_vf_rst(hdev, false);
break;
case HNAE3_GLOBAL_RESET:
case HNAE3_IMP_RESET:
ret = hclge_set_rst_done(hdev);
break;
default:
break;
}
/* clear up the handshake status after re-initialize done */
hclge_reset_handshake(hdev, false);
return ret;
}
static int hclge_reset_stack(struct hclge_dev *hdev)
{
int ret;
ret = hclge_notify_client(hdev, HNAE3_UNINIT_CLIENT);
if (ret)
return ret;
ret = hclge_reset_ae_dev(hdev->ae_dev);
if (ret)
return ret;
return hclge_notify_client(hdev, HNAE3_INIT_CLIENT);
}
static int hclge_reset_prepare(struct hclge_dev *hdev)
{
int ret;
hdev->rst_stats.reset_cnt++;
/* perform reset of the stack & ae device for a client */
ret = hclge_notify_roce_client(hdev, HNAE3_DOWN_CLIENT);
if (ret)
return ret;
rtnl_lock();
ret = hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
rtnl_unlock();
if (ret)
return ret;
return hclge_reset_prepare_wait(hdev);
}
static int hclge_reset_rebuild(struct hclge_dev *hdev)
{
int ret;
hdev->rst_stats.hw_reset_done_cnt++;
ret = hclge_notify_roce_client(hdev, HNAE3_UNINIT_CLIENT);
if (ret)
return ret;
rtnl_lock();
ret = hclge_reset_stack(hdev);
rtnl_unlock();
if (ret)
return ret;
hclge_clear_reset_cause(hdev);
ret = hclge_notify_roce_client(hdev, HNAE3_INIT_CLIENT);
/* ignore RoCE 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;
rtnl_lock();
ret = hclge_notify_client(hdev, HNAE3_UP_CLIENT);
rtnl_unlock();
if (ret)
return ret;
ret = hclge_notify_roce_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++;
clear_bit(HCLGE_STATE_RST_FAIL, &hdev->state);
hclge_update_reset_level(hdev);
return 0;
}
static void hclge_reset(struct hclge_dev *hdev)
{
if (hclge_reset_prepare(hdev))
goto err_reset;
if (hclge_reset_wait(hdev))
goto err_reset;
if (hclge_reset_rebuild(hdev))
goto err_reset;
return;
err_reset:
if (hclge_reset_err_handle(hdev))
hclge_reset_task_schedule(hdev);
}
static void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev);
struct hclge_dev *hdev = ae_dev->priv;
/* We might end up getting called broadly because of 2 below cases:
* 1. Recoverable error was conveyed through APEI and only way to bring
* normalcy is to reset.
* 2. A new reset request from the stack due to timeout
*
* check if this is a new reset request and we are not here just because
* last reset attempt did not succeed and watchdog hit us again. We will
* know this if last reset request did not occur very recently (watchdog
* timer = 5*HZ, let us check after sufficiently large time, say 4*5*Hz)
* In case of new request we reset the "reset level" to PF reset.
* And if it is a repeat reset request of the most recent one then we
* want to make sure we throttle the reset request. Therefore, we will
* not allow it again before 3*HZ times.
*/
if (time_before(jiffies, (hdev->last_reset_time +
HCLGE_RESET_INTERVAL))) {
mod_timer(&hdev->reset_timer, jiffies + HCLGE_RESET_INTERVAL);
return;
}
if (hdev->default_reset_request) {
hdev->reset_level =
hclge_get_reset_level(ae_dev,
&hdev->default_reset_request);
} else if (time_after(jiffies, (hdev->last_reset_time + 4 * 5 * HZ))) {
hdev->reset_level = HNAE3_FUNC_RESET;
}
dev_info(&hdev->pdev->dev, "received reset event, reset type is %d\n",
hdev->reset_level);
/* request reset & schedule reset task */
set_bit(hdev->reset_level, &hdev->reset_request);
hclge_reset_task_schedule(hdev);
if (hdev->reset_level < HNAE3_GLOBAL_RESET)
hdev->reset_level++;
}
static void hclge_set_def_reset_request(struct hnae3_ae_dev *ae_dev,
enum hnae3_reset_type rst_type)
{
struct hclge_dev *hdev = ae_dev->priv;
set_bit(rst_type, &hdev->default_reset_request);
}
static void hclge_reset_timer(struct timer_list *t)
{
struct hclge_dev *hdev = from_timer(hdev, t, reset_timer);
/* if default_reset_request has no value, it means that this reset
* request has already be handled, so just return here
*/
if (!hdev->default_reset_request)
return;
dev_info(&hdev->pdev->dev,
"triggering reset in reset timer\n");
hclge_reset_event(hdev->pdev, NULL);
}
static void hclge_reset_subtask(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
/* check if there is any ongoing reset in the hardware. This status can
* be checked from reset_pending. If there is then, we need to wait for
* hardware to complete reset.
* a. If we are able to figure out in reasonable time that hardware
* has fully resetted then, we can proceed with driver, client
* reset.
* b. else, we can come back later to check this status so re-sched
* now.
*/
hdev->last_reset_time = jiffies;
hdev->reset_type = hclge_get_reset_level(ae_dev, &hdev->reset_pending);
if (hdev->reset_type != HNAE3_NONE_RESET)
hclge_reset(hdev);
/* check if we got any *new* reset requests to be honored */
hdev->reset_type = hclge_get_reset_level(ae_dev, &hdev->reset_request);
if (hdev->reset_type != HNAE3_NONE_RESET)
hclge_do_reset(hdev);
hdev->reset_type = HNAE3_NONE_RESET;
}
static void hclge_handle_err_reset_request(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
enum hnae3_reset_type reset_type;
if (ae_dev->hw_err_reset_req) {
reset_type = hclge_get_reset_level(ae_dev,
&ae_dev->hw_err_reset_req);
hclge_set_def_reset_request(ae_dev, reset_type);
}
if (hdev->default_reset_request && ae_dev->ops->reset_event)
ae_dev->ops->reset_event(hdev->pdev, NULL);
/* enable interrupt after error handling complete */
hclge_enable_vector(&hdev->misc_vector, true);
}
static void hclge_handle_err_recovery(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
ae_dev->hw_err_reset_req = 0;
if (hclge_find_error_source(hdev)) {
hclge_handle_error_info_log(ae_dev);
hclge_handle_mac_tnl(hdev);
hclge_handle_vf_queue_err_ras(hdev);
}
hclge_handle_err_reset_request(hdev);
}
static void hclge_misc_err_recovery(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
struct device *dev = &hdev->pdev->dev;
u32 msix_sts_reg;
msix_sts_reg = hclge_read_dev(&hdev->hw, HCLGE_MISC_VECTOR_INT_STS);
if (msix_sts_reg & HCLGE_VECTOR0_REG_MSIX_MASK) {
if (hclge_handle_hw_msix_error
(hdev, &hdev->default_reset_request))
dev_info(dev, "received msix interrupt 0x%x\n",
msix_sts_reg);
}
hclge_handle_hw_ras_error(ae_dev);
hclge_handle_err_reset_request(hdev);
}
static void hclge_errhand_service_task(struct hclge_dev *hdev)
{
if (!test_and_clear_bit(HCLGE_STATE_ERR_SERVICE_SCHED, &hdev->state))
return;
if (hnae3_dev_ras_imp_supported(hdev))
hclge_handle_err_recovery(hdev);
else
hclge_misc_err_recovery(hdev);
}
static void hclge_reset_service_task(struct hclge_dev *hdev)
{
if (!test_and_clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state))
return;
if (time_is_before_jiffies(hdev->last_rst_scheduled +
HCLGE_RESET_SCHED_TIMEOUT))
dev_warn(&hdev->pdev->dev,
"reset service task is scheduled after %ums on cpu%u!\n",
jiffies_to_msecs(jiffies - hdev->last_rst_scheduled),
smp_processor_id());
down(&hdev->reset_sem);
set_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
hclge_reset_subtask(hdev);
clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
up(&hdev->reset_sem);
}
static void hclge_update_vport_alive(struct hclge_dev *hdev)
{
#define HCLGE_ALIVE_SECONDS_NORMAL 8
unsigned long alive_time = HCLGE_ALIVE_SECONDS_NORMAL * HZ;
int i;
/* start from vport 1 for PF is always alive */
for (i = 1; i < hdev->num_alloc_vport; i++) {
struct hclge_vport *vport = &hdev->vport[i];
if (!test_bit(HCLGE_VPORT_STATE_INITED, &vport->state) ||
!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
continue;
if (time_after(jiffies, vport->last_active_jiffies +
alive_time)) {
clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
dev_warn(&hdev->pdev->dev,
"VF %u heartbeat timeout\n",
i - HCLGE_VF_VPORT_START_NUM);
}
}
}
static void hclge_periodic_service_task(struct hclge_dev *hdev)
{
unsigned long delta = round_jiffies_relative(HZ);
if (test_bit(HCLGE_STATE_RST_FAIL, &hdev->state))
return;
/* Always handle the link updating to make sure link state is
* updated when it is triggered by mbx.
*/
hclge_update_link_status(hdev);
hclge_sync_mac_table(hdev);
hclge_sync_promisc_mode(hdev);
hclge_sync_fd_table(hdev);
if (time_is_after_jiffies(hdev->last_serv_processed + HZ)) {
delta = jiffies - hdev->last_serv_processed;
if (delta < round_jiffies_relative(HZ)) {
delta = round_jiffies_relative(HZ) - delta;
goto out;
}
}
hdev->serv_processed_cnt++;
hclge_update_vport_alive(hdev);
if (test_bit(HCLGE_STATE_DOWN, &hdev->state)) {
hdev->last_serv_processed = jiffies;
goto out;
}
if (!(hdev->serv_processed_cnt % HCLGE_STATS_TIMER_INTERVAL))
hclge_update_stats_for_all(hdev);
hclge_update_port_info(hdev);
hclge_sync_vlan_filter(hdev);
if (!(hdev->serv_processed_cnt % HCLGE_ARFS_EXPIRE_INTERVAL))
hclge_rfs_filter_expire(hdev);
hdev->last_serv_processed = jiffies;
out:
hclge_task_schedule(hdev, delta);
}
static void hclge_ptp_service_task(struct hclge_dev *hdev)
{
unsigned long flags;
if (!test_bit(HCLGE_STATE_PTP_EN, &hdev->state) ||
!test_bit(HCLGE_STATE_PTP_TX_HANDLING, &hdev->state) ||
!time_is_before_jiffies(hdev->ptp->tx_start + HZ))
return;
/* to prevent concurrence with the irq handler */
spin_lock_irqsave(&hdev->ptp->lock, flags);
/* check HCLGE_STATE_PTP_TX_HANDLING here again, since the irq
* handler may handle it just before spin_lock_irqsave().
*/
if (test_bit(HCLGE_STATE_PTP_TX_HANDLING, &hdev->state))
hclge_ptp_clean_tx_hwts(hdev);
spin_unlock_irqrestore(&hdev->ptp->lock, flags);
}
static void hclge_service_task(struct work_struct *work)
{
struct hclge_dev *hdev =
container_of(work, struct hclge_dev, service_task.work);
hclge_errhand_service_task(hdev);
hclge_reset_service_task(hdev);
hclge_ptp_service_task(hdev);
hclge_mailbox_service_task(hdev);
hclge_periodic_service_task(hdev);
/* Handle error recovery, reset and mbx again in case periodical task
* delays the handling by calling hclge_task_schedule() in
* hclge_periodic_service_task().
*/
hclge_errhand_service_task(hdev);
hclge_reset_service_task(hdev);
hclge_mailbox_service_task(hdev);
}
struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle)
{
/* VF handle has no client */
if (!handle->client)
return container_of(handle, struct hclge_vport, nic);
else if (handle->client->type == HNAE3_CLIENT_ROCE)
return container_of(handle, struct hclge_vport, roce);
else
return container_of(handle, struct hclge_vport, nic);
}
static void hclge_get_vector_info(struct hclge_dev *hdev, u16 idx,
struct hnae3_vector_info *vector_info)
{
#define HCLGE_PF_MAX_VECTOR_NUM_DEV_V2 64
vector_info->vector = pci_irq_vector(hdev->pdev, idx);
/* need an extend offset to config vector >= 64 */
if (idx - 1 < HCLGE_PF_MAX_VECTOR_NUM_DEV_V2)
vector_info->io_addr = hdev->hw.hw.io_base +
HCLGE_VECTOR_REG_BASE +
(idx - 1) * HCLGE_VECTOR_REG_OFFSET;
else
vector_info->io_addr = hdev->hw.hw.io_base +
HCLGE_VECTOR_EXT_REG_BASE +
(idx - 1) / HCLGE_PF_MAX_VECTOR_NUM_DEV_V2 *
HCLGE_VECTOR_REG_OFFSET_H +
(idx - 1) % HCLGE_PF_MAX_VECTOR_NUM_DEV_V2 *
HCLGE_VECTOR_REG_OFFSET;
hdev->vector_status[idx] = hdev->vport[0].vport_id;
hdev->vector_irq[idx] = vector_info->vector;
}
static int hclge_get_vector(struct hnae3_handle *handle, u16 vector_num,
struct hnae3_vector_info *vector_info)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hnae3_vector_info *vector = vector_info;
struct hclge_dev *hdev = vport->back;
int alloc = 0;
u16 i = 0;
u16 j;
vector_num = min_t(u16, hdev->num_nic_msi - 1, vector_num);
vector_num = min(hdev->num_msi_left, vector_num);
for (j = 0; j < vector_num; j++) {
while (++i < hdev->num_nic_msi) {
if (hdev->vector_status[i] == HCLGE_INVALID_VPORT) {
hclge_get_vector_info(hdev, i, vector);
vector++;
alloc++;
break;
}
}
}
hdev->num_msi_left -= alloc;
hdev->num_msi_used += alloc;
return alloc;
}
static int hclge_get_vector_index(struct hclge_dev *hdev, int vector)
{
int i;
for (i = 0; i < hdev->num_msi; i++)
if (vector == hdev->vector_irq[i])
return i;
return -EINVAL;
}
static int hclge_put_vector(struct hnae3_handle *handle, int vector)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int vector_id;
vector_id = hclge_get_vector_index(hdev, vector);
if (vector_id < 0) {
dev_err(&hdev->pdev->dev,
"Get vector index fail. vector = %d\n", vector);
return vector_id;
}
hclge_free_vector(hdev, vector_id);
return 0;
}
static int hclge_get_rss(struct hnae3_handle *handle, u32 *indir,
u8 *key, u8 *hfunc)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_comm_rss_cfg *rss_cfg = &vport->back->rss_cfg;
hclge_comm_get_rss_hash_info(rss_cfg, key, hfunc);
hclge_comm_get_rss_indir_tbl(rss_cfg, indir,
ae_dev->dev_specs.rss_ind_tbl_size);
return 0;
}
static int hclge_set_rss(struct hnae3_handle *handle, const u32 *indir,
const u8 *key, const u8 hfunc)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_comm_rss_cfg *rss_cfg = &hdev->rss_cfg;
int ret, i;
ret = hclge_comm_set_rss_hash_key(rss_cfg, &hdev->hw.hw, key, hfunc);
if (ret) {
dev_err(&hdev->pdev->dev, "invalid hfunc type %u\n", hfunc);
return ret;
}
/* Update the shadow RSS table with user specified qids */
for (i = 0; i < ae_dev->dev_specs.rss_ind_tbl_size; i++)
rss_cfg->rss_indirection_tbl[i] = indir[i];
/* Update the hardware */
return hclge_comm_set_rss_indir_table(ae_dev, &hdev->hw.hw,
rss_cfg->rss_indirection_tbl);
}
static int hclge_set_rss_tuple(struct hnae3_handle *handle,
struct ethtool_rxnfc *nfc)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int ret;
ret = hclge_comm_set_rss_tuple(hdev->ae_dev, &hdev->hw.hw,
&hdev->rss_cfg, nfc);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to set rss tuple, ret = %d.\n", ret);
return ret;
}
return 0;
}
static int hclge_get_rss_tuple(struct hnae3_handle *handle,
struct ethtool_rxnfc *nfc)
{
struct hclge_vport *vport = hclge_get_vport(handle);
u8 tuple_sets;
int ret;
nfc->data = 0;
ret = hclge_comm_get_rss_tuple(&vport->back->rss_cfg, nfc->flow_type,
&tuple_sets);
if (ret || !tuple_sets)
return ret;
nfc->data = hclge_comm_convert_rss_tuple(tuple_sets);
return 0;
}
static int hclge_get_tc_size(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return hdev->pf_rss_size_max;
}
static int hclge_init_rss_tc_mode(struct hclge_dev *hdev)
{
struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
struct hclge_vport *vport = hdev->vport;
u16 tc_offset[HCLGE_MAX_TC_NUM] = {0};
u16 tc_valid[HCLGE_MAX_TC_NUM] = {0};
u16 tc_size[HCLGE_MAX_TC_NUM] = {0};
struct hnae3_tc_info *tc_info;
u16 roundup_size;
u16 rss_size;
int i;
tc_info = &vport->nic.kinfo.tc_info;
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
rss_size = tc_info->tqp_count[i];
tc_valid[i] = 0;
if (!(hdev->hw_tc_map & BIT(i)))
continue;
/* tc_size set to hardware is the log2 of roundup power of two
* of rss_size, the acutal queue size is limited by indirection
* table.
*/
if (rss_size > ae_dev->dev_specs.rss_ind_tbl_size ||
rss_size == 0) {
dev_err(&hdev->pdev->dev,
"Configure rss tc size failed, invalid TC_SIZE = %u\n",
rss_size);
return -EINVAL;
}
roundup_size = roundup_pow_of_two(rss_size);
roundup_size = ilog2(roundup_size);
tc_valid[i] = 1;
tc_size[i] = roundup_size;
tc_offset[i] = tc_info->tqp_offset[i];
}
return hclge_comm_set_rss_tc_mode(&hdev->hw.hw, tc_offset, tc_valid,
tc_size);
}
int hclge_rss_init_hw(struct hclge_dev *hdev)
{
u16 *rss_indir = hdev->rss_cfg.rss_indirection_tbl;
u8 *key = hdev->rss_cfg.rss_hash_key;
u8 hfunc = hdev->rss_cfg.rss_algo;
int ret;
ret = hclge_comm_set_rss_indir_table(hdev->ae_dev, &hdev->hw.hw,
rss_indir);
if (ret)
return ret;
ret = hclge_comm_set_rss_algo_key(&hdev->hw.hw, hfunc, key);
if (ret)
return ret;
ret = hclge_comm_set_rss_input_tuple(&hdev->hw.hw, &hdev->rss_cfg);
if (ret)
return ret;
return hclge_init_rss_tc_mode(hdev);
}
int hclge_bind_ring_with_vector(struct hclge_vport *vport,
int vector_id, bool en,
struct hnae3_ring_chain_node *ring_chain)
{
struct hclge_dev *hdev = vport->back;
struct hnae3_ring_chain_node *node;
struct hclge_desc desc;
struct hclge_ctrl_vector_chain_cmd *req =
(struct hclge_ctrl_vector_chain_cmd *)desc.data;
enum hclge_comm_cmd_status status;
enum hclge_opcode_type op;
u16 tqp_type_and_id;
int i;
op = en ? HCLGE_OPC_ADD_RING_TO_VECTOR : HCLGE_OPC_DEL_RING_TO_VECTOR;
hclge_cmd_setup_basic_desc(&desc, op, false);
req->int_vector_id_l = hnae3_get_field(vector_id,
HCLGE_VECTOR_ID_L_M,
HCLGE_VECTOR_ID_L_S);
req->int_vector_id_h = hnae3_get_field(vector_id,
HCLGE_VECTOR_ID_H_M,
HCLGE_VECTOR_ID_H_S);
i = 0;
for (node = ring_chain; node; node = node->next) {
tqp_type_and_id = le16_to_cpu(req->tqp_type_and_id[i]);
hnae3_set_field(tqp_type_and_id, HCLGE_INT_TYPE_M,
HCLGE_INT_TYPE_S,
hnae3_get_bit(node->flag, HNAE3_RING_TYPE_B));
hnae3_set_field(tqp_type_and_id, HCLGE_TQP_ID_M,
HCLGE_TQP_ID_S, node->tqp_index);
hnae3_set_field(tqp_type_and_id, HCLGE_INT_GL_IDX_M,
HCLGE_INT_GL_IDX_S,
hnae3_get_field(node->int_gl_idx,
HNAE3_RING_GL_IDX_M,
HNAE3_RING_GL_IDX_S));
req->tqp_type_and_id[i] = cpu_to_le16(tqp_type_and_id);
if (++i >= HCLGE_VECTOR_ELEMENTS_PER_CMD) {
req->int_cause_num = HCLGE_VECTOR_ELEMENTS_PER_CMD;
req->vfid = vport->vport_id;
status = hclge_cmd_send(&hdev->hw, &desc, 1);
if (status) {
dev_err(&hdev->pdev->dev,
"Map TQP fail, status is %d.\n",
status);
return -EIO;
}
i = 0;
hclge_cmd_setup_basic_desc(&desc,
op,
false);
req->int_vector_id_l =
hnae3_get_field(vector_id,
HCLGE_VECTOR_ID_L_M,
HCLGE_VECTOR_ID_L_S);
req->int_vector_id_h =
hnae3_get_field(vector_id,
HCLGE_VECTOR_ID_H_M,
HCLGE_VECTOR_ID_H_S);
}
}
if (i > 0) {
req->int_cause_num = i;
req->vfid = vport->vport_id;
status = hclge_cmd_send(&hdev->hw, &desc, 1);
if (status) {
dev_err(&hdev->pdev->dev,
"Map TQP fail, status is %d.\n", status);
return -EIO;
}
}
return 0;
}
static int hclge_map_ring_to_vector(struct hnae3_handle *handle, int vector,
struct hnae3_ring_chain_node *ring_chain)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int vector_id;
vector_id = hclge_get_vector_index(hdev, vector);
if (vector_id < 0) {
dev_err(&hdev->pdev->dev,
"failed to get vector index. vector=%d\n", vector);
return vector_id;
}
return hclge_bind_ring_with_vector(vport, vector_id, true, ring_chain);
}
static int hclge_unmap_ring_frm_vector(struct hnae3_handle *handle, int vector,
struct hnae3_ring_chain_node *ring_chain)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int vector_id, ret;
if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
return 0;
vector_id = hclge_get_vector_index(hdev, vector);
if (vector_id < 0) {
dev_err(&handle->pdev->dev,
"Get vector index fail. ret =%d\n", vector_id);
return vector_id;
}
ret = hclge_bind_ring_with_vector(vport, vector_id, false, ring_chain);
if (ret)
dev_err(&handle->pdev->dev,
"Unmap ring from vector fail. vectorid=%d, ret =%d\n",
vector_id, ret);
return ret;
}
static int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev, u8 vf_id,
bool en_uc, bool en_mc, bool en_bc)
{
struct hclge_vport *vport = &hdev->vport[vf_id];
struct hnae3_handle *handle = &vport->nic;
struct hclge_promisc_cfg_cmd *req;
struct hclge_desc desc;
bool uc_tx_en = en_uc;
u8 promisc_cfg = 0;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_PROMISC_MODE, false);
req = (struct hclge_promisc_cfg_cmd *)desc.data;
req->vf_id = vf_id;
if (test_bit(HNAE3_PFLAG_LIMIT_PROMISC, &handle->priv_flags))
uc_tx_en = false;
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_UC_RX_EN, en_uc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_MC_RX_EN, en_mc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_BC_RX_EN, en_bc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_UC_TX_EN, uc_tx_en ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_MC_TX_EN, en_mc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_BC_TX_EN, en_bc ? 1 : 0);
req->extend_promisc = promisc_cfg;
/* to be compatible with DEVICE_VERSION_V1/2 */
promisc_cfg = 0;
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_EN_UC, en_uc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_EN_MC, en_mc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_EN_BC, en_bc ? 1 : 0);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_TX_EN, 1);
hnae3_set_bit(promisc_cfg, HCLGE_PROMISC_RX_EN, 1);
req->promisc = promisc_cfg;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to set vport %u promisc mode, ret = %d.\n",
vf_id, ret);
return ret;
}
int hclge_set_vport_promisc_mode(struct hclge_vport *vport, bool en_uc_pmc,
bool en_mc_pmc, bool en_bc_pmc)
{
return hclge_cmd_set_promisc_mode(vport->back, vport->vport_id,
en_uc_pmc, en_mc_pmc, en_bc_pmc);
}
static int hclge_set_promisc_mode(struct hnae3_handle *handle, bool en_uc_pmc,
bool en_mc_pmc)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
bool en_bc_pmc = true;
/* For device whose version below V2, if broadcast promisc enabled,
* vlan filter is always bypassed. So broadcast promisc should be
* disabled until user enable promisc mode
*/
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
en_bc_pmc = handle->netdev_flags & HNAE3_BPE ? true : false;
return hclge_set_vport_promisc_mode(vport, en_uc_pmc, en_mc_pmc,
en_bc_pmc);
}
static void hclge_request_update_promisc_mode(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
}
static void hclge_sync_fd_state(struct hclge_dev *hdev)
{
if (hlist_empty(&hdev->fd_rule_list))
hdev->fd_active_type = HCLGE_FD_RULE_NONE;
}
static void hclge_fd_inc_rule_cnt(struct hclge_dev *hdev, u16 location)
{
if (!test_bit(location, hdev->fd_bmap)) {
set_bit(location, hdev->fd_bmap);
hdev->hclge_fd_rule_num++;
}
}
static void hclge_fd_dec_rule_cnt(struct hclge_dev *hdev, u16 location)
{
if (test_bit(location, hdev->fd_bmap)) {
clear_bit(location, hdev->fd_bmap);
hdev->hclge_fd_rule_num--;
}
}
static void hclge_fd_free_node(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
hlist_del(&rule->rule_node);
kfree(rule);
hclge_sync_fd_state(hdev);
}
static void hclge_update_fd_rule_node(struct hclge_dev *hdev,
struct hclge_fd_rule *old_rule,
struct hclge_fd_rule *new_rule,
enum HCLGE_FD_NODE_STATE state)
{
switch (state) {
case HCLGE_FD_TO_ADD:
case HCLGE_FD_ACTIVE:
/* 1) if the new state is TO_ADD, just replace the old rule
* with the same location, no matter its state, because the
* new rule will be configured to the hardware.
* 2) if the new state is ACTIVE, it means the new rule
* has been configured to the hardware, so just replace
* the old rule node with the same location.
* 3) for it doesn't add a new node to the list, so it's
* unnecessary to update the rule number and fd_bmap.
*/
new_rule->rule_node.next = old_rule->rule_node.next;
new_rule->rule_node.pprev = old_rule->rule_node.pprev;
memcpy(old_rule, new_rule, sizeof(*old_rule));
kfree(new_rule);
break;
case HCLGE_FD_DELETED:
hclge_fd_dec_rule_cnt(hdev, old_rule->location);
hclge_fd_free_node(hdev, old_rule);
break;
case HCLGE_FD_TO_DEL:
/* if new request is TO_DEL, and old rule is existent
* 1) the state of old rule is TO_DEL, we need do nothing,
* because we delete rule by location, other rule content
* is unncessary.
* 2) the state of old rule is ACTIVE, we need to change its
* state to TO_DEL, so the rule will be deleted when periodic
* task being scheduled.
* 3) the state of old rule is TO_ADD, it means the rule hasn't
* been added to hardware, so we just delete the rule node from
* fd_rule_list directly.
*/
if (old_rule->state == HCLGE_FD_TO_ADD) {
hclge_fd_dec_rule_cnt(hdev, old_rule->location);
hclge_fd_free_node(hdev, old_rule);
return;
}
old_rule->state = HCLGE_FD_TO_DEL;
break;
}
}
static struct hclge_fd_rule *hclge_find_fd_rule(struct hlist_head *hlist,
u16 location,
struct hclge_fd_rule **parent)
{
struct hclge_fd_rule *rule;
struct hlist_node *node;
hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
if (rule->location == location)
return rule;
else if (rule->location > location)
return NULL;
/* record the parent node, use to keep the nodes in fd_rule_list
* in ascend order.
*/
*parent = rule;
}
return NULL;
}
/* insert fd rule node in ascend order according to rule->location */
static void hclge_fd_insert_rule_node(struct hlist_head *hlist,
struct hclge_fd_rule *rule,
struct hclge_fd_rule *parent)
{
INIT_HLIST_NODE(&rule->rule_node);
if (parent)
hlist_add_behind(&rule->rule_node, &parent->rule_node);
else
hlist_add_head(&rule->rule_node, hlist);
}
static int hclge_fd_set_user_def_cmd(struct hclge_dev *hdev,
struct hclge_fd_user_def_cfg *cfg)
{
struct hclge_fd_user_def_cfg_cmd *req;
struct hclge_desc desc;
u16 data = 0;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_USER_DEF_OP, false);
req = (struct hclge_fd_user_def_cfg_cmd *)desc.data;
hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[0].ref_cnt > 0);
hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
HCLGE_FD_USER_DEF_OFT_S, cfg[0].offset);
req->ol2_cfg = cpu_to_le16(data);
data = 0;
hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[1].ref_cnt > 0);
hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
HCLGE_FD_USER_DEF_OFT_S, cfg[1].offset);
req->ol3_cfg = cpu_to_le16(data);
data = 0;
hnae3_set_bit(data, HCLGE_FD_USER_DEF_EN_B, cfg[2].ref_cnt > 0);
hnae3_set_field(data, HCLGE_FD_USER_DEF_OFT_M,
HCLGE_FD_USER_DEF_OFT_S, cfg[2].offset);
req->ol4_cfg = cpu_to_le16(data);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to set fd user def data, ret= %d\n", ret);
return ret;
}
static void hclge_sync_fd_user_def_cfg(struct hclge_dev *hdev, bool locked)
{
int ret;
if (!test_and_clear_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state))
return;
if (!locked)
spin_lock_bh(&hdev->fd_rule_lock);
ret = hclge_fd_set_user_def_cmd(hdev, hdev->fd_cfg.user_def_cfg);
if (ret)
set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
if (!locked)
spin_unlock_bh(&hdev->fd_rule_lock);
}
static int hclge_fd_check_user_def_refcnt(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
struct hlist_head *hlist = &hdev->fd_rule_list;
struct hclge_fd_rule *fd_rule, *parent = NULL;
struct hclge_fd_user_def_info *info, *old_info;
struct hclge_fd_user_def_cfg *cfg;
if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
return 0;
/* for valid layer is start from 1, so need minus 1 to get the cfg */
cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
info = &rule->ep.user_def;
if (!cfg->ref_cnt || cfg->offset == info->offset)
return 0;
if (cfg->ref_cnt > 1)
goto error;
fd_rule = hclge_find_fd_rule(hlist, rule->location, &parent);
if (fd_rule) {
old_info = &fd_rule->ep.user_def;
if (info->layer == old_info->layer)
return 0;
}
error:
dev_err(&hdev->pdev->dev,
"No available offset for layer%d fd rule, each layer only support one user def offset.\n",
info->layer + 1);
return -ENOSPC;
}
static void hclge_fd_inc_user_def_refcnt(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
struct hclge_fd_user_def_cfg *cfg;
if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
return;
cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
if (!cfg->ref_cnt) {
cfg->offset = rule->ep.user_def.offset;
set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
}
cfg->ref_cnt++;
}
static void hclge_fd_dec_user_def_refcnt(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
struct hclge_fd_user_def_cfg *cfg;
if (!rule || rule->rule_type != HCLGE_FD_EP_ACTIVE ||
rule->ep.user_def.layer == HCLGE_FD_USER_DEF_NONE)
return;
cfg = &hdev->fd_cfg.user_def_cfg[rule->ep.user_def.layer - 1];
if (!cfg->ref_cnt)
return;
cfg->ref_cnt--;
if (!cfg->ref_cnt) {
cfg->offset = 0;
set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
}
}
static void hclge_update_fd_list(struct hclge_dev *hdev,
enum HCLGE_FD_NODE_STATE state, u16 location,
struct hclge_fd_rule *new_rule)
{
struct hlist_head *hlist = &hdev->fd_rule_list;
struct hclge_fd_rule *fd_rule, *parent = NULL;
fd_rule = hclge_find_fd_rule(hlist, location, &parent);
if (fd_rule) {
hclge_fd_dec_user_def_refcnt(hdev, fd_rule);
if (state == HCLGE_FD_ACTIVE)
hclge_fd_inc_user_def_refcnt(hdev, new_rule);
hclge_sync_fd_user_def_cfg(hdev, true);
hclge_update_fd_rule_node(hdev, fd_rule, new_rule, state);
return;
}
/* it's unlikely to fail here, because we have checked the rule
* exist before.
*/
if (unlikely(state == HCLGE_FD_TO_DEL || state == HCLGE_FD_DELETED)) {
dev_warn(&hdev->pdev->dev,
"failed to delete fd rule %u, it's inexistent\n",
location);
return;
}
hclge_fd_inc_user_def_refcnt(hdev, new_rule);
hclge_sync_fd_user_def_cfg(hdev, true);
hclge_fd_insert_rule_node(hlist, new_rule, parent);
hclge_fd_inc_rule_cnt(hdev, new_rule->location);
if (state == HCLGE_FD_TO_ADD) {
set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
hclge_task_schedule(hdev, 0);
}
}
static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode)
{
struct hclge_get_fd_mode_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_MODE_CTRL, true);
req = (struct hclge_get_fd_mode_cmd *)desc.data;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev, "get fd mode fail, ret=%d\n", ret);
return ret;
}
*fd_mode = req->mode;
return ret;
}
static int hclge_get_fd_allocation(struct hclge_dev *hdev,
u32 *stage1_entry_num,
u32 *stage2_entry_num,
u16 *stage1_counter_num,
u16 *stage2_counter_num)
{
struct hclge_get_fd_allocation_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_GET_ALLOCATION, true);
req = (struct hclge_get_fd_allocation_cmd *)desc.data;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev, "query fd allocation fail, ret=%d\n",
ret);
return ret;
}
*stage1_entry_num = le32_to_cpu(req->stage1_entry_num);
*stage2_entry_num = le32_to_cpu(req->stage2_entry_num);
*stage1_counter_num = le16_to_cpu(req->stage1_counter_num);
*stage2_counter_num = le16_to_cpu(req->stage2_counter_num);
return ret;
}
static int hclge_set_fd_key_config(struct hclge_dev *hdev,
enum HCLGE_FD_STAGE stage_num)
{
struct hclge_set_fd_key_config_cmd *req;
struct hclge_fd_key_cfg *stage;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_KEY_CONFIG, false);
req = (struct hclge_set_fd_key_config_cmd *)desc.data;
stage = &hdev->fd_cfg.key_cfg[stage_num];
req->stage = stage_num;
req->key_select = stage->key_sel;
req->inner_sipv6_word_en = stage->inner_sipv6_word_en;
req->inner_dipv6_word_en = stage->inner_dipv6_word_en;
req->outer_sipv6_word_en = stage->outer_sipv6_word_en;
req->outer_dipv6_word_en = stage->outer_dipv6_word_en;
req->tuple_mask = cpu_to_le32(~stage->tuple_active);
req->meta_data_mask = cpu_to_le32(~stage->meta_data_active);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "set fd key fail, ret=%d\n", ret);
return ret;
}
static void hclge_fd_disable_user_def(struct hclge_dev *hdev)
{
struct hclge_fd_user_def_cfg *cfg = hdev->fd_cfg.user_def_cfg;
spin_lock_bh(&hdev->fd_rule_lock);
memset(cfg, 0, sizeof(hdev->fd_cfg.user_def_cfg));
spin_unlock_bh(&hdev->fd_rule_lock);
hclge_fd_set_user_def_cmd(hdev, cfg);
}
static int hclge_init_fd_config(struct hclge_dev *hdev)
{
#define LOW_2_WORDS 0x03
struct hclge_fd_key_cfg *key_cfg;
int ret;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return 0;
ret = hclge_get_fd_mode(hdev, &hdev->fd_cfg.fd_mode);
if (ret)
return ret;
switch (hdev->fd_cfg.fd_mode) {
case HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1:
hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH;
break;
case HCLGE_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1:
hdev->fd_cfg.max_key_length = MAX_KEY_LENGTH / 2;
break;
default:
dev_err(&hdev->pdev->dev,
"Unsupported flow director mode %u\n",
hdev->fd_cfg.fd_mode);
return -EOPNOTSUPP;
}
key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1];
key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE;
key_cfg->inner_sipv6_word_en = LOW_2_WORDS;
key_cfg->inner_dipv6_word_en = LOW_2_WORDS;
key_cfg->outer_sipv6_word_en = 0;
key_cfg->outer_dipv6_word_en = 0;
key_cfg->tuple_active = BIT(INNER_VLAN_TAG_FST) | BIT(INNER_ETH_TYPE) |
BIT(INNER_IP_PROTO) | BIT(INNER_IP_TOS) |
BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
/* If use max 400bit key, we can support tuples for ether type */
if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
key_cfg->tuple_active |=
BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC);
if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3)
key_cfg->tuple_active |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
}
/* roce_type is used to filter roce frames
* dst_vport is used to specify the rule
*/
key_cfg->meta_data_active = BIT(ROCE_TYPE) | BIT(DST_VPORT);
ret = hclge_get_fd_allocation(hdev,
&hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1],
&hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_2],
&hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1],
&hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_2]);
if (ret)
return ret;
return hclge_set_fd_key_config(hdev, HCLGE_FD_STAGE_1);
}
static int hclge_fd_tcam_config(struct hclge_dev *hdev, u8 stage, bool sel_x,
int loc, u8 *key, bool is_add)
{
struct hclge_fd_tcam_config_1_cmd *req1;
struct hclge_fd_tcam_config_2_cmd *req2;
struct hclge_fd_tcam_config_3_cmd *req3;
struct hclge_desc desc[3];
int ret;
hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, false);
desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_cmd_setup_basic_desc(&desc[1], HCLGE_OPC_FD_TCAM_OP, false);
desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_cmd_setup_basic_desc(&desc[2], HCLGE_OPC_FD_TCAM_OP, false);
req1 = (struct hclge_fd_tcam_config_1_cmd *)desc[0].data;
req2 = (struct hclge_fd_tcam_config_2_cmd *)desc[1].data;
req3 = (struct hclge_fd_tcam_config_3_cmd *)desc[2].data;
req1->stage = stage;
req1->xy_sel = sel_x ? 1 : 0;
hnae3_set_bit(req1->port_info, HCLGE_FD_EPORT_SW_EN_B, 0);
req1->index = cpu_to_le32(loc);
req1->entry_vld = sel_x ? is_add : 0;
if (key) {
memcpy(req1->tcam_data, &key[0], sizeof(req1->tcam_data));
memcpy(req2->tcam_data, &key[sizeof(req1->tcam_data)],
sizeof(req2->tcam_data));
memcpy(req3->tcam_data, &key[sizeof(req1->tcam_data) +
sizeof(req2->tcam_data)], sizeof(req3->tcam_data));
}
ret = hclge_cmd_send(&hdev->hw, desc, 3);
if (ret)
dev_err(&hdev->pdev->dev,
"config tcam key fail, ret=%d\n",
ret);
return ret;
}
static int hclge_fd_ad_config(struct hclge_dev *hdev, u8 stage, int loc,
struct hclge_fd_ad_data *action)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
struct hclge_fd_ad_config_cmd *req;
struct hclge_desc desc;
u64 ad_data = 0;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_FD_AD_OP, false);
req = (struct hclge_fd_ad_config_cmd *)desc.data;
req->index = cpu_to_le32(loc);
req->stage = stage;
hnae3_set_bit(ad_data, HCLGE_FD_AD_WR_RULE_ID_B,
action->write_rule_id_to_bd);
hnae3_set_field(ad_data, HCLGE_FD_AD_RULE_ID_M, HCLGE_FD_AD_RULE_ID_S,
action->rule_id);
if (test_bit(HNAE3_DEV_SUPPORT_FD_FORWARD_TC_B, ae_dev->caps)) {
hnae3_set_bit(ad_data, HCLGE_FD_AD_TC_OVRD_B,
action->override_tc);
hnae3_set_field(ad_data, HCLGE_FD_AD_TC_SIZE_M,
HCLGE_FD_AD_TC_SIZE_S, (u32)action->tc_size);
}
ad_data <<= 32;
hnae3_set_bit(ad_data, HCLGE_FD_AD_DROP_B, action->drop_packet);
hnae3_set_bit(ad_data, HCLGE_FD_AD_DIRECT_QID_B,
action->forward_to_direct_queue);
hnae3_set_field(ad_data, HCLGE_FD_AD_QID_M, HCLGE_FD_AD_QID_S,
action->queue_id);
hnae3_set_bit(ad_data, HCLGE_FD_AD_USE_COUNTER_B, action->use_counter);
hnae3_set_field(ad_data, HCLGE_FD_AD_COUNTER_NUM_M,
HCLGE_FD_AD_COUNTER_NUM_S, action->counter_id);
hnae3_set_bit(ad_data, HCLGE_FD_AD_NXT_STEP_B, action->use_next_stage);
hnae3_set_field(ad_data, HCLGE_FD_AD_NXT_KEY_M, HCLGE_FD_AD_NXT_KEY_S,
action->counter_id);
req->ad_data = cpu_to_le64(ad_data);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "fd ad config fail, ret=%d\n", ret);
return ret;
}
static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y,
struct hclge_fd_rule *rule)
{
int offset, moffset, ip_offset;
enum HCLGE_FD_KEY_OPT key_opt;
u16 tmp_x_s, tmp_y_s;
u32 tmp_x_l, tmp_y_l;
u8 *p = (u8 *)rule;
int i;
if (rule->unused_tuple & BIT(tuple_bit))
return true;
key_opt = tuple_key_info[tuple_bit].key_opt;
offset = tuple_key_info[tuple_bit].offset;
moffset = tuple_key_info[tuple_bit].moffset;
switch (key_opt) {
case KEY_OPT_U8:
calc_x(*key_x, p[offset], p[moffset]);
calc_y(*key_y, p[offset], p[moffset]);
return true;
case KEY_OPT_LE16:
calc_x(tmp_x_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
calc_y(tmp_y_s, *(u16 *)(&p[offset]), *(u16 *)(&p[moffset]));
*(__le16 *)key_x = cpu_to_le16(tmp_x_s);
*(__le16 *)key_y = cpu_to_le16(tmp_y_s);
return true;
case KEY_OPT_LE32:
calc_x(tmp_x_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
calc_y(tmp_y_l, *(u32 *)(&p[offset]), *(u32 *)(&p[moffset]));
*(__le32 *)key_x = cpu_to_le32(tmp_x_l);
*(__le32 *)key_y = cpu_to_le32(tmp_y_l);
return true;
case KEY_OPT_MAC:
for (i = 0; i < ETH_ALEN; i++) {
calc_x(key_x[ETH_ALEN - 1 - i], p[offset + i],
p[moffset + i]);
calc_y(key_y[ETH_ALEN - 1 - i], p[offset + i],
p[moffset + i]);
}
return true;
case KEY_OPT_IP:
ip_offset = IPV4_INDEX * sizeof(u32);
calc_x(tmp_x_l, *(u32 *)(&p[offset + ip_offset]),
*(u32 *)(&p[moffset + ip_offset]));
calc_y(tmp_y_l, *(u32 *)(&p[offset + ip_offset]),
*(u32 *)(&p[moffset + ip_offset]));
*(__le32 *)key_x = cpu_to_le32(tmp_x_l);
*(__le32 *)key_y = cpu_to_le32(tmp_y_l);
return true;
default:
return false;
}
}
static u32 hclge_get_port_number(enum HLCGE_PORT_TYPE port_type, u8 pf_id,
u8 vf_id, u8 network_port_id)
{
u32 port_number = 0;
if (port_type == HOST_PORT) {
hnae3_set_field(port_number, HCLGE_PF_ID_M, HCLGE_PF_ID_S,
pf_id);
hnae3_set_field(port_number, HCLGE_VF_ID_M, HCLGE_VF_ID_S,
vf_id);
hnae3_set_bit(port_number, HCLGE_PORT_TYPE_B, HOST_PORT);
} else {
hnae3_set_field(port_number, HCLGE_NETWORK_PORT_ID_M,
HCLGE_NETWORK_PORT_ID_S, network_port_id);
hnae3_set_bit(port_number, HCLGE_PORT_TYPE_B, NETWORK_PORT);
}
return port_number;
}
static void hclge_fd_convert_meta_data(struct hclge_fd_key_cfg *key_cfg,
__le32 *key_x, __le32 *key_y,
struct hclge_fd_rule *rule)
{
u32 tuple_bit, meta_data = 0, tmp_x, tmp_y, port_number;
u8 cur_pos = 0, tuple_size, shift_bits;
unsigned int i;
for (i = 0; i < MAX_META_DATA; i++) {
tuple_size = meta_data_key_info[i].key_length;
tuple_bit = key_cfg->meta_data_active & BIT(i);
switch (tuple_bit) {
case BIT(ROCE_TYPE):
hnae3_set_bit(meta_data, cur_pos, NIC_PACKET);
cur_pos += tuple_size;
break;
case BIT(DST_VPORT):
port_number = hclge_get_port_number(HOST_PORT, 0,
rule->vf_id, 0);
hnae3_set_field(meta_data,
GENMASK(cur_pos + tuple_size, cur_pos),
cur_pos, port_number);
cur_pos += tuple_size;
break;
default:
break;
}
}
calc_x(tmp_x, meta_data, 0xFFFFFFFF);
calc_y(tmp_y, meta_data, 0xFFFFFFFF);
shift_bits = sizeof(meta_data) * 8 - cur_pos;
*key_x = cpu_to_le32(tmp_x << shift_bits);
*key_y = cpu_to_le32(tmp_y << shift_bits);
}
/* A complete key is combined with meta data key and tuple key.
* Meta data key is stored at the MSB region, and tuple key is stored at
* the LSB region, unused bits will be filled 0.
*/
static int hclge_config_key(struct hclge_dev *hdev, u8 stage,
struct hclge_fd_rule *rule)
{
struct hclge_fd_key_cfg *key_cfg = &hdev->fd_cfg.key_cfg[stage];
u8 key_x[MAX_KEY_BYTES], key_y[MAX_KEY_BYTES];
u8 *cur_key_x, *cur_key_y;
u8 meta_data_region;
u8 tuple_size;
int ret;
u32 i;
memset(key_x, 0, sizeof(key_x));
memset(key_y, 0, sizeof(key_y));
cur_key_x = key_x;
cur_key_y = key_y;
for (i = 0; i < MAX_TUPLE; i++) {
bool tuple_valid;
tuple_size = tuple_key_info[i].key_length / 8;
if (!(key_cfg->tuple_active & BIT(i)))
continue;
tuple_valid = hclge_fd_convert_tuple(i, cur_key_x,
cur_key_y, rule);
if (tuple_valid) {
cur_key_x += tuple_size;
cur_key_y += tuple_size;
}
}
meta_data_region = hdev->fd_cfg.max_key_length / 8 -
MAX_META_DATA_LENGTH / 8;
hclge_fd_convert_meta_data(key_cfg,
(__le32 *)(key_x + meta_data_region),
(__le32 *)(key_y + meta_data_region),
rule);
ret = hclge_fd_tcam_config(hdev, stage, false, rule->location, key_y,
true);
if (ret) {
dev_err(&hdev->pdev->dev,
"fd key_y config fail, loc=%u, ret=%d\n",
rule->queue_id, ret);
return ret;
}
ret = hclge_fd_tcam_config(hdev, stage, true, rule->location, key_x,
true);
if (ret)
dev_err(&hdev->pdev->dev,
"fd key_x config fail, loc=%u, ret=%d\n",
rule->queue_id, ret);
return ret;
}
static int hclge_config_action(struct hclge_dev *hdev, u8 stage,
struct hclge_fd_rule *rule)
{
struct hclge_vport *vport = hdev->vport;
struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
struct hclge_fd_ad_data ad_data;
memset(&ad_data, 0, sizeof(struct hclge_fd_ad_data));
ad_data.ad_id = rule->location;
if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
ad_data.drop_packet = true;
} else if (rule->action == HCLGE_FD_ACTION_SELECT_TC) {
ad_data.override_tc = true;
ad_data.queue_id =
kinfo->tc_info.tqp_offset[rule->cls_flower.tc];
ad_data.tc_size =
ilog2(kinfo->tc_info.tqp_count[rule->cls_flower.tc]);
} else {
ad_data.forward_to_direct_queue = true;
ad_data.queue_id = rule->queue_id;
}
if (hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1]) {
ad_data.use_counter = true;
ad_data.counter_id = rule->vf_id %
hdev->fd_cfg.cnt_num[HCLGE_FD_STAGE_1];
} else {
ad_data.use_counter = false;
ad_data.counter_id = 0;
}
ad_data.use_next_stage = false;
ad_data.next_input_key = 0;
ad_data.write_rule_id_to_bd = true;
ad_data.rule_id = rule->location;
return hclge_fd_ad_config(hdev, stage, ad_data.ad_id, &ad_data);
}
static int hclge_fd_check_tcpip4_tuple(struct ethtool_tcpip4_spec *spec,
u32 *unused_tuple)
{
if (!spec || !unused_tuple)
return -EINVAL;
*unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
if (!spec->ip4src)
*unused_tuple |= BIT(INNER_SRC_IP);
if (!spec->ip4dst)
*unused_tuple |= BIT(INNER_DST_IP);
if (!spec->psrc)
*unused_tuple |= BIT(INNER_SRC_PORT);
if (!spec->pdst)
*unused_tuple |= BIT(INNER_DST_PORT);
if (!spec->tos)
*unused_tuple |= BIT(INNER_IP_TOS);
return 0;
}
static int hclge_fd_check_ip4_tuple(struct ethtool_usrip4_spec *spec,
u32 *unused_tuple)
{
if (!spec || !unused_tuple)
return -EINVAL;
*unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
if (!spec->ip4src)
*unused_tuple |= BIT(INNER_SRC_IP);
if (!spec->ip4dst)
*unused_tuple |= BIT(INNER_DST_IP);
if (!spec->tos)
*unused_tuple |= BIT(INNER_IP_TOS);
if (!spec->proto)
*unused_tuple |= BIT(INNER_IP_PROTO);
if (spec->l4_4_bytes)
return -EOPNOTSUPP;
if (spec->ip_ver != ETH_RX_NFC_IP4)
return -EOPNOTSUPP;
return 0;
}
static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec,
u32 *unused_tuple)
{
if (!spec || !unused_tuple)
return -EINVAL;
*unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
/* check whether src/dst ip address used */
if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
*unused_tuple |= BIT(INNER_SRC_IP);
if (ipv6_addr_any((struct in6_addr *)spec->ip6dst))
*unused_tuple |= BIT(INNER_DST_IP);
if (!spec->psrc)
*unused_tuple |= BIT(INNER_SRC_PORT);
if (!spec->pdst)
*unused_tuple |= BIT(INNER_DST_PORT);
if (!spec->tclass)
*unused_tuple |= BIT(INNER_IP_TOS);
return 0;
}
static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec,
u32 *unused_tuple)
{
if (!spec || !unused_tuple)
return -EINVAL;
*unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
/* check whether src/dst ip address used */
if (ipv6_addr_any((struct in6_addr *)spec->ip6src))
*unused_tuple |= BIT(INNER_SRC_IP);
if (ipv6_addr_any((struct in6_addr *)spec->ip6dst))
*unused_tuple |= BIT(INNER_DST_IP);
if (!spec->l4_proto)
*unused_tuple |= BIT(INNER_IP_PROTO);
if (!spec->tclass)
*unused_tuple |= BIT(INNER_IP_TOS);
if (spec->l4_4_bytes)
return -EOPNOTSUPP;
return 0;
}
static int hclge_fd_check_ether_tuple(struct ethhdr *spec, u32 *unused_tuple)
{
if (!spec || !unused_tuple)
return -EINVAL;
*unused_tuple |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) |
BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO);
if (is_zero_ether_addr(spec->h_source))
*unused_tuple |= BIT(INNER_SRC_MAC);
if (is_zero_ether_addr(spec->h_dest))
*unused_tuple |= BIT(INNER_DST_MAC);
if (!spec->h_proto)
*unused_tuple |= BIT(INNER_ETH_TYPE);
return 0;
}
static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev,
struct ethtool_rx_flow_spec *fs,
u32 *unused_tuple)
{
if (fs->flow_type & FLOW_EXT) {
if (fs->h_ext.vlan_etype) {
dev_err(&hdev->pdev->dev, "vlan-etype is not supported!\n");
return -EOPNOTSUPP;
}
if (!fs->h_ext.vlan_tci)
*unused_tuple |= BIT(INNER_VLAN_TAG_FST);
if (fs->m_ext.vlan_tci &&
be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) {
dev_err(&hdev->pdev->dev,
"failed to config vlan_tci, invalid vlan_tci: %u, max is %d.\n",
ntohs(fs->h_ext.vlan_tci), VLAN_N_VID - 1);
return -EINVAL;
}
} else {
*unused_tuple |= BIT(INNER_VLAN_TAG_FST);
}
if (fs->flow_type & FLOW_MAC_EXT) {
if (hdev->fd_cfg.fd_mode !=
HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
dev_err(&hdev->pdev->dev,
"FLOW_MAC_EXT is not supported in current fd mode!\n");
return -EOPNOTSUPP;
}
if (is_zero_ether_addr(fs->h_ext.h_dest))
*unused_tuple |= BIT(INNER_DST_MAC);
else
*unused_tuple &= ~BIT(INNER_DST_MAC);
}
return 0;
}
static int hclge_fd_get_user_def_layer(u32 flow_type, u32 *unused_tuple,
struct hclge_fd_user_def_info *info)
{
switch (flow_type) {
case ETHER_FLOW:
info->layer = HCLGE_FD_USER_DEF_L2;
*unused_tuple &= ~BIT(INNER_L2_RSV);
break;
case IP_USER_FLOW:
case IPV6_USER_FLOW:
info->layer = HCLGE_FD_USER_DEF_L3;
*unused_tuple &= ~BIT(INNER_L3_RSV);
break;
case TCP_V4_FLOW:
case UDP_V4_FLOW:
case TCP_V6_FLOW:
case UDP_V6_FLOW:
info->layer = HCLGE_FD_USER_DEF_L4;
*unused_tuple &= ~BIT(INNER_L4_RSV);
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static bool hclge_fd_is_user_def_all_masked(struct ethtool_rx_flow_spec *fs)
{
return be32_to_cpu(fs->m_ext.data[1] | fs->m_ext.data[0]) == 0;
}
static int hclge_fd_parse_user_def_field(struct hclge_dev *hdev,
struct ethtool_rx_flow_spec *fs,
u32 *unused_tuple,
struct hclge_fd_user_def_info *info)
{
u32 tuple_active = hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1].tuple_active;
u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
u16 data, offset, data_mask, offset_mask;
int ret;
info->layer = HCLGE_FD_USER_DEF_NONE;
*unused_tuple |= HCLGE_FD_TUPLE_USER_DEF_TUPLES;
if (!(fs->flow_type & FLOW_EXT) || hclge_fd_is_user_def_all_masked(fs))
return 0;
/* user-def data from ethtool is 64 bit value, the bit0~15 is used
* for data, and bit32~47 is used for offset.
*/
data = be32_to_cpu(fs->h_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
data_mask = be32_to_cpu(fs->m_ext.data[1]) & HCLGE_FD_USER_DEF_DATA;
offset = be32_to_cpu(fs->h_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
offset_mask = be32_to_cpu(fs->m_ext.data[0]) & HCLGE_FD_USER_DEF_OFFSET;
if (!(tuple_active & HCLGE_FD_TUPLE_USER_DEF_TUPLES)) {
dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
return -EOPNOTSUPP;
}
if (offset > HCLGE_FD_MAX_USER_DEF_OFFSET) {
dev_err(&hdev->pdev->dev,
"user-def offset[%u] should be no more than %u\n",
offset, HCLGE_FD_MAX_USER_DEF_OFFSET);
return -EINVAL;
}
if (offset_mask != HCLGE_FD_USER_DEF_OFFSET_UNMASK) {
dev_err(&hdev->pdev->dev, "user-def offset can't be masked\n");
return -EINVAL;
}
ret = hclge_fd_get_user_def_layer(flow_type, unused_tuple, info);
if (ret) {
dev_err(&hdev->pdev->dev,
"unsupported flow type for user-def bytes, ret = %d\n",
ret);
return ret;
}
info->data = data;
info->data_mask = data_mask;
info->offset = offset;
return 0;
}
static int hclge_fd_check_spec(struct hclge_dev *hdev,
struct ethtool_rx_flow_spec *fs,
u32 *unused_tuple,
struct hclge_fd_user_def_info *info)
{
u32 flow_type;
int ret;
if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
dev_err(&hdev->pdev->dev,
"failed to config fd rules, invalid rule location: %u, max is %u\n.",
fs->location,
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1] - 1);
return -EINVAL;
}
ret = hclge_fd_parse_user_def_field(hdev, fs, unused_tuple, info);
if (ret)
return ret;
flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
switch (flow_type) {
case SCTP_V4_FLOW:
case TCP_V4_FLOW:
case UDP_V4_FLOW:
ret = hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec,
unused_tuple);
break;
case IP_USER_FLOW:
ret = hclge_fd_check_ip4_tuple(&fs->h_u.usr_ip4_spec,
unused_tuple);
break;
case SCTP_V6_FLOW:
case TCP_V6_FLOW:
case UDP_V6_FLOW:
ret = hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec,
unused_tuple);
break;
case IPV6_USER_FLOW:
ret = hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec,
unused_tuple);
break;
case ETHER_FLOW:
if (hdev->fd_cfg.fd_mode !=
HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
dev_err(&hdev->pdev->dev,
"ETHER_FLOW is not supported in current fd mode!\n");
return -EOPNOTSUPP;
}
ret = hclge_fd_check_ether_tuple(&fs->h_u.ether_spec,
unused_tuple);
break;
default:
dev_err(&hdev->pdev->dev,
"unsupported protocol type, protocol type = %#x\n",
flow_type);
return -EOPNOTSUPP;
}
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to check flow union tuple, ret = %d\n",
ret);
return ret;
}
return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple);
}
static void hclge_fd_get_tcpip4_tuple(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule, u8 ip_proto)
{
rule->tuples.src_ip[IPV4_INDEX] =
be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4src);
rule->tuples_mask.src_ip[IPV4_INDEX] =
be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4src);
rule->tuples.dst_ip[IPV4_INDEX] =
be32_to_cpu(fs->h_u.tcp_ip4_spec.ip4dst);
rule->tuples_mask.dst_ip[IPV4_INDEX] =
be32_to_cpu(fs->m_u.tcp_ip4_spec.ip4dst);
rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.psrc);
rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.psrc);
rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip4_spec.pdst);
rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip4_spec.pdst);
rule->tuples.ip_tos = fs->h_u.tcp_ip4_spec.tos;
rule->tuples_mask.ip_tos = fs->m_u.tcp_ip4_spec.tos;
rule->tuples.ether_proto = ETH_P_IP;
rule->tuples_mask.ether_proto = 0xFFFF;
rule->tuples.ip_proto = ip_proto;
rule->tuples_mask.ip_proto = 0xFF;
}
static void hclge_fd_get_ip4_tuple(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
rule->tuples.src_ip[IPV4_INDEX] =
be32_to_cpu(fs->h_u.usr_ip4_spec.ip4src);
rule->tuples_mask.src_ip[IPV4_INDEX] =
be32_to_cpu(fs->m_u.usr_ip4_spec.ip4src);
rule->tuples.dst_ip[IPV4_INDEX] =
be32_to_cpu(fs->h_u.usr_ip4_spec.ip4dst);
rule->tuples_mask.dst_ip[IPV4_INDEX] =
be32_to_cpu(fs->m_u.usr_ip4_spec.ip4dst);
rule->tuples.ip_tos = fs->h_u.usr_ip4_spec.tos;
rule->tuples_mask.ip_tos = fs->m_u.usr_ip4_spec.tos;
rule->tuples.ip_proto = fs->h_u.usr_ip4_spec.proto;
rule->tuples_mask.ip_proto = fs->m_u.usr_ip4_spec.proto;
rule->tuples.ether_proto = ETH_P_IP;
rule->tuples_mask.ether_proto = 0xFFFF;
}
static void hclge_fd_get_tcpip6_tuple(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule, u8 ip_proto)
{
be32_to_cpu_array(rule->tuples.src_ip, fs->h_u.tcp_ip6_spec.ip6src,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples_mask.src_ip, fs->m_u.tcp_ip6_spec.ip6src,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples.dst_ip, fs->h_u.tcp_ip6_spec.ip6dst,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples_mask.dst_ip, fs->m_u.tcp_ip6_spec.ip6dst,
IPV6_SIZE);
rule->tuples.src_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.psrc);
rule->tuples_mask.src_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.psrc);
rule->tuples.dst_port = be16_to_cpu(fs->h_u.tcp_ip6_spec.pdst);
rule->tuples_mask.dst_port = be16_to_cpu(fs->m_u.tcp_ip6_spec.pdst);
rule->tuples.ether_proto = ETH_P_IPV6;
rule->tuples_mask.ether_proto = 0xFFFF;
rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
rule->tuples.ip_proto = ip_proto;
rule->tuples_mask.ip_proto = 0xFF;
}
static void hclge_fd_get_ip6_tuple(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
be32_to_cpu_array(rule->tuples.src_ip, fs->h_u.usr_ip6_spec.ip6src,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples_mask.src_ip, fs->m_u.usr_ip6_spec.ip6src,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples.dst_ip, fs->h_u.usr_ip6_spec.ip6dst,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples_mask.dst_ip, fs->m_u.usr_ip6_spec.ip6dst,
IPV6_SIZE);
rule->tuples.ip_proto = fs->h_u.usr_ip6_spec.l4_proto;
rule->tuples_mask.ip_proto = fs->m_u.usr_ip6_spec.l4_proto;
rule->tuples.ip_tos = fs->h_u.tcp_ip6_spec.tclass;
rule->tuples_mask.ip_tos = fs->m_u.tcp_ip6_spec.tclass;
rule->tuples.ether_proto = ETH_P_IPV6;
rule->tuples_mask.ether_proto = 0xFFFF;
}
static void hclge_fd_get_ether_tuple(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
ether_addr_copy(rule->tuples.src_mac, fs->h_u.ether_spec.h_source);
ether_addr_copy(rule->tuples_mask.src_mac, fs->m_u.ether_spec.h_source);
ether_addr_copy(rule->tuples.dst_mac, fs->h_u.ether_spec.h_dest);
ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_u.ether_spec.h_dest);
rule->tuples.ether_proto = be16_to_cpu(fs->h_u.ether_spec.h_proto);
rule->tuples_mask.ether_proto = be16_to_cpu(fs->m_u.ether_spec.h_proto);
}
static void hclge_fd_get_user_def_tuple(struct hclge_fd_user_def_info *info,
struct hclge_fd_rule *rule)
{
switch (info->layer) {
case HCLGE_FD_USER_DEF_L2:
rule->tuples.l2_user_def = info->data;
rule->tuples_mask.l2_user_def = info->data_mask;
break;
case HCLGE_FD_USER_DEF_L3:
rule->tuples.l3_user_def = info->data;
rule->tuples_mask.l3_user_def = info->data_mask;
break;
case HCLGE_FD_USER_DEF_L4:
rule->tuples.l4_user_def = (u32)info->data << 16;
rule->tuples_mask.l4_user_def = (u32)info->data_mask << 16;
break;
default:
break;
}
rule->ep.user_def = *info;
}
static int hclge_fd_get_tuple(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule,
struct hclge_fd_user_def_info *info)
{
u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
switch (flow_type) {
case SCTP_V4_FLOW:
hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_SCTP);
break;
case TCP_V4_FLOW:
hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_TCP);
break;
case UDP_V4_FLOW:
hclge_fd_get_tcpip4_tuple(fs, rule, IPPROTO_UDP);
break;
case IP_USER_FLOW:
hclge_fd_get_ip4_tuple(fs, rule);
break;
case SCTP_V6_FLOW:
hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_SCTP);
break;
case TCP_V6_FLOW:
hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_TCP);
break;
case UDP_V6_FLOW:
hclge_fd_get_tcpip6_tuple(fs, rule, IPPROTO_UDP);
break;
case IPV6_USER_FLOW:
hclge_fd_get_ip6_tuple(fs, rule);
break;
case ETHER_FLOW:
hclge_fd_get_ether_tuple(fs, rule);
break;
default:
return -EOPNOTSUPP;
}
if (fs->flow_type & FLOW_EXT) {
rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci);
rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci);
hclge_fd_get_user_def_tuple(info, rule);
}
if (fs->flow_type & FLOW_MAC_EXT) {
ether_addr_copy(rule->tuples.dst_mac, fs->h_ext.h_dest);
ether_addr_copy(rule->tuples_mask.dst_mac, fs->m_ext.h_dest);
}
return 0;
}
static int hclge_fd_config_rule(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
int ret;
ret = hclge_config_action(hdev, HCLGE_FD_STAGE_1, rule);
if (ret)
return ret;
return hclge_config_key(hdev, HCLGE_FD_STAGE_1, rule);
}
static int hclge_add_fd_entry_common(struct hclge_dev *hdev,
struct hclge_fd_rule *rule)
{
int ret;
spin_lock_bh(&hdev->fd_rule_lock);
if (hdev->fd_active_type != rule->rule_type &&
(hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
hdev->fd_active_type == HCLGE_FD_EP_ACTIVE)) {
dev_err(&hdev->pdev->dev,
"mode conflict(new type %d, active type %d), please delete existent rules first\n",
rule->rule_type, hdev->fd_active_type);
spin_unlock_bh(&hdev->fd_rule_lock);
return -EINVAL;
}
ret = hclge_fd_check_user_def_refcnt(hdev, rule);
if (ret)
goto out;
ret = hclge_clear_arfs_rules(hdev);
if (ret)
goto out;
ret = hclge_fd_config_rule(hdev, rule);
if (ret)
goto out;
rule->state = HCLGE_FD_ACTIVE;
hdev->fd_active_type = rule->rule_type;
hclge_update_fd_list(hdev, rule->state, rule->location, rule);
out:
spin_unlock_bh(&hdev->fd_rule_lock);
return ret;
}
static bool hclge_is_cls_flower_active(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE;
}
static int hclge_fd_parse_ring_cookie(struct hclge_dev *hdev, u64 ring_cookie,
u16 *vport_id, u8 *action, u16 *queue_id)
{
struct hclge_vport *vport = hdev->vport;
if (ring_cookie == RX_CLS_FLOW_DISC) {
*action = HCLGE_FD_ACTION_DROP_PACKET;
} else {
u32 ring = ethtool_get_flow_spec_ring(ring_cookie);
u8 vf = ethtool_get_flow_spec_ring_vf(ring_cookie);
u16 tqps;
/* To keep consistent with user's configuration, minus 1 when
* printing 'vf', because vf id from ethtool is added 1 for vf.
*/
if (vf > hdev->num_req_vfs) {
dev_err(&hdev->pdev->dev,
"Error: vf id (%u) should be less than %u\n",
vf - 1U, hdev->num_req_vfs);
return -EINVAL;
}
*vport_id = vf ? hdev->vport[vf].vport_id : vport->vport_id;
tqps = hdev->vport[vf].nic.kinfo.num_tqps;
if (ring >= tqps) {
dev_err(&hdev->pdev->dev,
"Error: queue id (%u) > max tqp num (%u)\n",
ring, tqps - 1U);
return -EINVAL;
}
*action = HCLGE_FD_ACTION_SELECT_QUEUE;
*queue_id = ring;
}
return 0;
}
static int hclge_add_fd_entry(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_fd_user_def_info info;
u16 dst_vport_id = 0, q_index = 0;
struct ethtool_rx_flow_spec *fs;
struct hclge_fd_rule *rule;
u32 unused = 0;
u8 action;
int ret;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
dev_err(&hdev->pdev->dev,
"flow table director is not supported\n");
return -EOPNOTSUPP;
}
if (!hdev->fd_en) {
dev_err(&hdev->pdev->dev,
"please enable flow director first\n");
return -EOPNOTSUPP;
}
fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
ret = hclge_fd_check_spec(hdev, fs, &unused, &info);
if (ret)
return ret;
ret = hclge_fd_parse_ring_cookie(hdev, fs->ring_cookie, &dst_vport_id,
&action, &q_index);
if (ret)
return ret;
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
ret = hclge_fd_get_tuple(fs, rule, &info);
if (ret) {
kfree(rule);
return ret;
}
rule->flow_type = fs->flow_type;
rule->location = fs->location;
rule->unused_tuple = unused;
rule->vf_id = dst_vport_id;
rule->queue_id = q_index;
rule->action = action;
rule->rule_type = HCLGE_FD_EP_ACTIVE;
ret = hclge_add_fd_entry_common(hdev, rule);
if (ret)
kfree(rule);
return ret;
}
static int hclge_del_fd_entry(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct ethtool_rx_flow_spec *fs;
int ret;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return -EOPNOTSUPP;
fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
return -EINVAL;
spin_lock_bh(&hdev->fd_rule_lock);
if (hdev->fd_active_type == HCLGE_FD_TC_FLOWER_ACTIVE ||
!test_bit(fs->location, hdev->fd_bmap)) {
dev_err(&hdev->pdev->dev,
"Delete fail, rule %u is inexistent\n", fs->location);
spin_unlock_bh(&hdev->fd_rule_lock);
return -ENOENT;
}
ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, fs->location,
NULL, false);
if (ret)
goto out;
hclge_update_fd_list(hdev, HCLGE_FD_DELETED, fs->location, NULL);
out:
spin_unlock_bh(&hdev->fd_rule_lock);
return ret;
}
static void hclge_clear_fd_rules_in_list(struct hclge_dev *hdev,
bool clear_list)
{
struct hclge_fd_rule *rule;
struct hlist_node *node;
u16 location;
spin_lock_bh(&hdev->fd_rule_lock);
for_each_set_bit(location, hdev->fd_bmap,
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, location,
NULL, false);
if (clear_list) {
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list,
rule_node) {
hlist_del(&rule->rule_node);
kfree(rule);
}
hdev->fd_active_type = HCLGE_FD_RULE_NONE;
hdev->hclge_fd_rule_num = 0;
bitmap_zero(hdev->fd_bmap,
hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
}
spin_unlock_bh(&hdev->fd_rule_lock);
}
static void hclge_del_all_fd_entries(struct hclge_dev *hdev)
{
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return;
hclge_clear_fd_rules_in_list(hdev, true);
hclge_fd_disable_user_def(hdev);
}
static int hclge_restore_fd_entries(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
struct hlist_node *node;
/* Return ok here, because reset error handling will check this
* return value. If error is returned here, the reset process will
* fail.
*/
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return 0;
/* if fd is disabled, should not restore it when reset */
if (!hdev->fd_en)
return 0;
spin_lock_bh(&hdev->fd_rule_lock);
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
if (rule->state == HCLGE_FD_ACTIVE)
rule->state = HCLGE_FD_TO_ADD;
}
spin_unlock_bh(&hdev->fd_rule_lock);
set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
return 0;
}
static int hclge_get_fd_rule_cnt(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev) || hclge_is_cls_flower_active(handle))
return -EOPNOTSUPP;
cmd->rule_cnt = hdev->hclge_fd_rule_num;
cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
return 0;
}
static void hclge_fd_get_tcpip4_info(struct hclge_fd_rule *rule,
struct ethtool_tcpip4_spec *spec,
struct ethtool_tcpip4_spec *spec_mask)
{
spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
spec->psrc = cpu_to_be16(rule->tuples.src_port);
spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
0 : cpu_to_be16(rule->tuples_mask.src_port);
spec->pdst = cpu_to_be16(rule->tuples.dst_port);
spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
0 : cpu_to_be16(rule->tuples_mask.dst_port);
spec->tos = rule->tuples.ip_tos;
spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
0 : rule->tuples_mask.ip_tos;
}
static void hclge_fd_get_ip4_info(struct hclge_fd_rule *rule,
struct ethtool_usrip4_spec *spec,
struct ethtool_usrip4_spec *spec_mask)
{
spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
spec->tos = rule->tuples.ip_tos;
spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
0 : rule->tuples_mask.ip_tos;
spec->proto = rule->tuples.ip_proto;
spec_mask->proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
0 : rule->tuples_mask.ip_proto;
spec->ip_ver = ETH_RX_NFC_IP4;
}
static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule,
struct ethtool_tcpip6_spec *spec,
struct ethtool_tcpip6_spec *spec_mask)
{
cpu_to_be32_array(spec->ip6src,
rule->tuples.src_ip, IPV6_SIZE);
cpu_to_be32_array(spec->ip6dst,
rule->tuples.dst_ip, IPV6_SIZE);
if (rule->unused_tuple & BIT(INNER_SRC_IP))
memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
else
cpu_to_be32_array(spec_mask->ip6src, rule->tuples_mask.src_ip,
IPV6_SIZE);
if (rule->unused_tuple & BIT(INNER_DST_IP))
memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
else
cpu_to_be32_array(spec_mask->ip6dst, rule->tuples_mask.dst_ip,
IPV6_SIZE);
spec->tclass = rule->tuples.ip_tos;
spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
0 : rule->tuples_mask.ip_tos;
spec->psrc = cpu_to_be16(rule->tuples.src_port);
spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
0 : cpu_to_be16(rule->tuples_mask.src_port);
spec->pdst = cpu_to_be16(rule->tuples.dst_port);
spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
0 : cpu_to_be16(rule->tuples_mask.dst_port);
}
static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule,
struct ethtool_usrip6_spec *spec,
struct ethtool_usrip6_spec *spec_mask)
{
cpu_to_be32_array(spec->ip6src, rule->tuples.src_ip, IPV6_SIZE);
cpu_to_be32_array(spec->ip6dst, rule->tuples.dst_ip, IPV6_SIZE);
if (rule->unused_tuple & BIT(INNER_SRC_IP))
memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
else
cpu_to_be32_array(spec_mask->ip6src,
rule->tuples_mask.src_ip, IPV6_SIZE);
if (rule->unused_tuple & BIT(INNER_DST_IP))
memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
else
cpu_to_be32_array(spec_mask->ip6dst,
rule->tuples_mask.dst_ip, IPV6_SIZE);
spec->tclass = rule->tuples.ip_tos;
spec_mask->tclass = rule->unused_tuple & BIT(INNER_IP_TOS) ?
0 : rule->tuples_mask.ip_tos;
spec->l4_proto = rule->tuples.ip_proto;
spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
0 : rule->tuples_mask.ip_proto;
}
static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule,
struct ethhdr *spec,
struct ethhdr *spec_mask)
{
ether_addr_copy(spec->h_source, rule->tuples.src_mac);
ether_addr_copy(spec->h_dest, rule->tuples.dst_mac);
if (rule->unused_tuple & BIT(INNER_SRC_MAC))
eth_zero_addr(spec_mask->h_source);
else
ether_addr_copy(spec_mask->h_source, rule->tuples_mask.src_mac);
if (rule->unused_tuple & BIT(INNER_DST_MAC))
eth_zero_addr(spec_mask->h_dest);
else
ether_addr_copy(spec_mask->h_dest, rule->tuples_mask.dst_mac);
spec->h_proto = cpu_to_be16(rule->tuples.ether_proto);
spec_mask->h_proto = rule->unused_tuple & BIT(INNER_ETH_TYPE) ?
0 : cpu_to_be16(rule->tuples_mask.ether_proto);
}
static void hclge_fd_get_user_def_info(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
if ((rule->unused_tuple & HCLGE_FD_TUPLE_USER_DEF_TUPLES) ==
HCLGE_FD_TUPLE_USER_DEF_TUPLES) {
fs->h_ext.data[0] = 0;
fs->h_ext.data[1] = 0;
fs->m_ext.data[0] = 0;
fs->m_ext.data[1] = 0;
} else {
fs->h_ext.data[0] = cpu_to_be32(rule->ep.user_def.offset);
fs->h_ext.data[1] = cpu_to_be32(rule->ep.user_def.data);
fs->m_ext.data[0] =
cpu_to_be32(HCLGE_FD_USER_DEF_OFFSET_UNMASK);
fs->m_ext.data[1] = cpu_to_be32(rule->ep.user_def.data_mask);
}
}
static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
if (fs->flow_type & FLOW_EXT) {
fs->h_ext.vlan_tci = cpu_to_be16(rule->tuples.vlan_tag1);
fs->m_ext.vlan_tci =
rule->unused_tuple & BIT(INNER_VLAN_TAG_FST) ?
0 : cpu_to_be16(rule->tuples_mask.vlan_tag1);
hclge_fd_get_user_def_info(fs, rule);
}
if (fs->flow_type & FLOW_MAC_EXT) {
ether_addr_copy(fs->h_ext.h_dest, rule->tuples.dst_mac);
if (rule->unused_tuple & BIT(INNER_DST_MAC))
eth_zero_addr(fs->m_u.ether_spec.h_dest);
else
ether_addr_copy(fs->m_u.ether_spec.h_dest,
rule->tuples_mask.dst_mac);
}
}
static struct hclge_fd_rule *hclge_get_fd_rule(struct hclge_dev *hdev,
u16 location)
{
struct hclge_fd_rule *rule = NULL;
struct hlist_node *node2;
hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
if (rule->location == location)
return rule;
else if (rule->location > location)
return NULL;
}
return NULL;
}
static void hclge_fd_get_ring_cookie(struct ethtool_rx_flow_spec *fs,
struct hclge_fd_rule *rule)
{
if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
fs->ring_cookie = RX_CLS_FLOW_DISC;
} else {
u64 vf_id;
fs->ring_cookie = rule->queue_id;
vf_id = rule->vf_id;
vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
fs->ring_cookie |= vf_id;
}
}
static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_fd_rule *rule = NULL;
struct hclge_dev *hdev = vport->back;
struct ethtool_rx_flow_spec *fs;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return -EOPNOTSUPP;
fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
spin_lock_bh(&hdev->fd_rule_lock);
rule = hclge_get_fd_rule(hdev, fs->location);
if (!rule) {
spin_unlock_bh(&hdev->fd_rule_lock);
return -ENOENT;
}
fs->flow_type = rule->flow_type;
switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
case SCTP_V4_FLOW:
case TCP_V4_FLOW:
case UDP_V4_FLOW:
hclge_fd_get_tcpip4_info(rule, &fs->h_u.tcp_ip4_spec,
&fs->m_u.tcp_ip4_spec);
break;
case IP_USER_FLOW:
hclge_fd_get_ip4_info(rule, &fs->h_u.usr_ip4_spec,
&fs->m_u.usr_ip4_spec);
break;
case SCTP_V6_FLOW:
case TCP_V6_FLOW:
case UDP_V6_FLOW:
hclge_fd_get_tcpip6_info(rule, &fs->h_u.tcp_ip6_spec,
&fs->m_u.tcp_ip6_spec);
break;
case IPV6_USER_FLOW:
hclge_fd_get_ip6_info(rule, &fs->h_u.usr_ip6_spec,
&fs->m_u.usr_ip6_spec);
break;
/* The flow type of fd rule has been checked before adding in to rule
* list. As other flow types have been handled, it must be ETHER_FLOW
* for the default case
*/
default:
hclge_fd_get_ether_info(rule, &fs->h_u.ether_spec,
&fs->m_u.ether_spec);
break;
}
hclge_fd_get_ext_info(fs, rule);
hclge_fd_get_ring_cookie(fs, rule);
spin_unlock_bh(&hdev->fd_rule_lock);
return 0;
}
static int hclge_get_all_rules(struct hnae3_handle *handle,
struct ethtool_rxnfc *cmd, u32 *rule_locs)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
struct hlist_node *node2;
int cnt = 0;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return -EOPNOTSUPP;
cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
spin_lock_bh(&hdev->fd_rule_lock);
hlist_for_each_entry_safe(rule, node2,
&hdev->fd_rule_list, rule_node) {
if (cnt == cmd->rule_cnt) {
spin_unlock_bh(&hdev->fd_rule_lock);
return -EMSGSIZE;
}
if (rule->state == HCLGE_FD_TO_DEL)
continue;
rule_locs[cnt] = rule->location;
cnt++;
}
spin_unlock_bh(&hdev->fd_rule_lock);
cmd->rule_cnt = cnt;
return 0;
}
static void hclge_fd_get_flow_tuples(const struct flow_keys *fkeys,
struct hclge_fd_rule_tuples *tuples)
{
#define flow_ip6_src fkeys->addrs.v6addrs.src.in6_u.u6_addr32
#define flow_ip6_dst fkeys->addrs.v6addrs.dst.in6_u.u6_addr32
tuples->ether_proto = be16_to_cpu(fkeys->basic.n_proto);
tuples->ip_proto = fkeys->basic.ip_proto;
tuples->dst_port = be16_to_cpu(fkeys->ports.dst);
if (fkeys->basic.n_proto == htons(ETH_P_IP)) {
tuples->src_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.src);
tuples->dst_ip[3] = be32_to_cpu(fkeys->addrs.v4addrs.dst);
} else {
int i;
for (i = 0; i < IPV6_SIZE; i++) {
tuples->src_ip[i] = be32_to_cpu(flow_ip6_src[i]);
tuples->dst_ip[i] = be32_to_cpu(flow_ip6_dst[i]);
}
}
}
/* traverse all rules, check whether an existed rule has the same tuples */
static struct hclge_fd_rule *
hclge_fd_search_flow_keys(struct hclge_dev *hdev,
const struct hclge_fd_rule_tuples *tuples)
{
struct hclge_fd_rule *rule = NULL;
struct hlist_node *node;
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
if (!memcmp(tuples, &rule->tuples, sizeof(*tuples)))
return rule;
}
return NULL;
}
static void hclge_fd_build_arfs_rule(const struct hclge_fd_rule_tuples *tuples,
struct hclge_fd_rule *rule)
{
rule->unused_tuple = BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
BIT(INNER_VLAN_TAG_FST) | BIT(INNER_IP_TOS) |
BIT(INNER_SRC_PORT);
rule->action = 0;
rule->vf_id = 0;
rule->rule_type = HCLGE_FD_ARFS_ACTIVE;
rule->state = HCLGE_FD_TO_ADD;
if (tuples->ether_proto == ETH_P_IP) {
if (tuples->ip_proto == IPPROTO_TCP)
rule->flow_type = TCP_V4_FLOW;
else
rule->flow_type = UDP_V4_FLOW;
} else {
if (tuples->ip_proto == IPPROTO_TCP)
rule->flow_type = TCP_V6_FLOW;
else
rule->flow_type = UDP_V6_FLOW;
}
memcpy(&rule->tuples, tuples, sizeof(rule->tuples));
memset(&rule->tuples_mask, 0xFF, sizeof(rule->tuples_mask));
}
static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
u16 flow_id, struct flow_keys *fkeys)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_fd_rule_tuples new_tuples = {};
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
u16 bit_id;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return -EOPNOTSUPP;
/* when there is already fd rule existed add by user,
* arfs should not work
*/
spin_lock_bh(&hdev->fd_rule_lock);
if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE &&
hdev->fd_active_type != HCLGE_FD_RULE_NONE) {
spin_unlock_bh(&hdev->fd_rule_lock);
return -EOPNOTSUPP;
}
hclge_fd_get_flow_tuples(fkeys, &new_tuples);
/* check is there flow director filter existed for this flow,
* if not, create a new filter for it;
* if filter exist with different queue id, modify the filter;
* if filter exist with same queue id, do nothing
*/
rule = hclge_fd_search_flow_keys(hdev, &new_tuples);
if (!rule) {
bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM);
if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
spin_unlock_bh(&hdev->fd_rule_lock);
return -ENOSPC;
}
rule = kzalloc(sizeof(*rule), GFP_ATOMIC);
if (!rule) {
spin_unlock_bh(&hdev->fd_rule_lock);
return -ENOMEM;
}
rule->location = bit_id;
rule->arfs.flow_id = flow_id;
rule->queue_id = queue_id;
hclge_fd_build_arfs_rule(&new_tuples, rule);
hclge_update_fd_list(hdev, rule->state, rule->location, rule);
hdev->fd_active_type = HCLGE_FD_ARFS_ACTIVE;
} else if (rule->queue_id != queue_id) {
rule->queue_id = queue_id;
rule->state = HCLGE_FD_TO_ADD;
set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
hclge_task_schedule(hdev, 0);
}
spin_unlock_bh(&hdev->fd_rule_lock);
return rule->location;
}
static void hclge_rfs_filter_expire(struct hclge_dev *hdev)
{
#ifdef CONFIG_RFS_ACCEL
struct hnae3_handle *handle = &hdev->vport[0].nic;
struct hclge_fd_rule *rule;
struct hlist_node *node;
spin_lock_bh(&hdev->fd_rule_lock);
if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE) {
spin_unlock_bh(&hdev->fd_rule_lock);
return;
}
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
if (rule->state != HCLGE_FD_ACTIVE)
continue;
if (rps_may_expire_flow(handle->netdev, rule->queue_id,
rule->arfs.flow_id, rule->location)) {
rule->state = HCLGE_FD_TO_DEL;
set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
}
}
spin_unlock_bh(&hdev->fd_rule_lock);
#endif
}
/* make sure being called after lock up with fd_rule_lock */
static int hclge_clear_arfs_rules(struct hclge_dev *hdev)
{
#ifdef CONFIG_RFS_ACCEL
struct hclge_fd_rule *rule;
struct hlist_node *node;
int ret;
if (hdev->fd_active_type != HCLGE_FD_ARFS_ACTIVE)
return 0;
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
switch (rule->state) {
case HCLGE_FD_TO_DEL:
case HCLGE_FD_ACTIVE:
ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
rule->location, NULL, false);
if (ret)
return ret;
fallthrough;
case HCLGE_FD_TO_ADD:
hclge_fd_dec_rule_cnt(hdev, rule->location);
hlist_del(&rule->rule_node);
kfree(rule);
break;
default:
break;
}
}
hclge_sync_fd_state(hdev);
#endif
return 0;
}
static void hclge_get_cls_key_basic(const struct flow_rule *flow,
struct hclge_fd_rule *rule)
{
if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_BASIC)) {
struct flow_match_basic match;
u16 ethtype_key, ethtype_mask;
flow_rule_match_basic(flow, &match);
ethtype_key = ntohs(match.key->n_proto);
ethtype_mask = ntohs(match.mask->n_proto);
if (ethtype_key == ETH_P_ALL) {
ethtype_key = 0;
ethtype_mask = 0;
}
rule->tuples.ether_proto = ethtype_key;
rule->tuples_mask.ether_proto = ethtype_mask;
rule->tuples.ip_proto = match.key->ip_proto;
rule->tuples_mask.ip_proto = match.mask->ip_proto;
} else {
rule->unused_tuple |= BIT(INNER_IP_PROTO);
rule->unused_tuple |= BIT(INNER_ETH_TYPE);
}
}
static void hclge_get_cls_key_mac(const struct flow_rule *flow,
struct hclge_fd_rule *rule)
{
if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
struct flow_match_eth_addrs match;
flow_rule_match_eth_addrs(flow, &match);
ether_addr_copy(rule->tuples.dst_mac, match.key->dst);
ether_addr_copy(rule->tuples_mask.dst_mac, match.mask->dst);
ether_addr_copy(rule->tuples.src_mac, match.key->src);
ether_addr_copy(rule->tuples_mask.src_mac, match.mask->src);
} else {
rule->unused_tuple |= BIT(INNER_DST_MAC);
rule->unused_tuple |= BIT(INNER_SRC_MAC);
}
}
static void hclge_get_cls_key_vlan(const struct flow_rule *flow,
struct hclge_fd_rule *rule)
{
if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_VLAN)) {
struct flow_match_vlan match;
flow_rule_match_vlan(flow, &match);
rule->tuples.vlan_tag1 = match.key->vlan_id |
(match.key->vlan_priority << VLAN_PRIO_SHIFT);
rule->tuples_mask.vlan_tag1 = match.mask->vlan_id |
(match.mask->vlan_priority << VLAN_PRIO_SHIFT);
} else {
rule->unused_tuple |= BIT(INNER_VLAN_TAG_FST);
}
}
static void hclge_get_cls_key_ip(const struct flow_rule *flow,
struct hclge_fd_rule *rule)
{
u16 addr_type = 0;
if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_CONTROL)) {
struct flow_match_control match;
flow_rule_match_control(flow, &match);
addr_type = match.key->addr_type;
}
if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
struct flow_match_ipv4_addrs match;
flow_rule_match_ipv4_addrs(flow, &match);
rule->tuples.src_ip[IPV4_INDEX] = be32_to_cpu(match.key->src);
rule->tuples_mask.src_ip[IPV4_INDEX] =
be32_to_cpu(match.mask->src);
rule->tuples.dst_ip[IPV4_INDEX] = be32_to_cpu(match.key->dst);
rule->tuples_mask.dst_ip[IPV4_INDEX] =
be32_to_cpu(match.mask->dst);
} else if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
struct flow_match_ipv6_addrs match;
flow_rule_match_ipv6_addrs(flow, &match);
be32_to_cpu_array(rule->tuples.src_ip, match.key->src.s6_addr32,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples_mask.src_ip,
match.mask->src.s6_addr32, IPV6_SIZE);
be32_to_cpu_array(rule->tuples.dst_ip, match.key->dst.s6_addr32,
IPV6_SIZE);
be32_to_cpu_array(rule->tuples_mask.dst_ip,
match.mask->dst.s6_addr32, IPV6_SIZE);
} else {
rule->unused_tuple |= BIT(INNER_SRC_IP);
rule->unused_tuple |= BIT(INNER_DST_IP);
}
}
static void hclge_get_cls_key_port(const struct flow_rule *flow,
struct hclge_fd_rule *rule)
{
if (flow_rule_match_key(flow, FLOW_DISSECTOR_KEY_PORTS)) {
struct flow_match_ports match;
flow_rule_match_ports(flow, &match);
rule->tuples.src_port = be16_to_cpu(match.key->src);
rule->tuples_mask.src_port = be16_to_cpu(match.mask->src);
rule->tuples.dst_port = be16_to_cpu(match.key->dst);
rule->tuples_mask.dst_port = be16_to_cpu(match.mask->dst);
} else {
rule->unused_tuple |= BIT(INNER_SRC_PORT);
rule->unused_tuple |= BIT(INNER_DST_PORT);
}
}
static int hclge_parse_cls_flower(struct hclge_dev *hdev,
struct flow_cls_offload *cls_flower,
struct hclge_fd_rule *rule)
{
struct flow_rule *flow = flow_cls_offload_flow_rule(cls_flower);
struct flow_dissector *dissector = flow->match.dissector;
if (dissector->used_keys &
~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
BIT_ULL(FLOW_DISSECTOR_KEY_VLAN) |
BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
BIT_ULL(FLOW_DISSECTOR_KEY_PORTS))) {
dev_err(&hdev->pdev->dev, "unsupported key set: %#llx\n",
dissector->used_keys);
return -EOPNOTSUPP;
}
hclge_get_cls_key_basic(flow, rule);
hclge_get_cls_key_mac(flow, rule);
hclge_get_cls_key_vlan(flow, rule);
hclge_get_cls_key_ip(flow, rule);
hclge_get_cls_key_port(flow, rule);
return 0;
}
static int hclge_check_cls_flower(struct hclge_dev *hdev,
struct flow_cls_offload *cls_flower, int tc)
{
u32 prio = cls_flower->common.prio;
if (tc < 0 || tc > hdev->tc_max) {
dev_err(&hdev->pdev->dev, "invalid traffic class\n");
return -EINVAL;
}
if (prio == 0 ||
prio > hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
dev_err(&hdev->pdev->dev,
"prio %u should be in range[1, %u]\n",
prio, hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]);
return -EINVAL;
}
if (test_bit(prio - 1, hdev->fd_bmap)) {
dev_err(&hdev->pdev->dev, "prio %u is already used\n", prio);
return -EINVAL;
}
return 0;
}
static int hclge_add_cls_flower(struct hnae3_handle *handle,
struct flow_cls_offload *cls_flower,
int tc)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
int ret;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev)) {
dev_err(&hdev->pdev->dev,
"cls flower is not supported\n");
return -EOPNOTSUPP;
}
ret = hclge_check_cls_flower(hdev, cls_flower, tc);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to check cls flower params, ret = %d\n", ret);
return ret;
}
rule = kzalloc(sizeof(*rule), GFP_KERNEL);
if (!rule)
return -ENOMEM;
ret = hclge_parse_cls_flower(hdev, cls_flower, rule);
if (ret) {
kfree(rule);
return ret;
}
rule->action = HCLGE_FD_ACTION_SELECT_TC;
rule->cls_flower.tc = tc;
rule->location = cls_flower->common.prio - 1;
rule->vf_id = 0;
rule->cls_flower.cookie = cls_flower->cookie;
rule->rule_type = HCLGE_FD_TC_FLOWER_ACTIVE;
ret = hclge_add_fd_entry_common(hdev, rule);
if (ret)
kfree(rule);
return ret;
}
static struct hclge_fd_rule *hclge_find_cls_flower(struct hclge_dev *hdev,
unsigned long cookie)
{
struct hclge_fd_rule *rule;
struct hlist_node *node;
hlist_for_each_entry_safe(rule, node, &hdev->fd_rule_list, rule_node) {
if (rule->cls_flower.cookie == cookie)
return rule;
}
return NULL;
}
static int hclge_del_cls_flower(struct hnae3_handle *handle,
struct flow_cls_offload *cls_flower)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_fd_rule *rule;
int ret;
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return -EOPNOTSUPP;
spin_lock_bh(&hdev->fd_rule_lock);
rule = hclge_find_cls_flower(hdev, cls_flower->cookie);
if (!rule) {
spin_unlock_bh(&hdev->fd_rule_lock);
return -EINVAL;
}
ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location,
NULL, false);
if (ret) {
/* if tcam config fail, set rule state to TO_DEL,
* so the rule will be deleted when periodic
* task being scheduled.
*/
hclge_update_fd_list(hdev, HCLGE_FD_TO_DEL, rule->location, NULL);
set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
spin_unlock_bh(&hdev->fd_rule_lock);
return ret;
}
hclge_update_fd_list(hdev, HCLGE_FD_DELETED, rule->location, NULL);
spin_unlock_bh(&hdev->fd_rule_lock);
return 0;
}
static void hclge_sync_fd_list(struct hclge_dev *hdev, struct hlist_head *hlist)
{
struct hclge_fd_rule *rule;
struct hlist_node *node;
int ret = 0;
if (!test_and_clear_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state))
return;
spin_lock_bh(&hdev->fd_rule_lock);
hlist_for_each_entry_safe(rule, node, hlist, rule_node) {
switch (rule->state) {
case HCLGE_FD_TO_ADD:
ret = hclge_fd_config_rule(hdev, rule);
if (ret)
goto out;
rule->state = HCLGE_FD_ACTIVE;
break;
case HCLGE_FD_TO_DEL:
ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true,
rule->location, NULL, false);
if (ret)
goto out;
hclge_fd_dec_rule_cnt(hdev, rule->location);
hclge_fd_free_node(hdev, rule);
break;
default:
break;
}
}
out:
if (ret)
set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state);
spin_unlock_bh(&hdev->fd_rule_lock);
}
static void hclge_sync_fd_table(struct hclge_dev *hdev)
{
if (!hnae3_ae_dev_fd_supported(hdev->ae_dev))
return;
if (test_and_clear_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state)) {
bool clear_list = hdev->fd_active_type == HCLGE_FD_ARFS_ACTIVE;
hclge_clear_fd_rules_in_list(hdev, clear_list);
}
hclge_sync_fd_user_def_cfg(hdev, false);
hclge_sync_fd_list(hdev, &hdev->fd_rule_list);
}
static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG) ||
hclge_read_dev(&hdev->hw, HCLGE_FUN_RST_ING);
}
static bool hclge_get_cmdq_stat(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
}
static bool hclge_ae_dev_resetting(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
}
static unsigned long hclge_ae_dev_reset_cnt(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return hdev->rst_stats.hw_reset_done_cnt;
}
static void hclge_enable_fd(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
hdev->fd_en = enable;
if (!enable)
set_bit(HCLGE_STATE_FD_CLEAR_ALL, &hdev->state);
else
hclge_restore_fd_entries(handle);
hclge_task_schedule(hdev, 0);
}
static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable)
{
#define HCLGE_LINK_STATUS_WAIT_CNT 3
struct hclge_desc desc;
struct hclge_config_mac_mode_cmd *req =
(struct hclge_config_mac_mode_cmd *)desc.data;
u32 loop_en = 0;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false);
if (enable) {
hnae3_set_bit(loop_en, HCLGE_MAC_TX_EN_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_RX_EN_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_PAD_TX_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_PAD_RX_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_FCS_TX_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_RX_FCS_STRIP_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, 1U);
hnae3_set_bit(loop_en, HCLGE_MAC_TX_UNDER_MIN_ERR_B, 1U);
}
req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"mac enable fail, ret =%d.\n", ret);
return;
}
if (!enable)
hclge_mac_link_status_wait(hdev, HCLGE_LINK_STATUS_DOWN,
HCLGE_LINK_STATUS_WAIT_CNT);
}
static int hclge_config_switch_param(struct hclge_dev *hdev, int vfid,
u8 switch_param, u8 param_mask)
{
struct hclge_mac_vlan_switch_cmd *req;
struct hclge_desc desc;
u32 func_id;
int ret;
func_id = hclge_get_port_number(HOST_PORT, 0, vfid, 0);
req = (struct hclge_mac_vlan_switch_cmd *)desc.data;
/* read current config parameter */
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_SWITCH_PARAM,
true);
req->roce_sel = HCLGE_MAC_VLAN_NIC_SEL;
req->func_id = cpu_to_le32(func_id);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"read mac vlan switch parameter fail, ret = %d\n", ret);
return ret;
}
/* modify and write new config parameter */
hclge_comm_cmd_reuse_desc(&desc, false);
req->switch_param = (req->switch_param & param_mask) | switch_param;
req->param_mask = param_mask;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"set mac vlan switch parameter fail, ret = %d\n", ret);
return ret;
}
static void hclge_phy_link_status_wait(struct hclge_dev *hdev,
int link_ret)
{
#define HCLGE_PHY_LINK_STATUS_NUM 200
struct phy_device *phydev = hdev->hw.mac.phydev;
int i = 0;
int ret;
do {
ret = phy_read_status(phydev);
if (ret) {
dev_err(&hdev->pdev->dev,
"phy update link status fail, ret = %d\n", ret);
return;
}
if (phydev->link == link_ret)
break;
msleep(HCLGE_LINK_STATUS_MS);
} while (++i < HCLGE_PHY_LINK_STATUS_NUM);
}
static int hclge_mac_link_status_wait(struct hclge_dev *hdev, int link_ret,
int wait_cnt)
{
int link_status;
int i = 0;
int ret;
do {
ret = hclge_get_mac_link_status(hdev, &link_status);
if (ret)
return ret;
if (link_status == link_ret)
return 0;
msleep(HCLGE_LINK_STATUS_MS);
} while (++i < wait_cnt);
return -EBUSY;
}
static int hclge_mac_phy_link_status_wait(struct hclge_dev *hdev, bool en,
bool is_phy)
{
#define HCLGE_MAC_LINK_STATUS_NUM 100
int link_ret;
link_ret = en ? HCLGE_LINK_STATUS_UP : HCLGE_LINK_STATUS_DOWN;
if (is_phy)
hclge_phy_link_status_wait(hdev, link_ret);
return hclge_mac_link_status_wait(hdev, link_ret,
HCLGE_MAC_LINK_STATUS_NUM);
}
static int hclge_set_app_loopback(struct hclge_dev *hdev, bool en)
{
struct hclge_config_mac_mode_cmd *req;
struct hclge_desc desc;
u32 loop_en;
int ret;
req = (struct hclge_config_mac_mode_cmd *)&desc.data[0];
/* 1 Read out the MAC mode config at first */
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"mac loopback get fail, ret =%d.\n", ret);
return ret;
}
/* 2 Then setup the loopback flag */
loop_en = le32_to_cpu(req->txrx_pad_fcs_loop_en);
hnae3_set_bit(loop_en, HCLGE_MAC_APP_LP_B, en ? 1 : 0);
req->txrx_pad_fcs_loop_en = cpu_to_le32(loop_en);
/* 3 Config mac work mode with loopback flag
* and its original configure parameters
*/
hclge_comm_cmd_reuse_desc(&desc, false);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"mac loopback set fail, ret =%d.\n", ret);
return ret;
}
static int hclge_cfg_common_loopback_cmd_send(struct hclge_dev *hdev, bool en,
enum hnae3_loop loop_mode)
{
struct hclge_common_lb_cmd *req;
struct hclge_desc desc;
u8 loop_mode_b;
int ret;
req = (struct hclge_common_lb_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_COMMON_LOOPBACK, false);
switch (loop_mode) {
case HNAE3_LOOP_SERIAL_SERDES:
loop_mode_b = HCLGE_CMD_SERDES_SERIAL_INNER_LOOP_B;
break;
case HNAE3_LOOP_PARALLEL_SERDES:
loop_mode_b = HCLGE_CMD_SERDES_PARALLEL_INNER_LOOP_B;
break;
case HNAE3_LOOP_PHY:
loop_mode_b = HCLGE_CMD_GE_PHY_INNER_LOOP_B;
break;
default:
dev_err(&hdev->pdev->dev,
"unsupported loopback mode %d\n", loop_mode);
return -ENOTSUPP;
}
req->mask = loop_mode_b;
if (en)
req->enable = loop_mode_b;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to send loopback cmd, loop_mode = %d, ret = %d\n",
loop_mode, ret);
return ret;
}
static int hclge_cfg_common_loopback_wait(struct hclge_dev *hdev)
{
#define HCLGE_COMMON_LB_RETRY_MS 10
#define HCLGE_COMMON_LB_RETRY_NUM 100
struct hclge_common_lb_cmd *req;
struct hclge_desc desc;
u32 i = 0;
int ret;
req = (struct hclge_common_lb_cmd *)desc.data;
do {
msleep(HCLGE_COMMON_LB_RETRY_MS);
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_COMMON_LOOPBACK,
true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to get loopback done status, ret = %d\n",
ret);
return ret;
}
} while (++i < HCLGE_COMMON_LB_RETRY_NUM &&
!(req->result & HCLGE_CMD_COMMON_LB_DONE_B));
if (!(req->result & HCLGE_CMD_COMMON_LB_DONE_B)) {
dev_err(&hdev->pdev->dev, "wait loopback timeout\n");
return -EBUSY;
} else if (!(req->result & HCLGE_CMD_COMMON_LB_SUCCESS_B)) {
dev_err(&hdev->pdev->dev, "failed to do loopback test\n");
return -EIO;
}
return 0;
}
static int hclge_cfg_common_loopback(struct hclge_dev *hdev, bool en,
enum hnae3_loop loop_mode)
{
int ret;
ret = hclge_cfg_common_loopback_cmd_send(hdev, en, loop_mode);
if (ret)
return ret;
return hclge_cfg_common_loopback_wait(hdev);
}
static int hclge_set_common_loopback(struct hclge_dev *hdev, bool en,
enum hnae3_loop loop_mode)
{
int ret;
ret = hclge_cfg_common_loopback(hdev, en, loop_mode);
if (ret)
return ret;
hclge_cfg_mac_mode(hdev, en);
ret = hclge_mac_phy_link_status_wait(hdev, en, false);
if (ret)
dev_err(&hdev->pdev->dev,
"serdes loopback config mac mode timeout\n");
return ret;
}
static int hclge_enable_phy_loopback(struct hclge_dev *hdev,
struct phy_device *phydev)
{
int ret;
if (!phydev->suspended) {
ret = phy_suspend(phydev);
if (ret)
return ret;
}
ret = phy_resume(phydev);
if (ret)
return ret;
return phy_loopback(phydev, true);
}
static int hclge_disable_phy_loopback(struct hclge_dev *hdev,
struct phy_device *phydev)
{
int ret;
ret = phy_loopback(phydev, false);
if (ret)
return ret;
return phy_suspend(phydev);
}
static int hclge_set_phy_loopback(struct hclge_dev *hdev, bool en)
{
struct phy_device *phydev = hdev->hw.mac.phydev;
int ret;
if (!phydev) {
if (hnae3_dev_phy_imp_supported(hdev))
return hclge_set_common_loopback(hdev, en,
HNAE3_LOOP_PHY);
return -ENOTSUPP;
}
if (en)
ret = hclge_enable_phy_loopback(hdev, phydev);
else
ret = hclge_disable_phy_loopback(hdev, phydev);
if (ret) {
dev_err(&hdev->pdev->dev,
"set phy loopback fail, ret = %d\n", ret);
return ret;
}
hclge_cfg_mac_mode(hdev, en);
ret = hclge_mac_phy_link_status_wait(hdev, en, true);
if (ret)
dev_err(&hdev->pdev->dev,
"phy loopback config mac mode timeout\n");
return ret;
}
static int hclge_tqp_enable_cmd_send(struct hclge_dev *hdev, u16 tqp_id,
u16 stream_id, bool enable)
{
struct hclge_desc desc;
struct hclge_cfg_com_tqp_queue_cmd *req =
(struct hclge_cfg_com_tqp_queue_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_COM_TQP_QUEUE, false);
req->tqp_id = cpu_to_le16(tqp_id);
req->stream_id = cpu_to_le16(stream_id);
if (enable)
req->enable |= 1U << HCLGE_TQP_ENABLE_B;
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
static int hclge_tqp_enable(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int ret;
u16 i;
for (i = 0; i < handle->kinfo.num_tqps; i++) {
ret = hclge_tqp_enable_cmd_send(hdev, i, 0, enable);
if (ret)
return ret;
}
return 0;
}
static int hclge_set_loopback(struct hnae3_handle *handle,
enum hnae3_loop loop_mode, bool en)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int ret = 0;
/* Loopback can be enabled in three places: SSU, MAC, and serdes. By
* default, SSU loopback is enabled, so if the SMAC and the DMAC are
* the same, the packets are looped back in the SSU. If SSU loopback
* is disabled, packets can reach MAC even if SMAC is the same as DMAC.
*/
if (hdev->ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V2) {
u8 switch_param = en ? 0 : BIT(HCLGE_SWITCH_ALW_LPBK_B);
ret = hclge_config_switch_param(hdev, PF_VPORT_ID, switch_param,
HCLGE_SWITCH_ALW_LPBK_MASK);
if (ret)
return ret;
}
switch (loop_mode) {
case HNAE3_LOOP_APP:
ret = hclge_set_app_loopback(hdev, en);
break;
case HNAE3_LOOP_SERIAL_SERDES:
case HNAE3_LOOP_PARALLEL_SERDES:
ret = hclge_set_common_loopback(hdev, en, loop_mode);
break;
case HNAE3_LOOP_PHY:
ret = hclge_set_phy_loopback(hdev, en);
break;
case HNAE3_LOOP_EXTERNAL:
break;
default:
ret = -ENOTSUPP;
dev_err(&hdev->pdev->dev,
"loop_mode %d is not supported\n", loop_mode);
break;
}
if (ret)
return ret;
ret = hclge_tqp_enable(handle, en);
if (ret)
dev_err(&hdev->pdev->dev, "failed to %s tqp in loopback, ret = %d\n",
en ? "enable" : "disable", ret);
return ret;
}
static int hclge_set_default_loopback(struct hclge_dev *hdev)
{
int ret;
ret = hclge_set_app_loopback(hdev, false);
if (ret)
return ret;
ret = hclge_cfg_common_loopback(hdev, false, HNAE3_LOOP_SERIAL_SERDES);
if (ret)
return ret;
return hclge_cfg_common_loopback(hdev, false,
HNAE3_LOOP_PARALLEL_SERDES);
}
static void hclge_flush_link_update(struct hclge_dev *hdev)
{
#define HCLGE_FLUSH_LINK_TIMEOUT 100000
unsigned long last = hdev->serv_processed_cnt;
int i = 0;
while (test_bit(HCLGE_STATE_LINK_UPDATING, &hdev->state) &&
i++ < HCLGE_FLUSH_LINK_TIMEOUT &&
last == hdev->serv_processed_cnt)
usleep_range(1, 1);
}
static void hclge_set_timer_task(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
if (enable) {
hclge_task_schedule(hdev, 0);
} else {
/* Set the DOWN flag here to disable link updating */
set_bit(HCLGE_STATE_DOWN, &hdev->state);
/* flush memory to make sure DOWN is seen by service task */
smp_mb__before_atomic();
hclge_flush_link_update(hdev);
}
}
static int hclge_ae_start(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
/* mac enable */
hclge_cfg_mac_mode(hdev, true);
clear_bit(HCLGE_STATE_DOWN, &hdev->state);
hdev->hw.mac.link = 0;
/* reset tqp stats */
hclge_comm_reset_tqp_stats(handle);
hclge_mac_start_phy(hdev);
return 0;
}
static void hclge_ae_stop(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
set_bit(HCLGE_STATE_DOWN, &hdev->state);
spin_lock_bh(&hdev->fd_rule_lock);
hclge_clear_arfs_rules(hdev);
spin_unlock_bh(&hdev->fd_rule_lock);
/* If it is not PF reset or FLR, the firmware will disable the MAC,
* so it only need to stop phy here.
*/
if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) {
hclge_pfc_pause_en_cfg(hdev, HCLGE_PFC_TX_RX_DISABLE,
HCLGE_PFC_DISABLE);
if (hdev->reset_type != HNAE3_FUNC_RESET &&
hdev->reset_type != HNAE3_FLR_RESET) {
hclge_mac_stop_phy(hdev);
hclge_update_link_status(hdev);
return;
}
}
hclge_reset_tqp(handle);
hclge_config_mac_tnl_int(hdev, false);
/* Mac disable */
hclge_cfg_mac_mode(hdev, false);
hclge_mac_stop_phy(hdev);
/* reset tqp stats */
hclge_comm_reset_tqp_stats(handle);
hclge_update_link_status(hdev);
}
int hclge_vport_start(struct hclge_vport *vport)
{
struct hclge_dev *hdev = vport->back;
set_bit(HCLGE_VPORT_STATE_INITED, &vport->state);
set_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
vport->last_active_jiffies = jiffies;
vport->need_notify = 0;
if (test_bit(vport->vport_id, hdev->vport_config_block)) {
if (vport->vport_id) {
hclge_restore_mac_table_common(vport);
hclge_restore_vport_vlan_table(vport);
} else {
hclge_restore_hw_table(hdev);
}
}
clear_bit(vport->vport_id, hdev->vport_config_block);
return 0;
}
void hclge_vport_stop(struct hclge_vport *vport)
{
clear_bit(HCLGE_VPORT_STATE_INITED, &vport->state);
clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
vport->need_notify = 0;
}
static int hclge_client_start(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_vport_start(vport);
}
static void hclge_client_stop(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
hclge_vport_stop(vport);
}
static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
u16 cmdq_resp, u8 resp_code,
enum hclge_mac_vlan_tbl_opcode op)
{
struct hclge_dev *hdev = vport->back;
if (cmdq_resp) {
dev_err(&hdev->pdev->dev,
"cmdq execute failed for get_mac_vlan_cmd_status,status=%u.\n",
cmdq_resp);
return -EIO;
}
if (op == HCLGE_MAC_VLAN_ADD) {
if (!resp_code || resp_code == 1)
return 0;
else if (resp_code == HCLGE_ADD_UC_OVERFLOW ||
resp_code == HCLGE_ADD_MC_OVERFLOW)
return -ENOSPC;
dev_err(&hdev->pdev->dev,
"add mac addr failed for undefined, code=%u.\n",
resp_code);
return -EIO;
} else if (op == HCLGE_MAC_VLAN_REMOVE) {
if (!resp_code) {
return 0;
} else if (resp_code == 1) {
dev_dbg(&hdev->pdev->dev,
"remove mac addr failed for miss.\n");
return -ENOENT;
}
dev_err(&hdev->pdev->dev,
"remove mac addr failed for undefined, code=%u.\n",
resp_code);
return -EIO;
} else if (op == HCLGE_MAC_VLAN_LKUP) {
if (!resp_code) {
return 0;
} else if (resp_code == 1) {
dev_dbg(&hdev->pdev->dev,
"lookup mac addr failed for miss.\n");
return -ENOENT;
}
dev_err(&hdev->pdev->dev,
"lookup mac addr failed for undefined, code=%u.\n",
resp_code);
return -EIO;
}
dev_err(&hdev->pdev->dev,
"unknown opcode for get_mac_vlan_cmd_status, opcode=%d.\n", op);
return -EINVAL;
}
static int hclge_update_desc_vfid(struct hclge_desc *desc, int vfid, bool clr)
{
#define HCLGE_VF_NUM_IN_FIRST_DESC 192
unsigned int word_num;
unsigned int bit_num;
if (vfid > 255 || vfid < 0)
return -EIO;
if (vfid >= 0 && vfid < HCLGE_VF_NUM_IN_FIRST_DESC) {
word_num = vfid / 32;
bit_num = vfid % 32;
if (clr)
desc[1].data[word_num] &= cpu_to_le32(~(1 << bit_num));
else
desc[1].data[word_num] |= cpu_to_le32(1 << bit_num);
} else {
word_num = (vfid - HCLGE_VF_NUM_IN_FIRST_DESC) / 32;
bit_num = vfid % 32;
if (clr)
desc[2].data[word_num] &= cpu_to_le32(~(1 << bit_num));
else
desc[2].data[word_num] |= cpu_to_le32(1 << bit_num);
}
return 0;
}
static bool hclge_is_all_function_id_zero(struct hclge_desc *desc)
{
#define HCLGE_DESC_NUMBER 3
#define HCLGE_FUNC_NUMBER_PER_DESC 6
int i, j;
for (i = 1; i < HCLGE_DESC_NUMBER; i++)
for (j = 0; j < HCLGE_FUNC_NUMBER_PER_DESC; j++)
if (desc[i].data[j])
return false;
return true;
}
static void hclge_prepare_mac_addr(struct hclge_mac_vlan_tbl_entry_cmd *new_req,
const u8 *addr, bool is_mc)
{
const unsigned char *mac_addr = addr;
u32 high_val = mac_addr[2] << 16 | (mac_addr[3] << 24) |
(mac_addr[0]) | (mac_addr[1] << 8);
u32 low_val = mac_addr[4] | (mac_addr[5] << 8);
hnae3_set_bit(new_req->flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
if (is_mc) {
hnae3_set_bit(new_req->entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1);
hnae3_set_bit(new_req->mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 1);
}
new_req->mac_addr_hi32 = cpu_to_le32(high_val);
new_req->mac_addr_lo16 = cpu_to_le16(low_val & 0xffff);
}
static int hclge_remove_mac_vlan_tbl(struct hclge_vport *vport,
struct hclge_mac_vlan_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_MAC_VLAN_REMOVE, false);
memcpy(desc.data, req, sizeof(struct hclge_mac_vlan_tbl_entry_cmd));
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"del mac addr failed for cmd_send, ret =%d.\n",
ret);
return ret;
}
resp_code = (le32_to_cpu(desc.data[0]) >> 8) & 0xff;
retval = le16_to_cpu(desc.retval);
return hclge_get_mac_vlan_cmd_status(vport, retval, resp_code,
HCLGE_MAC_VLAN_REMOVE);
}
static int hclge_lookup_mac_vlan_tbl(struct hclge_vport *vport,
struct hclge_mac_vlan_tbl_entry_cmd *req,
struct hclge_desc *desc,
bool is_mc)
{
struct hclge_dev *hdev = vport->back;
u8 resp_code;
u16 retval;
int ret;
hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_MAC_VLAN_ADD, true);
if (is_mc) {
desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
memcpy(desc[0].data,
req,
sizeof(struct hclge_mac_vlan_tbl_entry_cmd));
hclge_cmd_setup_basic_desc(&desc[1],
HCLGE_OPC_MAC_VLAN_ADD,
true);
desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_cmd_setup_basic_desc(&desc[2],
HCLGE_OPC_MAC_VLAN_ADD,
true);
ret = hclge_cmd_send(&hdev->hw, desc, 3);
} else {
memcpy(desc[0].data,
req,
sizeof(struct hclge_mac_vlan_tbl_entry_cmd));
ret = hclge_cmd_send(&hdev->hw, desc, 1);
}
if (ret) {
dev_err(&hdev->pdev->dev,
"lookup mac addr failed for cmd_send, ret =%d.\n",
ret);
return ret;
}
resp_code = (le32_to_cpu(desc[0].data[0]) >> 8) & 0xff;
retval = le16_to_cpu(desc[0].retval);
return hclge_get_mac_vlan_cmd_status(vport, retval, resp_code,
HCLGE_MAC_VLAN_LKUP);
}
static int hclge_add_mac_vlan_tbl(struct hclge_vport *vport,
struct hclge_mac_vlan_tbl_entry_cmd *req,
struct hclge_desc *mc_desc)
{
struct hclge_dev *hdev = vport->back;
int cfg_status;
u8 resp_code;
u16 retval;
int ret;
if (!mc_desc) {
struct hclge_desc desc;
hclge_cmd_setup_basic_desc(&desc,
HCLGE_OPC_MAC_VLAN_ADD,
false);
memcpy(desc.data, req,
sizeof(struct hclge_mac_vlan_tbl_entry_cmd));
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
resp_code = (le32_to_cpu(desc.data[0]) >> 8) & 0xff;
retval = le16_to_cpu(desc.retval);
cfg_status = hclge_get_mac_vlan_cmd_status(vport, retval,
resp_code,
HCLGE_MAC_VLAN_ADD);
} else {
hclge_comm_cmd_reuse_desc(&mc_desc[0], false);
mc_desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_comm_cmd_reuse_desc(&mc_desc[1], false);
mc_desc[1].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
hclge_comm_cmd_reuse_desc(&mc_desc[2], false);
mc_desc[2].flag &= cpu_to_le16(~HCLGE_COMM_CMD_FLAG_NEXT);
memcpy(mc_desc[0].data, req,
sizeof(struct hclge_mac_vlan_tbl_entry_cmd));
ret = hclge_cmd_send(&hdev->hw, mc_desc, 3);
resp_code = (le32_to_cpu(mc_desc[0].data[0]) >> 8) & 0xff;
retval = le16_to_cpu(mc_desc[0].retval);
cfg_status = hclge_get_mac_vlan_cmd_status(vport, retval,
resp_code,
HCLGE_MAC_VLAN_ADD);
}
if (ret) {
dev_err(&hdev->pdev->dev,
"add mac addr failed for cmd_send, ret =%d.\n",
ret);
return ret;
}
return cfg_status;
}
static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
u16 *allocated_size)
{
struct hclge_umv_spc_alc_cmd *req;
struct hclge_desc desc;
int ret;
req = (struct hclge_umv_spc_alc_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_ALLOCATE, false);
req->space_size = cpu_to_le32(space_size);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev, "failed to set umv space, ret = %d\n",
ret);
return ret;
}
*allocated_size = le32_to_cpu(desc.data[1]);
return 0;
}
static int hclge_init_umv_space(struct hclge_dev *hdev)
{
u16 allocated_size = 0;
int ret;
ret = hclge_set_umv_space(hdev, hdev->wanted_umv_size, &allocated_size);
if (ret)
return ret;
if (allocated_size < hdev->wanted_umv_size)
dev_warn(&hdev->pdev->dev,
"failed to alloc umv space, want %u, get %u\n",
hdev->wanted_umv_size, allocated_size);
hdev->max_umv_size = allocated_size;
hdev->priv_umv_size = hdev->max_umv_size / (hdev->num_alloc_vport + 1);
hdev->share_umv_size = hdev->priv_umv_size +
hdev->max_umv_size % (hdev->num_alloc_vport + 1);
if (hdev->ae_dev->dev_specs.mc_mac_size)
set_bit(HNAE3_DEV_SUPPORT_MC_MAC_MNG_B, hdev->ae_dev->caps);
return 0;
}
static void hclge_reset_umv_space(struct hclge_dev *hdev)
{
struct hclge_vport *vport;
int i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
vport = &hdev->vport[i];
vport->used_umv_num = 0;
}
mutex_lock(&hdev->vport_lock);
hdev->share_umv_size = hdev->priv_umv_size +
hdev->max_umv_size % (hdev->num_alloc_vport + 1);
mutex_unlock(&hdev->vport_lock);
hdev->used_mc_mac_num = 0;
}
static bool hclge_is_umv_space_full(struct hclge_vport *vport, bool need_lock)
{
struct hclge_dev *hdev = vport->back;
bool is_full;
if (need_lock)
mutex_lock(&hdev->vport_lock);
is_full = (vport->used_umv_num >= hdev->priv_umv_size &&
hdev->share_umv_size == 0);
if (need_lock)
mutex_unlock(&hdev->vport_lock);
return is_full;
}
static void hclge_update_umv_space(struct hclge_vport *vport, bool is_free)
{
struct hclge_dev *hdev = vport->back;
if (is_free) {
if (vport->used_umv_num > hdev->priv_umv_size)
hdev->share_umv_size++;
if (vport->used_umv_num > 0)
vport->used_umv_num--;
} else {
if (vport->used_umv_num >= hdev->priv_umv_size &&
hdev->share_umv_size > 0)
hdev->share_umv_size--;
vport->used_umv_num++;
}
}
static struct hclge_mac_node *hclge_find_mac_node(struct list_head *list,
const u8 *mac_addr)
{
struct hclge_mac_node *mac_node, *tmp;
list_for_each_entry_safe(mac_node, tmp, list, node)
if (ether_addr_equal(mac_addr, mac_node->mac_addr))
return mac_node;
return NULL;
}
static void hclge_update_mac_node(struct hclge_mac_node *mac_node,
enum HCLGE_MAC_NODE_STATE state)
{
switch (state) {
/* from set_rx_mode or tmp_add_list */
case HCLGE_MAC_TO_ADD:
if (mac_node->state == HCLGE_MAC_TO_DEL)
mac_node->state = HCLGE_MAC_ACTIVE;
break;
/* only from set_rx_mode */
case HCLGE_MAC_TO_DEL:
if (mac_node->state == HCLGE_MAC_TO_ADD) {
list_del(&mac_node->node);
kfree(mac_node);
} else {
mac_node->state = HCLGE_MAC_TO_DEL;
}
break;
/* only from tmp_add_list, the mac_node->state won't be
* ACTIVE.
*/
case HCLGE_MAC_ACTIVE:
if (mac_node->state == HCLGE_MAC_TO_ADD)
mac_node->state = HCLGE_MAC_ACTIVE;
break;
}
}
int hclge_update_mac_list(struct hclge_vport *vport,
enum HCLGE_MAC_NODE_STATE state,
enum HCLGE_MAC_ADDR_TYPE mac_type,
const unsigned char *addr)
{
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
struct hclge_mac_node *mac_node;
struct list_head *list;
list = (mac_type == HCLGE_MAC_ADDR_UC) ?
&vport->uc_mac_list : &vport->mc_mac_list;
spin_lock_bh(&vport->mac_list_lock);
/* if the mac addr is already in the mac list, no need to add a new
* one into it, just check the mac addr state, convert it to a new
* state, or just remove it, or do nothing.
*/
mac_node = hclge_find_mac_node(list, addr);
if (mac_node) {
hclge_update_mac_node(mac_node, state);
spin_unlock_bh(&vport->mac_list_lock);
set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
return 0;
}
/* if this address is never added, unnecessary to delete */
if (state == HCLGE_MAC_TO_DEL) {
spin_unlock_bh(&vport->mac_list_lock);
hnae3_format_mac_addr(format_mac_addr, addr);
dev_err(&hdev->pdev->dev,
"failed to delete address %s from mac list\n",
format_mac_addr);
return -ENOENT;
}
mac_node = kzalloc(sizeof(*mac_node), GFP_ATOMIC);
if (!mac_node) {
spin_unlock_bh(&vport->mac_list_lock);
return -ENOMEM;
}
set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
mac_node->state = state;
ether_addr_copy(mac_node->mac_addr, addr);
list_add_tail(&mac_node->node, list);
spin_unlock_bh(&vport->mac_list_lock);
return 0;
}
static int hclge_add_uc_addr(struct hnae3_handle *handle,
const unsigned char *addr)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_update_mac_list(vport, HCLGE_MAC_TO_ADD, HCLGE_MAC_ADDR_UC,
addr);
}
int hclge_add_uc_addr_common(struct hclge_vport *vport,
const unsigned char *addr)
{
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
struct hclge_mac_vlan_tbl_entry_cmd req;
struct hclge_desc desc;
u16 egress_port = 0;
int ret;
/* mac addr check */
if (is_zero_ether_addr(addr) ||
is_broadcast_ether_addr(addr) ||
is_multicast_ether_addr(addr)) {
hnae3_format_mac_addr(format_mac_addr, addr);
dev_err(&hdev->pdev->dev,
"Set_uc mac err! invalid mac:%s. is_zero:%d,is_br=%d,is_mul=%d\n",
format_mac_addr, is_zero_ether_addr(addr),
is_broadcast_ether_addr(addr),
is_multicast_ether_addr(addr));
return -EINVAL;
}
memset(&req, 0, sizeof(req));
hnae3_set_field(egress_port, HCLGE_MAC_EPORT_VFID_M,
HCLGE_MAC_EPORT_VFID_S, vport->vport_id);
req.egress_port = cpu_to_le16(egress_port);
hclge_prepare_mac_addr(&req, addr, false);
/* Lookup the mac address in the mac_vlan table, and add
* it if the entry is inexistent. Repeated unicast entry
* is not allowed in the mac vlan table.
*/
ret = hclge_lookup_mac_vlan_tbl(vport, &req, &desc, false);
if (ret == -ENOENT) {
mutex_lock(&hdev->vport_lock);
if (!hclge_is_umv_space_full(vport, false)) {
ret = hclge_add_mac_vlan_tbl(vport, &req, NULL);
if (!ret)
hclge_update_umv_space(vport, false);
mutex_unlock(&hdev->vport_lock);
return ret;
}
mutex_unlock(&hdev->vport_lock);
if (!(vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE))
dev_err(&hdev->pdev->dev, "UC MAC table full(%u)\n",
hdev->priv_umv_size);
return -ENOSPC;
}
/* check if we just hit the duplicate */
if (!ret)
return -EEXIST;
return ret;
}
static int hclge_rm_uc_addr(struct hnae3_handle *handle,
const unsigned char *addr)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_update_mac_list(vport, HCLGE_MAC_TO_DEL, HCLGE_MAC_ADDR_UC,
addr);
}
int hclge_rm_uc_addr_common(struct hclge_vport *vport,
const unsigned char *addr)
{
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
struct hclge_mac_vlan_tbl_entry_cmd req;
int ret;
/* mac addr check */
if (is_zero_ether_addr(addr) ||
is_broadcast_ether_addr(addr) ||
is_multicast_ether_addr(addr)) {
hnae3_format_mac_addr(format_mac_addr, addr);
dev_dbg(&hdev->pdev->dev, "Remove mac err! invalid mac:%s.\n",
format_mac_addr);
return -EINVAL;
}
memset(&req, 0, sizeof(req));
hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
hclge_prepare_mac_addr(&req, addr, false);
ret = hclge_remove_mac_vlan_tbl(vport, &req);
if (!ret || ret == -ENOENT) {
mutex_lock(&hdev->vport_lock);
hclge_update_umv_space(vport, true);
mutex_unlock(&hdev->vport_lock);
return 0;
}
return ret;
}
static int hclge_add_mc_addr(struct hnae3_handle *handle,
const unsigned char *addr)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_update_mac_list(vport, HCLGE_MAC_TO_ADD, HCLGE_MAC_ADDR_MC,
addr);
}
int hclge_add_mc_addr_common(struct hclge_vport *vport,
const unsigned char *addr)
{
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
struct hclge_mac_vlan_tbl_entry_cmd req;
struct hclge_desc desc[3];
bool is_new_addr = false;
int status;
/* mac addr check */
if (!is_multicast_ether_addr(addr)) {
hnae3_format_mac_addr(format_mac_addr, addr);
dev_err(&hdev->pdev->dev,
"Add mc mac err! invalid mac:%s.\n",
format_mac_addr);
return -EINVAL;
}
memset(&req, 0, sizeof(req));
hclge_prepare_mac_addr(&req, addr, true);
status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true);
if (status) {
if (hnae3_ae_dev_mc_mac_mng_supported(hdev->ae_dev) &&
hdev->used_mc_mac_num >=
hdev->ae_dev->dev_specs.mc_mac_size)
goto err_no_space;
is_new_addr = true;
/* This mac addr do not exist, add new entry for it */
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));
}
status = hclge_update_desc_vfid(desc, vport->vport_id, false);
if (status)
return status;
status = hclge_add_mac_vlan_tbl(vport, &req, desc);
if (status == -ENOSPC)
goto err_no_space;
else if (!status && is_new_addr)
hdev->used_mc_mac_num++;
return status;
err_no_space:
/* if already overflow, not to print each time */
if (!(vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE)) {
vport->overflow_promisc_flags |= HNAE3_OVERFLOW_MPE;
dev_err(&hdev->pdev->dev, "mc mac vlan table is full\n");
}
return -ENOSPC;
}
static int hclge_rm_mc_addr(struct hnae3_handle *handle,
const unsigned char *addr)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_update_mac_list(vport, HCLGE_MAC_TO_DEL, HCLGE_MAC_ADDR_MC,
addr);
}
int hclge_rm_mc_addr_common(struct hclge_vport *vport,
const unsigned char *addr)
{
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
struct hclge_mac_vlan_tbl_entry_cmd req;
enum hclge_comm_cmd_status status;
struct hclge_desc desc[3];
/* mac addr check */
if (!is_multicast_ether_addr(addr)) {
hnae3_format_mac_addr(format_mac_addr, addr);
dev_dbg(&hdev->pdev->dev,
"Remove mc mac err! invalid mac:%s.\n",
format_mac_addr);
return -EINVAL;
}
memset(&req, 0, sizeof(req));
hclge_prepare_mac_addr(&req, addr, true);
status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true);
if (!status) {
/* This mac addr exist, remove this handle's VFID for it */
status = hclge_update_desc_vfid(desc, vport->vport_id, true);
if (status)
return status;
if (hclge_is_all_function_id_zero(desc)) {
/* All the vfid is zero, so need to delete this entry */
status = hclge_remove_mac_vlan_tbl(vport, &req);
if (!status)
hdev->used_mc_mac_num--;
} else {
/* Not all the vfid is zero, update the vfid */
status = hclge_add_mac_vlan_tbl(vport, &req, desc);
}
} else if (status == -ENOENT) {
status = 0;
}
return status;
}
static void hclge_sync_vport_mac_list(struct hclge_vport *vport,
struct list_head *list,
enum HCLGE_MAC_ADDR_TYPE mac_type)
{
int (*sync)(struct hclge_vport *vport, const unsigned char *addr);
struct hclge_mac_node *mac_node, *tmp;
int ret;
if (mac_type == HCLGE_MAC_ADDR_UC)
sync = hclge_add_uc_addr_common;
else
sync = hclge_add_mc_addr_common;
list_for_each_entry_safe(mac_node, tmp, list, node) {
ret = sync(vport, mac_node->mac_addr);
if (!ret) {
mac_node->state = HCLGE_MAC_ACTIVE;
} else {
set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE,
&vport->state);
/* If one unicast mac address is existing in hardware,
* we need to try whether other unicast mac addresses
* are new addresses that can be added.
* Multicast mac address can be reusable, even though
* there is no space to add new multicast mac address,
* we should check whether other mac addresses are
* existing in hardware for reuse.
*/
if ((mac_type == HCLGE_MAC_ADDR_UC && ret != -EEXIST) ||
(mac_type == HCLGE_MAC_ADDR_MC && ret != -ENOSPC))
break;
}
}
}
static void hclge_unsync_vport_mac_list(struct hclge_vport *vport,
struct list_head *list,
enum HCLGE_MAC_ADDR_TYPE mac_type)
{
int (*unsync)(struct hclge_vport *vport, const unsigned char *addr);
struct hclge_mac_node *mac_node, *tmp;
int ret;
if (mac_type == HCLGE_MAC_ADDR_UC)
unsync = hclge_rm_uc_addr_common;
else
unsync = hclge_rm_mc_addr_common;
list_for_each_entry_safe(mac_node, tmp, list, node) {
ret = unsync(vport, mac_node->mac_addr);
if (!ret || ret == -ENOENT) {
list_del(&mac_node->node);
kfree(mac_node);
} else {
set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE,
&vport->state);
break;
}
}
}
static bool hclge_sync_from_add_list(struct list_head *add_list,
struct list_head *mac_list)
{
struct hclge_mac_node *mac_node, *tmp, *new_node;
bool all_added = true;
list_for_each_entry_safe(mac_node, tmp, add_list, node) {
if (mac_node->state == HCLGE_MAC_TO_ADD)
all_added = false;
/* if the mac address from tmp_add_list is not in the
* uc/mc_mac_list, it means have received a TO_DEL request
* during the time window of adding the mac address into mac
* table. if mac_node state is ACTIVE, then change it to TO_DEL,
* then it will be removed at next time. else it must be TO_ADD,
* this address hasn't been added into mac table,
* so just remove the mac node.
*/
new_node = hclge_find_mac_node(mac_list, mac_node->mac_addr);
if (new_node) {
hclge_update_mac_node(new_node, mac_node->state);
list_del(&mac_node->node);
kfree(mac_node);
} else if (mac_node->state == HCLGE_MAC_ACTIVE) {
mac_node->state = HCLGE_MAC_TO_DEL;
list_move_tail(&mac_node->node, mac_list);
} else {
list_del(&mac_node->node);
kfree(mac_node);
}
}
return all_added;
}
static void hclge_sync_from_del_list(struct list_head *del_list,
struct list_head *mac_list)
{
struct hclge_mac_node *mac_node, *tmp, *new_node;
list_for_each_entry_safe(mac_node, tmp, del_list, node) {
new_node = hclge_find_mac_node(mac_list, mac_node->mac_addr);
if (new_node) {
/* If the mac addr exists in the mac list, it means
* received a new TO_ADD request during the time window
* of configuring the mac address. For the mac node
* state is TO_ADD, and the address is already in the
* in the hardware(due to delete fail), so we just need
* to change the mac node state to ACTIVE.
*/
new_node->state = HCLGE_MAC_ACTIVE;
list_del(&mac_node->node);
kfree(mac_node);
} else {
list_move_tail(&mac_node->node, mac_list);
}
}
}
static void hclge_update_overflow_flags(struct hclge_vport *vport,
enum HCLGE_MAC_ADDR_TYPE mac_type,
bool is_all_added)
{
if (mac_type == HCLGE_MAC_ADDR_UC) {
if (is_all_added)
vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_UPE;
else if (hclge_is_umv_space_full(vport, true))
vport->overflow_promisc_flags |= HNAE3_OVERFLOW_UPE;
} else {
if (is_all_added)
vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_MPE;
else
vport->overflow_promisc_flags |= HNAE3_OVERFLOW_MPE;
}
}
static void hclge_sync_vport_mac_table(struct hclge_vport *vport,
enum HCLGE_MAC_ADDR_TYPE mac_type)
{
struct hclge_mac_node *mac_node, *tmp, *new_node;
struct list_head tmp_add_list, tmp_del_list;
struct list_head *list;
bool all_added;
INIT_LIST_HEAD(&tmp_add_list);
INIT_LIST_HEAD(&tmp_del_list);
/* move the mac addr to the tmp_add_list and tmp_del_list, then
* we can add/delete these mac addr outside the spin lock
*/
list = (mac_type == HCLGE_MAC_ADDR_UC) ?
&vport->uc_mac_list : &vport->mc_mac_list;
spin_lock_bh(&vport->mac_list_lock);
list_for_each_entry_safe(mac_node, tmp, list, node) {
switch (mac_node->state) {
case HCLGE_MAC_TO_DEL:
list_move_tail(&mac_node->node, &tmp_del_list);
break;
case HCLGE_MAC_TO_ADD:
new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
if (!new_node)
goto stop_traverse;
ether_addr_copy(new_node->mac_addr, mac_node->mac_addr);
new_node->state = mac_node->state;
list_add_tail(&new_node->node, &tmp_add_list);
break;
default:
break;
}
}
stop_traverse:
spin_unlock_bh(&vport->mac_list_lock);
/* delete first, in order to get max mac table space for adding */
hclge_unsync_vport_mac_list(vport, &tmp_del_list, mac_type);
hclge_sync_vport_mac_list(vport, &tmp_add_list, mac_type);
/* if some mac addresses were added/deleted fail, move back to the
* mac_list, and retry at next time.
*/
spin_lock_bh(&vport->mac_list_lock);
hclge_sync_from_del_list(&tmp_del_list, list);
all_added = hclge_sync_from_add_list(&tmp_add_list, list);
spin_unlock_bh(&vport->mac_list_lock);
hclge_update_overflow_flags(vport, mac_type, all_added);
}
static bool hclge_need_sync_mac_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_MAC_TBL_CHANGE, &vport->state))
return true;
return false;
}
static void hclge_sync_mac_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_need_sync_mac_table(vport))
continue;
hclge_sync_vport_mac_table(vport, HCLGE_MAC_ADDR_UC);
hclge_sync_vport_mac_table(vport, HCLGE_MAC_ADDR_MC);
}
}
static void hclge_build_del_list(struct list_head *list,
bool is_del_list,
struct list_head *tmp_del_list)
{
struct hclge_mac_node *mac_cfg, *tmp;
list_for_each_entry_safe(mac_cfg, tmp, list, node) {
switch (mac_cfg->state) {
case HCLGE_MAC_TO_DEL:
case HCLGE_MAC_ACTIVE:
list_move_tail(&mac_cfg->node, tmp_del_list);
break;
case HCLGE_MAC_TO_ADD:
if (is_del_list) {
list_del(&mac_cfg->node);
kfree(mac_cfg);
}
break;
}
}
}
static void hclge_unsync_del_list(struct hclge_vport *vport,
int (*unsync)(struct hclge_vport *vport,
const unsigned char *addr),
bool is_del_list,
struct list_head *tmp_del_list)
{
struct hclge_mac_node *mac_cfg, *tmp;
int ret;
list_for_each_entry_safe(mac_cfg, tmp, tmp_del_list, node) {
ret = unsync(vport, mac_cfg->mac_addr);
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 &&
mac_cfg->state == HCLGE_MAC_ACTIVE) {
mac_cfg->state = HCLGE_MAC_TO_ADD;
} else {
list_del(&mac_cfg->node);
kfree(mac_cfg);
}
} else if (is_del_list) {
mac_cfg->state = HCLGE_MAC_TO_DEL;
}
}
}
void hclge_rm_vport_all_mac_table(struct hclge_vport *vport, bool is_del_list,
enum HCLGE_MAC_ADDR_TYPE mac_type)
{
int (*unsync)(struct hclge_vport *vport, const unsigned char *addr);
struct hclge_dev *hdev = vport->back;
struct list_head tmp_del_list, *list;
if (mac_type == HCLGE_MAC_ADDR_UC) {
list = &vport->uc_mac_list;
unsync = hclge_rm_uc_addr_common;
} else {
list = &vport->mc_mac_list;
unsync = hclge_rm_mc_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->mac_list_lock);
hclge_build_del_list(list, is_del_list, &tmp_del_list);
spin_unlock_bh(&vport->mac_list_lock);
hclge_unsync_del_list(vport, unsync, is_del_list, &tmp_del_list);
spin_lock_bh(&vport->mac_list_lock);
hclge_sync_from_del_list(&tmp_del_list, list);
spin_unlock_bh(&vport->mac_list_lock);
}
/* remove all mac address when uninitailize */
static void hclge_uninit_vport_mac_list(struct hclge_vport *vport,
enum HCLGE_MAC_ADDR_TYPE mac_type)
{
struct hclge_mac_node *mac_node, *tmp;
struct hclge_dev *hdev = vport->back;
struct list_head tmp_del_list, *list;
INIT_LIST_HEAD(&tmp_del_list);
list = (mac_type == HCLGE_MAC_ADDR_UC) ?
&vport->uc_mac_list : &vport->mc_mac_list;
spin_lock_bh(&vport->mac_list_lock);
list_for_each_entry_safe(mac_node, tmp, list, node) {
switch (mac_node->state) {
case HCLGE_MAC_TO_DEL:
case HCLGE_MAC_ACTIVE:
list_move_tail(&mac_node->node, &tmp_del_list);
break;
case HCLGE_MAC_TO_ADD:
list_del(&mac_node->node);
kfree(mac_node);
break;
}
}
spin_unlock_bh(&vport->mac_list_lock);
hclge_unsync_vport_mac_list(vport, &tmp_del_list, mac_type);
if (!list_empty(&tmp_del_list))
dev_warn(&hdev->pdev->dev,
"uninit %s mac list for vport %u not completely.\n",
mac_type == HCLGE_MAC_ADDR_UC ? "uc" : "mc",
vport->vport_id);
list_for_each_entry_safe(mac_node, tmp, &tmp_del_list, node) {
list_del(&mac_node->node);
kfree(mac_node);
}
}
static void hclge_uninit_mac_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_uninit_vport_mac_list(vport, HCLGE_MAC_ADDR_UC);
hclge_uninit_vport_mac_list(vport, HCLGE_MAC_ADDR_MC);
}
}
static int hclge_get_mac_ethertype_cmd_status(struct hclge_dev *hdev,
u16 cmdq_resp, u8 resp_code)
{
#define HCLGE_ETHERTYPE_SUCCESS_ADD 0
#define HCLGE_ETHERTYPE_ALREADY_ADD 1
#define HCLGE_ETHERTYPE_MGR_TBL_OVERFLOW 2
#define HCLGE_ETHERTYPE_KEY_CONFLICT 3
int return_status;
if (cmdq_resp) {
dev_err(&hdev->pdev->dev,
"cmdq execute failed for get_mac_ethertype_cmd_status, status=%u.\n",
cmdq_resp);
return -EIO;
}
switch (resp_code) {
case HCLGE_ETHERTYPE_SUCCESS_ADD:
case HCLGE_ETHERTYPE_ALREADY_ADD:
return_status = 0;
break;
case HCLGE_ETHERTYPE_MGR_TBL_OVERFLOW:
dev_err(&hdev->pdev->dev,
"add mac ethertype failed for manager table overflow.\n");
return_status = -EIO;
break;
case HCLGE_ETHERTYPE_KEY_CONFLICT:
dev_err(&hdev->pdev->dev,
"add mac ethertype failed for key conflict.\n");
return_status = -EIO;
break;
default:
dev_err(&hdev->pdev->dev,
"add mac ethertype failed for undefined, code=%u.\n",
resp_code);
return_status = -EIO;
}
return return_status;
}
static int hclge_set_vf_mac(struct hnae3_handle *handle, int vf,
u8 *mac_addr)
{
struct hclge_vport *vport = hclge_get_vport(handle);
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
hnae3_format_mac_addr(format_mac_addr, mac_addr);
if (ether_addr_equal(mac_addr, vport->vf_info.mac)) {
dev_info(&hdev->pdev->dev,
"Specified MAC(=%s) is same as before, no change committed!\n",
format_mac_addr);
return 0;
}
ether_addr_copy(vport->vf_info.mac, mac_addr);
/* there is a timewindow for PF to know VF unalive, it may
* cause send mailbox fail, but it doesn't matter, VF will
* query it when reinit.
*/
if (test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state)) {
dev_info(&hdev->pdev->dev,
"MAC of VF %d has been set to %s, and it will be reinitialized!\n",
vf, format_mac_addr);
(void)hclge_inform_reset_assert_to_vf(vport);
return 0;
}
dev_info(&hdev->pdev->dev,
"MAC of VF %d has been set to %s, will be active after VF reset\n",
vf, format_mac_addr);
return 0;
}
static int hclge_add_mgr_tbl(struct hclge_dev *hdev,
const struct hclge_mac_mgr_tbl_entry_cmd *req)
{
struct hclge_desc desc;
u8 resp_code;
u16 retval;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_ETHTYPE_ADD, false);
memcpy(desc.data, req, sizeof(struct hclge_mac_mgr_tbl_entry_cmd));
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"add mac ethertype failed for cmd_send, ret =%d.\n",
ret);
return ret;
}
resp_code = (le32_to_cpu(desc.data[0]) >> 8) & 0xff;
retval = le16_to_cpu(desc.retval);
return hclge_get_mac_ethertype_cmd_status(hdev, retval, resp_code);
}
static int init_mgr_tbl(struct hclge_dev *hdev)
{
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(hclge_mgr_table); i++) {
ret = hclge_add_mgr_tbl(hdev, &hclge_mgr_table[i]);
if (ret) {
dev_err(&hdev->pdev->dev,
"add mac ethertype failed, ret =%d.\n",
ret);
return ret;
}
}
return 0;
}
static void hclge_get_mac_addr(struct hnae3_handle *handle, u8 *p)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
ether_addr_copy(p, hdev->hw.mac.mac_addr);
}
int hclge_update_mac_node_for_dev_addr(struct hclge_vport *vport,
const u8 *old_addr, const u8 *new_addr)
{
struct list_head *list = &vport->uc_mac_list;
struct hclge_mac_node *old_node, *new_node;
new_node = hclge_find_mac_node(list, new_addr);
if (!new_node) {
new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
if (!new_node)
return -ENOMEM;
new_node->state = HCLGE_MAC_TO_ADD;
ether_addr_copy(new_node->mac_addr, new_addr);
list_add(&new_node->node, list);
} else {
if (new_node->state == HCLGE_MAC_TO_DEL)
new_node->state = HCLGE_MAC_ACTIVE;
/* make sure the new addr is in the list head, avoid dev
* addr may be not re-added into mac table for the umv space
* limitation after global/imp reset which will clear mac
* table by hardware.
*/
list_move(&new_node->node, list);
}
if (old_addr && !ether_addr_equal(old_addr, new_addr)) {
old_node = hclge_find_mac_node(list, old_addr);
if (old_node) {
if (old_node->state == HCLGE_MAC_TO_ADD) {
list_del(&old_node->node);
kfree(old_node);
} else {
old_node->state = HCLGE_MAC_TO_DEL;
}
}
}
set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
return 0;
}
static int hclge_set_mac_addr(struct hnae3_handle *handle, const void *p,
bool is_first)
{
const unsigned char *new_addr = (const unsigned char *)p;
struct hclge_vport *vport = hclge_get_vport(handle);
char format_mac_addr[HNAE3_FORMAT_MAC_ADDR_LEN];
struct hclge_dev *hdev = vport->back;
unsigned char *old_addr = NULL;
int ret;
/* mac addr check */
if (is_zero_ether_addr(new_addr) ||
is_broadcast_ether_addr(new_addr) ||
is_multicast_ether_addr(new_addr)) {
hnae3_format_mac_addr(format_mac_addr, new_addr);
dev_err(&hdev->pdev->dev,
"change uc mac err! invalid mac: %s.\n",
format_mac_addr);
return -EINVAL;
}
ret = hclge_pause_addr_cfg(hdev, new_addr);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to configure mac pause address, ret = %d\n",
ret);
return ret;
}
if (!is_first)
old_addr = hdev->hw.mac.mac_addr;
spin_lock_bh(&vport->mac_list_lock);
ret = hclge_update_mac_node_for_dev_addr(vport, old_addr, new_addr);
if (ret) {
hnae3_format_mac_addr(format_mac_addr, new_addr);
dev_err(&hdev->pdev->dev,
"failed to change the mac addr:%s, ret = %d\n",
format_mac_addr, ret);
spin_unlock_bh(&vport->mac_list_lock);
if (!is_first)
hclge_pause_addr_cfg(hdev, old_addr);
return ret;
}
/* we must update dev addr with spin lock protect, preventing dev addr
* being removed by set_rx_mode path.
*/
ether_addr_copy(hdev->hw.mac.mac_addr, new_addr);
spin_unlock_bh(&vport->mac_list_lock);
hclge_task_schedule(hdev, 0);
return 0;
}
static int hclge_mii_ioctl(struct hclge_dev *hdev, struct ifreq *ifr, int cmd)
{
struct mii_ioctl_data *data = if_mii(ifr);
if (!hnae3_dev_phy_imp_supported(hdev))
return -EOPNOTSUPP;
switch (cmd) {
case SIOCGMIIPHY:
data->phy_id = hdev->hw.mac.phy_addr;
/* this command reads phy id and register at the same time */
fallthrough;
case SIOCGMIIREG:
data->val_out = hclge_read_phy_reg(hdev, data->reg_num);
return 0;
case SIOCSMIIREG:
return hclge_write_phy_reg(hdev, data->reg_num, data->val_in);
default:
return -EOPNOTSUPP;
}
}
static int hclge_do_ioctl(struct hnae3_handle *handle, struct ifreq *ifr,
int cmd)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
switch (cmd) {
case SIOCGHWTSTAMP:
return hclge_ptp_get_cfg(hdev, ifr);
case SIOCSHWTSTAMP:
return hclge_ptp_set_cfg(hdev, ifr);
default:
if (!hdev->hw.mac.phydev)
return hclge_mii_ioctl(hdev, ifr, cmd);
}
return phy_mii_ioctl(hdev->hw.mac.phydev, ifr, cmd);
}
static int hclge_set_port_vlan_filter_bypass(struct hclge_dev *hdev, u8 vf_id,
bool bypass_en)
{
struct hclge_port_vlan_filter_bypass_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PORT_VLAN_BYPASS, false);
req = (struct hclge_port_vlan_filter_bypass_cmd *)desc.data;
req->vf_id = vf_id;
hnae3_set_bit(req->bypass_state, HCLGE_INGRESS_BYPASS_B,
bypass_en ? 1 : 0);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to set vport%u port vlan filter bypass state, ret = %d.\n",
vf_id, ret);
return ret;
}
static int hclge_set_vlan_filter_ctrl(struct hclge_dev *hdev, u8 vlan_type,
u8 fe_type, bool filter_en, u8 vf_id)
{
struct hclge_vlan_filter_ctrl_cmd *req;
struct hclge_desc desc;
int ret;
/* read current vlan filter parameter */
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_FILTER_CTRL, true);
req = (struct hclge_vlan_filter_ctrl_cmd *)desc.data;
req->vlan_type = vlan_type;
req->vf_id = vf_id;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev, "failed to get vport%u vlan filter config, ret = %d.\n",
vf_id, ret);
return ret;
}
/* modify and write new config parameter */
hclge_comm_cmd_reuse_desc(&desc, false);
req->vlan_fe = filter_en ?
(req->vlan_fe | fe_type) : (req->vlan_fe & ~fe_type);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev, "failed to set vport%u vlan filter, ret = %d.\n",
vf_id, ret);
return ret;
}
static int hclge_set_vport_vlan_filter(struct hclge_vport *vport, bool enable)
{
struct hclge_dev *hdev = vport->back;
struct hnae3_ae_dev *ae_dev = hdev->ae_dev;
int ret;
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
return hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_VF,
HCLGE_FILTER_FE_EGRESS_V1_B,
enable, vport->vport_id);
ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_VF,
HCLGE_FILTER_FE_EGRESS, enable,
vport->vport_id);
if (ret)
return ret;
if (test_bit(HNAE3_DEV_SUPPORT_PORT_VLAN_BYPASS_B, ae_dev->caps)) {
ret = hclge_set_port_vlan_filter_bypass(hdev, vport->vport_id,
!enable);
} else if (!vport->vport_id) {
if (test_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, ae_dev->caps))
enable = false;
ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_PORT,
HCLGE_FILTER_FE_INGRESS,
enable, 0);
}
return ret;
}
static bool hclge_need_enable_vport_vlan_filter(struct hclge_vport *vport)
{
struct hnae3_handle *handle = &vport->nic;
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_dev *hdev = vport->back;
if (vport->vport_id) {
if (vport->port_base_vlan_cfg.state !=
HNAE3_PORT_BASE_VLAN_DISABLE)
return true;
if (vport->vf_info.trusted && vport->vf_info.request_uc_en)
return false;
} else if (handle->netdev_flags & HNAE3_USER_UPE) {
return false;
}
if (!vport->req_vlan_fltr_en)
return false;
/* compatible with former device, always enable vlan filter */
if (!test_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, hdev->ae_dev->caps))
return true;
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node)
if (vlan->vlan_id != 0)
return true;
return false;
}
int hclge_enable_vport_vlan_filter(struct hclge_vport *vport, bool request_en)
{
struct hclge_dev *hdev = vport->back;
bool need_en;
int ret;
mutex_lock(&hdev->vport_lock);
vport->req_vlan_fltr_en = request_en;
need_en = hclge_need_enable_vport_vlan_filter(vport);
if (need_en == vport->cur_vlan_fltr_en) {
mutex_unlock(&hdev->vport_lock);
return 0;
}
ret = hclge_set_vport_vlan_filter(vport, need_en);
if (ret) {
mutex_unlock(&hdev->vport_lock);
return ret;
}
vport->cur_vlan_fltr_en = need_en;
mutex_unlock(&hdev->vport_lock);
return 0;
}
static int hclge_enable_vlan_filter(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_enable_vport_vlan_filter(vport, enable);
}
static int hclge_set_vf_vlan_filter_cmd(struct hclge_dev *hdev, u16 vfid,
bool is_kill, u16 vlan,
struct hclge_desc *desc)
{
struct hclge_vlan_filter_vf_cfg_cmd *req0;
struct hclge_vlan_filter_vf_cfg_cmd *req1;
u8 vf_byte_val;
u8 vf_byte_off;
int ret;
hclge_cmd_setup_basic_desc(&desc[0],
HCLGE_OPC_VLAN_FILTER_VF_CFG, false);
hclge_cmd_setup_basic_desc(&desc[1],
HCLGE_OPC_VLAN_FILTER_VF_CFG, false);
desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
vf_byte_off = vfid / 8;
vf_byte_val = 1 << (vfid % 8);
req0 = (struct hclge_vlan_filter_vf_cfg_cmd *)desc[0].data;
req1 = (struct hclge_vlan_filter_vf_cfg_cmd *)desc[1].data;
req0->vlan_id = cpu_to_le16(vlan);
req0->vlan_cfg = is_kill;
if (vf_byte_off < HCLGE_MAX_VF_BYTES)
req0->vf_bitmap[vf_byte_off] = vf_byte_val;
else
req1->vf_bitmap[vf_byte_off - HCLGE_MAX_VF_BYTES] = vf_byte_val;
ret = hclge_cmd_send(&hdev->hw, desc, 2);
if (ret) {
dev_err(&hdev->pdev->dev,
"Send vf vlan command fail, ret =%d.\n",
ret);
return ret;
}
return 0;
}
static int hclge_check_vf_vlan_cmd_status(struct hclge_dev *hdev, u16 vfid,
bool is_kill, struct hclge_desc *desc)
{
struct hclge_vlan_filter_vf_cfg_cmd *req;
req = (struct hclge_vlan_filter_vf_cfg_cmd *)desc[0].data;
if (!is_kill) {
#define HCLGE_VF_VLAN_NO_ENTRY 2
if (!req->resp_code || req->resp_code == 1)
return 0;
if (req->resp_code == HCLGE_VF_VLAN_NO_ENTRY) {
set_bit(vfid, hdev->vf_vlan_full);
dev_warn(&hdev->pdev->dev,
"vf vlan table is full, vf vlan filter is disabled\n");
return 0;
}
dev_err(&hdev->pdev->dev,
"Add vf vlan filter fail, ret =%u.\n",
req->resp_code);
} else {
#define HCLGE_VF_VLAN_DEL_NO_FOUND 1
if (!req->resp_code)
return 0;
/* vf vlan filter is disabled when vf vlan table is full,
* then new vlan id will not be added into vf vlan table.
* Just return 0 without warning, avoid massive verbose
* print logs when unload.
*/
if (req->resp_code == HCLGE_VF_VLAN_DEL_NO_FOUND)
return 0;
dev_err(&hdev->pdev->dev,
"Kill vf vlan filter fail, ret =%u.\n",
req->resp_code);
}
return -EIO;
}
static int hclge_set_vf_vlan_common(struct hclge_dev *hdev, u16 vfid,
bool is_kill, u16 vlan)
{
struct hclge_vport *vport = &hdev->vport[vfid];
struct hclge_desc desc[2];
int ret;
/* if vf vlan table is full, firmware will close vf vlan filter, it
* is unable and unnecessary to add new vlan id to vf vlan filter.
* If spoof check is enable, and vf vlan is full, it shouldn't add
* new vlan, because tx packets with these vlan id will be dropped.
*/
if (test_bit(vfid, hdev->vf_vlan_full) && !is_kill) {
if (vport->vf_info.spoofchk && vlan) {
dev_err(&hdev->pdev->dev,
"Can't add vlan due to spoof check is on and vf vlan table is full\n");
return -EPERM;
}
return 0;
}
ret = hclge_set_vf_vlan_filter_cmd(hdev, vfid, is_kill, vlan, desc);
if (ret)
return ret;
return hclge_check_vf_vlan_cmd_status(hdev, vfid, is_kill, desc);
}
static int hclge_set_port_vlan_filter(struct hclge_dev *hdev, __be16 proto,
u16 vlan_id, bool is_kill)
{
struct hclge_vlan_filter_pf_cfg_cmd *req;
struct hclge_desc desc;
u8 vlan_offset_byte_val;
u8 vlan_offset_byte;
u8 vlan_offset_160;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_FILTER_PF_CFG, false);
vlan_offset_160 = vlan_id / HCLGE_VLAN_ID_OFFSET_STEP;
vlan_offset_byte = (vlan_id % HCLGE_VLAN_ID_OFFSET_STEP) /
HCLGE_VLAN_BYTE_SIZE;
vlan_offset_byte_val = 1 << (vlan_id % HCLGE_VLAN_BYTE_SIZE);
req = (struct hclge_vlan_filter_pf_cfg_cmd *)desc.data;
req->vlan_offset = vlan_offset_160;
req->vlan_cfg = is_kill;
req->vlan_offset_bitmap[vlan_offset_byte] = vlan_offset_byte_val;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"port vlan command, send fail, ret =%d.\n", ret);
return ret;
}
static bool hclge_need_update_port_vlan(struct hclge_dev *hdev, u16 vport_id,
u16 vlan_id, bool is_kill)
{
/* vlan 0 may be added twice when 8021q module is enabled */
if (!is_kill && !vlan_id &&
test_bit(vport_id, hdev->vlan_table[vlan_id]))
return false;
if (!is_kill && test_and_set_bit(vport_id, hdev->vlan_table[vlan_id])) {
dev_warn(&hdev->pdev->dev,
"Add port vlan failed, vport %u is already in vlan %u\n",
vport_id, vlan_id);
return false;
}
if (is_kill &&
!test_and_clear_bit(vport_id, hdev->vlan_table[vlan_id])) {
dev_warn(&hdev->pdev->dev,
"Delete port vlan failed, vport %u is not in vlan %u\n",
vport_id, vlan_id);
return false;
}
return true;
}
static int hclge_set_vlan_filter_hw(struct hclge_dev *hdev, __be16 proto,
u16 vport_id, u16 vlan_id,
bool is_kill)
{
u16 vport_idx, vport_num = 0;
int ret;
if (is_kill && !vlan_id)
return 0;
if (vlan_id >= VLAN_N_VID)
return -EINVAL;
ret = hclge_set_vf_vlan_common(hdev, vport_id, is_kill, vlan_id);
if (ret) {
dev_err(&hdev->pdev->dev,
"Set %u vport vlan filter config fail, ret =%d.\n",
vport_id, ret);
return ret;
}
if (!hclge_need_update_port_vlan(hdev, vport_id, vlan_id, is_kill))
return 0;
for_each_set_bit(vport_idx, hdev->vlan_table[vlan_id], HCLGE_VPORT_NUM)
vport_num++;
if ((is_kill && vport_num == 0) || (!is_kill && vport_num == 1))
ret = hclge_set_port_vlan_filter(hdev, proto, vlan_id,
is_kill);
return ret;
}
static int hclge_set_vlan_tx_offload_cfg(struct hclge_vport *vport)
{
struct hclge_tx_vtag_cfg *vcfg = &vport->txvlan_cfg;
struct hclge_vport_vtag_tx_cfg_cmd *req;
struct hclge_dev *hdev = vport->back;
struct hclge_desc desc;
u16 bmap_index;
int status;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_PORT_TX_CFG, false);
req = (struct hclge_vport_vtag_tx_cfg_cmd *)desc.data;
req->def_vlan_tag1 = cpu_to_le16(vcfg->default_tag1);
req->def_vlan_tag2 = cpu_to_le16(vcfg->default_tag2);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_ACCEPT_TAG1_B,
vcfg->accept_tag1 ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_ACCEPT_UNTAG1_B,
vcfg->accept_untag1 ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_ACCEPT_TAG2_B,
vcfg->accept_tag2 ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_ACCEPT_UNTAG2_B,
vcfg->accept_untag2 ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_PORT_INS_TAG1_EN_B,
vcfg->insert_tag1_en ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_PORT_INS_TAG2_EN_B,
vcfg->insert_tag2_en ? 1 : 0);
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_TAG_SHIFT_MODE_EN_B,
vcfg->tag_shift_mode_en ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_CFG_NIC_ROCE_SEL_B, 0);
req->vf_offset = vport->vport_id / HCLGE_VF_NUM_PER_CMD;
bmap_index = vport->vport_id % HCLGE_VF_NUM_PER_CMD /
HCLGE_VF_NUM_PER_BYTE;
req->vf_bitmap[bmap_index] =
1U << (vport->vport_id % HCLGE_VF_NUM_PER_BYTE);
status = hclge_cmd_send(&hdev->hw, &desc, 1);
if (status)
dev_err(&hdev->pdev->dev,
"Send port txvlan cfg command fail, ret =%d\n",
status);
return status;
}
static int hclge_set_vlan_rx_offload_cfg(struct hclge_vport *vport)
{
struct hclge_rx_vtag_cfg *vcfg = &vport->rxvlan_cfg;
struct hclge_vport_vtag_rx_cfg_cmd *req;
struct hclge_dev *hdev = vport->back;
struct hclge_desc desc;
u16 bmap_index;
int status;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_PORT_RX_CFG, false);
req = (struct hclge_vport_vtag_rx_cfg_cmd *)desc.data;
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_REM_TAG1_EN_B,
vcfg->strip_tag1_en ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_REM_TAG2_EN_B,
vcfg->strip_tag2_en ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_SHOW_TAG1_EN_B,
vcfg->vlan1_vlan_prionly ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_SHOW_TAG2_EN_B,
vcfg->vlan2_vlan_prionly ? 1 : 0);
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_DISCARD_TAG1_EN_B,
vcfg->strip_tag1_discard_en ? 1 : 0);
hnae3_set_bit(req->vport_vlan_cfg, HCLGE_DISCARD_TAG2_EN_B,
vcfg->strip_tag2_discard_en ? 1 : 0);
req->vf_offset = vport->vport_id / HCLGE_VF_NUM_PER_CMD;
bmap_index = vport->vport_id % HCLGE_VF_NUM_PER_CMD /
HCLGE_VF_NUM_PER_BYTE;
req->vf_bitmap[bmap_index] =
1U << (vport->vport_id % HCLGE_VF_NUM_PER_BYTE);
status = hclge_cmd_send(&hdev->hw, &desc, 1);
if (status)
dev_err(&hdev->pdev->dev,
"Send port rxvlan cfg command fail, ret =%d\n",
status);
return status;
}
static int hclge_vlan_offload_cfg(struct hclge_vport *vport,
u16 port_base_vlan_state,
u16 vlan_tag, u8 qos)
{
int ret;
if (port_base_vlan_state == HNAE3_PORT_BASE_VLAN_DISABLE) {
vport->txvlan_cfg.accept_tag1 = true;
vport->txvlan_cfg.insert_tag1_en = false;
vport->txvlan_cfg.default_tag1 = 0;
} else {
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(vport->nic.pdev);
vport->txvlan_cfg.accept_tag1 =
ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3;
vport->txvlan_cfg.insert_tag1_en = true;
vport->txvlan_cfg.default_tag1 = (qos << VLAN_PRIO_SHIFT) |
vlan_tag;
}
vport->txvlan_cfg.accept_untag1 = true;
/* accept_tag2 and accept_untag2 are not supported on
* pdev revision(0x20), new revision support them,
* this two fields can not be configured by user.
*/
vport->txvlan_cfg.accept_tag2 = true;
vport->txvlan_cfg.accept_untag2 = true;
vport->txvlan_cfg.insert_tag2_en = false;
vport->txvlan_cfg.default_tag2 = 0;
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->txvlan_cfg.tag_shift_mode_en = true;
if (port_base_vlan_state == HNAE3_PORT_BASE_VLAN_DISABLE) {
vport->rxvlan_cfg.strip_tag1_en = false;
vport->rxvlan_cfg.strip_tag2_en =
vport->rxvlan_cfg.rx_vlan_offload_en;
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->rxvlan_cfg.strip_tag2_discard_en = false;
} else {
vport->rxvlan_cfg.strip_tag1_en =
vport->rxvlan_cfg.rx_vlan_offload_en;
vport->rxvlan_cfg.strip_tag2_en = true;
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->rxvlan_cfg.strip_tag2_discard_en = true;
}
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->rxvlan_cfg.strip_tag1_discard_en = false;
vport->rxvlan_cfg.vlan1_vlan_prionly = false;
vport->rxvlan_cfg.vlan2_vlan_prionly = false;
ret = hclge_set_vlan_tx_offload_cfg(vport);
if (ret)
return ret;
return hclge_set_vlan_rx_offload_cfg(vport);
}
static int hclge_set_vlan_protocol_type(struct hclge_dev *hdev)
{
struct hclge_rx_vlan_type_cfg_cmd *rx_req;
struct hclge_tx_vlan_type_cfg_cmd *tx_req;
struct hclge_desc desc;
int status;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_TYPE_ID, false);
rx_req = (struct hclge_rx_vlan_type_cfg_cmd *)desc.data;
rx_req->ot_fst_vlan_type =
cpu_to_le16(hdev->vlan_type_cfg.rx_ot_fst_vlan_type);
rx_req->ot_sec_vlan_type =
cpu_to_le16(hdev->vlan_type_cfg.rx_ot_sec_vlan_type);
rx_req->in_fst_vlan_type =
cpu_to_le16(hdev->vlan_type_cfg.rx_in_fst_vlan_type);
rx_req->in_sec_vlan_type =
cpu_to_le16(hdev->vlan_type_cfg.rx_in_sec_vlan_type);
status = hclge_cmd_send(&hdev->hw, &desc, 1);
if (status) {
dev_err(&hdev->pdev->dev,
"Send rxvlan protocol type command fail, ret =%d\n",
status);
return status;
}
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_INSERT, false);
tx_req = (struct hclge_tx_vlan_type_cfg_cmd *)desc.data;
tx_req->ot_vlan_type = cpu_to_le16(hdev->vlan_type_cfg.tx_ot_vlan_type);
tx_req->in_vlan_type = cpu_to_le16(hdev->vlan_type_cfg.tx_in_vlan_type);
status = hclge_cmd_send(&hdev->hw, &desc, 1);
if (status)
dev_err(&hdev->pdev->dev,
"Send txvlan protocol type command fail, ret =%d\n",
status);
return status;
}
static int hclge_init_vlan_filter(struct hclge_dev *hdev)
{
struct hclge_vport *vport;
int ret;
int i;
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
return hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_VF,
HCLGE_FILTER_FE_EGRESS_V1_B,
true, 0);
/* for revision 0x21, vf vlan filter is per function */
for (i = 0; i < hdev->num_alloc_vport; i++) {
vport = &hdev->vport[i];
ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_VF,
HCLGE_FILTER_FE_EGRESS, true,
vport->vport_id);
if (ret)
return ret;
vport->cur_vlan_fltr_en = true;
}
return hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_PORT,
HCLGE_FILTER_FE_INGRESS, true, 0);
}
static int hclge_init_vlan_type(struct hclge_dev *hdev)
{
hdev->vlan_type_cfg.rx_in_fst_vlan_type = ETH_P_8021Q;
hdev->vlan_type_cfg.rx_in_sec_vlan_type = ETH_P_8021Q;
hdev->vlan_type_cfg.rx_ot_fst_vlan_type = ETH_P_8021Q;
hdev->vlan_type_cfg.rx_ot_sec_vlan_type = ETH_P_8021Q;
hdev->vlan_type_cfg.tx_ot_vlan_type = ETH_P_8021Q;
hdev->vlan_type_cfg.tx_in_vlan_type = ETH_P_8021Q;
return hclge_set_vlan_protocol_type(hdev);
}
static int hclge_init_vport_vlan_offload(struct hclge_dev *hdev)
{
struct hclge_port_base_vlan_config *cfg;
struct hclge_vport *vport;
int ret;
int i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
vport = &hdev->vport[i];
cfg = &vport->port_base_vlan_cfg;
ret = hclge_vlan_offload_cfg(vport, cfg->state,
cfg->vlan_info.vlan_tag,
cfg->vlan_info.qos);
if (ret)
return ret;
}
return 0;
}
static int hclge_init_vlan_config(struct hclge_dev *hdev)
{
struct hnae3_handle *handle = &hdev->vport[0].nic;
int ret;
ret = hclge_init_vlan_filter(hdev);
if (ret)
return ret;
ret = hclge_init_vlan_type(hdev);
if (ret)
return ret;
ret = hclge_init_vport_vlan_offload(hdev);
if (ret)
return ret;
return hclge_set_vlan_filter(handle, htons(ETH_P_8021Q), 0, false);
}
static void hclge_add_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id,
bool writen_to_tbl)
{
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_dev *hdev = vport->back;
mutex_lock(&hdev->vport_lock);
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
if (vlan->vlan_id == vlan_id) {
mutex_unlock(&hdev->vport_lock);
return;
}
}
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
if (!vlan) {
mutex_unlock(&hdev->vport_lock);
return;
}
vlan->hd_tbl_status = writen_to_tbl;
vlan->vlan_id = vlan_id;
list_add_tail(&vlan->node, &vport->vlan_list);
mutex_unlock(&hdev->vport_lock);
}
static int hclge_add_vport_all_vlan_table(struct hclge_vport *vport)
{
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_dev *hdev = vport->back;
int ret;
mutex_lock(&hdev->vport_lock);
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
if (!vlan->hd_tbl_status) {
ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
vport->vport_id,
vlan->vlan_id, false);
if (ret) {
dev_err(&hdev->pdev->dev,
"restore vport vlan list failed, ret=%d\n",
ret);
mutex_unlock(&hdev->vport_lock);
return ret;
}
}
vlan->hd_tbl_status = true;
}
mutex_unlock(&hdev->vport_lock);
return 0;
}
static void hclge_rm_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id,
bool is_write_tbl)
{
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_dev *hdev = vport->back;
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
if (vlan->vlan_id == vlan_id) {
if (is_write_tbl && vlan->hd_tbl_status)
hclge_set_vlan_filter_hw(hdev,
htons(ETH_P_8021Q),
vport->vport_id,
vlan_id,
true);
list_del(&vlan->node);
kfree(vlan);
break;
}
}
}
void hclge_rm_vport_all_vlan_table(struct hclge_vport *vport, bool is_del_list)
{
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_dev *hdev = vport->back;
mutex_lock(&hdev->vport_lock);
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
if (vlan->hd_tbl_status)
hclge_set_vlan_filter_hw(hdev,
htons(ETH_P_8021Q),
vport->vport_id,
vlan->vlan_id,
true);
vlan->hd_tbl_status = false;
if (is_del_list) {
list_del(&vlan->node);
kfree(vlan);
}
}
clear_bit(vport->vport_id, hdev->vf_vlan_full);
mutex_unlock(&hdev->vport_lock);
}
void hclge_uninit_vport_vlan_table(struct hclge_dev *hdev)
{
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_vport *vport;
int i;
mutex_lock(&hdev->vport_lock);
for (i = 0; i < hdev->num_alloc_vport; i++) {
vport = &hdev->vport[i];
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
list_del(&vlan->node);
kfree(vlan);
}
}
mutex_unlock(&hdev->vport_lock);
}
void hclge_restore_vport_port_base_vlan_config(struct hclge_dev *hdev)
{
struct hclge_vlan_info *vlan_info;
struct hclge_vport *vport;
u16 vlan_proto;
u16 vlan_id;
u16 state;
int vf_id;
int ret;
/* PF should restore all vfs port base vlan */
for (vf_id = 0; vf_id < hdev->num_alloc_vfs; vf_id++) {
vport = &hdev->vport[vf_id + HCLGE_VF_VPORT_START_NUM];
vlan_info = vport->port_base_vlan_cfg.tbl_sta ?
&vport->port_base_vlan_cfg.vlan_info :
&vport->port_base_vlan_cfg.old_vlan_info;
vlan_id = vlan_info->vlan_tag;
vlan_proto = vlan_info->vlan_proto;
state = vport->port_base_vlan_cfg.state;
if (state != HNAE3_PORT_BASE_VLAN_DISABLE) {
clear_bit(vport->vport_id, hdev->vlan_table[vlan_id]);
ret = hclge_set_vlan_filter_hw(hdev, htons(vlan_proto),
vport->vport_id,
vlan_id, false);
vport->port_base_vlan_cfg.tbl_sta = ret == 0;
}
}
}
void hclge_restore_vport_vlan_table(struct hclge_vport *vport)
{
struct hclge_vport_vlan_cfg *vlan, *tmp;
struct hclge_dev *hdev = vport->back;
int ret;
mutex_lock(&hdev->vport_lock);
if (vport->port_base_vlan_cfg.state == HNAE3_PORT_BASE_VLAN_DISABLE) {
list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
vport->vport_id,
vlan->vlan_id, false);
if (ret)
break;
vlan->hd_tbl_status = true;
}
}
mutex_unlock(&hdev->vport_lock);
}
/* For global reset and imp reset, hardware will clear the mac table,
* so we change the mac address state from ACTIVE to TO_ADD, then they
* can be restored in the service task after reset complete. Furtherly,
* the mac addresses with state TO_DEL or DEL_FAIL are unnecessary to
* be restored after reset, so just remove these mac nodes from mac_list.
*/
static void hclge_mac_node_convert_for_reset(struct list_head *list)
{
struct hclge_mac_node *mac_node, *tmp;
list_for_each_entry_safe(mac_node, tmp, list, node) {
if (mac_node->state == HCLGE_MAC_ACTIVE) {
mac_node->state = HCLGE_MAC_TO_ADD;
} else if (mac_node->state == HCLGE_MAC_TO_DEL) {
list_del(&mac_node->node);
kfree(mac_node);
}
}
}
void hclge_restore_mac_table_common(struct hclge_vport *vport)
{
spin_lock_bh(&vport->mac_list_lock);
hclge_mac_node_convert_for_reset(&vport->uc_mac_list);
hclge_mac_node_convert_for_reset(&vport->mc_mac_list);
set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
spin_unlock_bh(&vport->mac_list_lock);
}
static void hclge_restore_hw_table(struct hclge_dev *hdev)
{
struct hclge_vport *vport = &hdev->vport[0];
struct hnae3_handle *handle = &vport->nic;
hclge_restore_mac_table_common(vport);
hclge_restore_vport_port_base_vlan_config(hdev);
hclge_restore_vport_vlan_table(vport);
set_bit(HCLGE_STATE_FD_USER_DEF_CHANGED, &hdev->state);
hclge_restore_fd_entries(handle);
}
int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
if (vport->port_base_vlan_cfg.state == HNAE3_PORT_BASE_VLAN_DISABLE) {
vport->rxvlan_cfg.strip_tag1_en = false;
vport->rxvlan_cfg.strip_tag2_en = enable;
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->rxvlan_cfg.strip_tag2_discard_en = false;
} else {
vport->rxvlan_cfg.strip_tag1_en = enable;
vport->rxvlan_cfg.strip_tag2_en = true;
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->rxvlan_cfg.strip_tag2_discard_en = true;
}
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
vport->rxvlan_cfg.strip_tag1_discard_en = false;
vport->rxvlan_cfg.vlan1_vlan_prionly = false;
vport->rxvlan_cfg.vlan2_vlan_prionly = false;
vport->rxvlan_cfg.rx_vlan_offload_en = enable;
return hclge_set_vlan_rx_offload_cfg(vport);
}
static void hclge_set_vport_vlan_fltr_change(struct hclge_vport *vport)
{
struct hclge_dev *hdev = vport->back;
if (test_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, hdev->ae_dev->caps))
set_bit(HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE, &vport->state);
}
static int hclge_update_vlan_filter_entries(struct hclge_vport *vport,
u16 port_base_vlan_state,
struct hclge_vlan_info *new_info,
struct hclge_vlan_info *old_info)
{
struct hclge_dev *hdev = vport->back;
int ret;
if (port_base_vlan_state == HNAE3_PORT_BASE_VLAN_ENABLE) {
hclge_rm_vport_all_vlan_table(vport, false);
/* force clear VLAN 0 */
ret = hclge_set_vf_vlan_common(hdev, vport->vport_id, true, 0);
if (ret)
return ret;
return hclge_set_vlan_filter_hw(hdev,
htons(new_info->vlan_proto),
vport->vport_id,
new_info->vlan_tag,
false);
}
vport->port_base_vlan_cfg.tbl_sta = false;
/* force add VLAN 0 */
ret = hclge_set_vf_vlan_common(hdev, vport->vport_id, false, 0);
if (ret)
return ret;
ret = hclge_set_vlan_filter_hw(hdev, htons(old_info->vlan_proto),
vport->vport_id, old_info->vlan_tag,
true);
if (ret)
return ret;
return hclge_add_vport_all_vlan_table(vport);
}
static bool hclge_need_update_vlan_filter(const struct hclge_vlan_info *new_cfg,
const struct hclge_vlan_info *old_cfg)
{
if (new_cfg->vlan_tag != old_cfg->vlan_tag)
return true;
if (new_cfg->vlan_tag == 0 && (new_cfg->qos == 0 || old_cfg->qos == 0))
return true;
return false;
}
static int hclge_modify_port_base_vlan_tag(struct hclge_vport *vport,
struct hclge_vlan_info *new_info,
struct hclge_vlan_info *old_info)
{
struct hclge_dev *hdev = vport->back;
int ret;
/* add new VLAN tag */
ret = hclge_set_vlan_filter_hw(hdev, htons(new_info->vlan_proto),
vport->vport_id, new_info->vlan_tag,
false);
if (ret)
return ret;
vport->port_base_vlan_cfg.tbl_sta = false;
/* remove old VLAN tag */
if (old_info->vlan_tag == 0)
ret = hclge_set_vf_vlan_common(hdev, vport->vport_id,
true, 0);
else
ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
vport->vport_id,
old_info->vlan_tag, true);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to clear vport%u port base vlan %u, ret = %d.\n",
vport->vport_id, old_info->vlan_tag, ret);
return ret;
}
int hclge_update_port_base_vlan_cfg(struct hclge_vport *vport, u16 state,
struct hclge_vlan_info *vlan_info)
{
struct hnae3_handle *nic = &vport->nic;
struct hclge_vlan_info *old_vlan_info;
int ret;
old_vlan_info = &vport->port_base_vlan_cfg.vlan_info;
ret = hclge_vlan_offload_cfg(vport, state, vlan_info->vlan_tag,
vlan_info->qos);
if (ret)
return ret;
if (!hclge_need_update_vlan_filter(vlan_info, old_vlan_info))
goto out;
if (state == HNAE3_PORT_BASE_VLAN_MODIFY)
ret = hclge_modify_port_base_vlan_tag(vport, vlan_info,
old_vlan_info);
else
ret = hclge_update_vlan_filter_entries(vport, state, vlan_info,
old_vlan_info);
if (ret)
return ret;
out:
vport->port_base_vlan_cfg.state = state;
if (state == HNAE3_PORT_BASE_VLAN_DISABLE)
nic->port_base_vlan_state = HNAE3_PORT_BASE_VLAN_DISABLE;
else
nic->port_base_vlan_state = HNAE3_PORT_BASE_VLAN_ENABLE;
vport->port_base_vlan_cfg.old_vlan_info = *old_vlan_info;
vport->port_base_vlan_cfg.vlan_info = *vlan_info;
vport->port_base_vlan_cfg.tbl_sta = true;
hclge_set_vport_vlan_fltr_change(vport);
return 0;
}
static u16 hclge_get_port_base_vlan_state(struct hclge_vport *vport,
enum hnae3_port_base_vlan_state state,
u16 vlan, u8 qos)
{
if (state == HNAE3_PORT_BASE_VLAN_DISABLE) {
if (!vlan && !qos)
return HNAE3_PORT_BASE_VLAN_NOCHANGE;
return HNAE3_PORT_BASE_VLAN_ENABLE;
}
if (!vlan && !qos)
return HNAE3_PORT_BASE_VLAN_DISABLE;
if (vport->port_base_vlan_cfg.vlan_info.vlan_tag == vlan &&
vport->port_base_vlan_cfg.vlan_info.qos == qos)
return HNAE3_PORT_BASE_VLAN_NOCHANGE;
return HNAE3_PORT_BASE_VLAN_MODIFY;
}
static int hclge_set_vf_vlan_filter(struct hnae3_handle *handle, int vfid,
u16 vlan, u8 qos, __be16 proto)
{
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_vlan_info vlan_info;
u16 state;
int ret;
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
return -EOPNOTSUPP;
vport = hclge_get_vf_vport(hdev, vfid);
if (!vport)
return -EINVAL;
/* qos is a 3 bits value, so can not be bigger than 7 */
if (vlan > VLAN_N_VID - 1 || qos > 7)
return -EINVAL;
if (proto != htons(ETH_P_8021Q))
return -EPROTONOSUPPORT;
state = hclge_get_port_base_vlan_state(vport,
vport->port_base_vlan_cfg.state,
vlan, qos);
if (state == HNAE3_PORT_BASE_VLAN_NOCHANGE)
return 0;
vlan_info.vlan_tag = vlan;
vlan_info.qos = qos;
vlan_info.vlan_proto = ntohs(proto);
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
ret = hclge_update_port_base_vlan_cfg(vport, state, &vlan_info);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to update port base vlan for vf %d, ret = %d\n",
vfid, ret);
return ret;
}
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
/* there is a timewindow for PF to know VF unalive, it may
* cause send mailbox fail, but it doesn't matter, VF will
* query it when reinit.
* for DEVICE_VERSION_V3, vf doesn't need to know about the port based
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
* VLAN state.
*/
if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3) {
if (test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
(void)hclge_push_vf_port_base_vlan_info(&hdev->vport[0],
vport->vport_id,
state,
&vlan_info);
else
set_bit(HCLGE_VPORT_NEED_NOTIFY_VF_VLAN,
&vport->need_notify);
}
net: hns3: refine the VLAN tag handle for port based VLAN For DEVICE_VERSION_V2, the hardware only supports max two layer VLAN tags, including port based tag inserted by hardware, tag in tx buffer descriptor(get from skb->tci) and tag in packet. For transmit packet: If port based VLAN disabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the Outer_VLAN_TAG field (tag near to DMAC) of tx buffer descriptor, otherwise it may be inserted after the tag in packet. If port based VLAN enabled, and vf driver gets a VLAN tag from skb, the VLAN tag must be filled to the VLAN_TAG field (tag far to DMAC) of tx buffer descriptor, otherwise it may be conflicted with port based VLAN, and raise a hardware error. For receive packet: The hardware will strip the VLAN tags and fill them in the rx buffer descriptor, no matter port based VLAN enable or not. Because port based VLAN tag is useless for stack, so vf driver needs to discard the port based VLAN tag get from rx buffer descriptor when port based VLAN enabled. So vf must know about the port based VLAN state. For DEVICE_VERSION_V3, the hardware provides some new configuration to improve it. For transmit packet: When enable tag shift mode, hardware will handle the VLAN tag in outer_VLAN_TAG field as VLAN_TAG, so it won't conflict with port based VLAN. And hardware also make sure the tag before the tag in packet. So vf driver doesn't need to specify the tag position according to the port based VLAN state anymore. For receive packet: When enable discard mode, hardware will strip and discard the port based VLAN tag, so vf driver doesn't need to identify it from rx buffer descriptor. So modify the port based VLAN configuration, simplify the process for vf handling the VLAN tag. Signed-off-by: Guojia Liao <liaoguojia@huawei.com> Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-12-06 04:06:15 +00:00
return 0;
}
static void hclge_clear_vf_vlan(struct hclge_dev *hdev)
{
struct hclge_vlan_info *vlan_info;
struct hclge_vport *vport;
int ret;
int vf;
/* clear port base vlan for all vf */
for (vf = HCLGE_VF_VPORT_START_NUM; vf < hdev->num_alloc_vport; vf++) {
vport = &hdev->vport[vf];
vlan_info = &vport->port_base_vlan_cfg.vlan_info;
ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
vport->vport_id,
vlan_info->vlan_tag, true);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to clear vf vlan for vf%d, ret = %d\n",
vf - HCLGE_VF_VPORT_START_NUM, ret);
}
}
int hclge_set_vlan_filter(struct hnae3_handle *handle, __be16 proto,
u16 vlan_id, bool is_kill)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
bool writen_to_tbl = false;
int ret = 0;
/* When device is resetting or reset failed, firmware is unable to
* handle mailbox. Just record the vlan id, and remove it after
* reset finished.
*/
mutex_lock(&hdev->vport_lock);
if ((test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
test_bit(HCLGE_STATE_RST_FAIL, &hdev->state)) && is_kill) {
set_bit(vlan_id, vport->vlan_del_fail_bmap);
mutex_unlock(&hdev->vport_lock);
return -EBUSY;
} else if (!is_kill && test_bit(vlan_id, vport->vlan_del_fail_bmap)) {
clear_bit(vlan_id, vport->vlan_del_fail_bmap);
}
mutex_unlock(&hdev->vport_lock);
/* when port base vlan enabled, we use port base vlan as the vlan
* filter entry. In this case, we don't update vlan filter table
* when user add new vlan or remove exist vlan, just update the vport
* vlan list. The vlan id in vlan list will be writen in vlan filter
* table until port base vlan disabled
*/
if (handle->port_base_vlan_state == HNAE3_PORT_BASE_VLAN_DISABLE) {
ret = hclge_set_vlan_filter_hw(hdev, proto, vport->vport_id,
vlan_id, is_kill);
writen_to_tbl = true;
}
if (!ret) {
if (!is_kill) {
hclge_add_vport_vlan_table(vport, vlan_id,
writen_to_tbl);
} else if (is_kill && vlan_id != 0) {
mutex_lock(&hdev->vport_lock);
hclge_rm_vport_vlan_table(vport, vlan_id, false);
mutex_unlock(&hdev->vport_lock);
}
} else if (is_kill) {
/* when remove hw vlan filter failed, record the vlan id,
* and try to remove it from hw later, to be consistence
* with stack
*/
mutex_lock(&hdev->vport_lock);
set_bit(vlan_id, vport->vlan_del_fail_bmap);
mutex_unlock(&hdev->vport_lock);
}
hclge_set_vport_vlan_fltr_change(vport);
return ret;
}
static void hclge_sync_vlan_fltr_state(struct hclge_dev *hdev)
{
struct hclge_vport *vport;
int ret;
u16 i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
vport = &hdev->vport[i];
if (!test_and_clear_bit(HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE,
&vport->state))
continue;
ret = hclge_enable_vport_vlan_filter(vport,
vport->req_vlan_fltr_en);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to sync vlan filter state for vport%u, ret = %d\n",
vport->vport_id, ret);
set_bit(HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE,
&vport->state);
return;
}
}
}
static void hclge_sync_vlan_filter(struct hclge_dev *hdev)
{
#define HCLGE_MAX_SYNC_COUNT 60
int i, ret, sync_cnt = 0;
u16 vlan_id;
mutex_lock(&hdev->vport_lock);
/* start from vport 1 for PF is always alive */
for (i = 0; i < hdev->num_alloc_vport; i++) {
struct hclge_vport *vport = &hdev->vport[i];
vlan_id = find_first_bit(vport->vlan_del_fail_bmap,
VLAN_N_VID);
while (vlan_id != VLAN_N_VID) {
ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
vport->vport_id, vlan_id,
true);
if (ret && ret != -EINVAL) {
mutex_unlock(&hdev->vport_lock);
return;
}
clear_bit(vlan_id, vport->vlan_del_fail_bmap);
hclge_rm_vport_vlan_table(vport, vlan_id, false);
hclge_set_vport_vlan_fltr_change(vport);
sync_cnt++;
if (sync_cnt >= HCLGE_MAX_SYNC_COUNT) {
mutex_unlock(&hdev->vport_lock);
return;
}
vlan_id = find_first_bit(vport->vlan_del_fail_bmap,
VLAN_N_VID);
}
}
mutex_unlock(&hdev->vport_lock);
hclge_sync_vlan_fltr_state(hdev);
}
static int hclge_set_mac_mtu(struct hclge_dev *hdev, int new_mps)
{
struct hclge_config_max_frm_size_cmd *req;
struct hclge_desc desc;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAX_FRM_SIZE, false);
req = (struct hclge_config_max_frm_size_cmd *)desc.data;
req->max_frm_size = cpu_to_le16(new_mps);
req->min_frm_size = HCLGE_MAC_MIN_FRAME;
return hclge_cmd_send(&hdev->hw, &desc, 1);
}
static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return hclge_set_vport_mtu(vport, new_mtu);
}
int hclge_set_vport_mtu(struct hclge_vport *vport, int new_mtu)
{
struct hclge_dev *hdev = vport->back;
int i, max_frm_size, ret;
/* 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 > hdev->ae_dev->dev_specs.max_frm_size)
return -EINVAL;
max_frm_size = max(max_frm_size, HCLGE_MAC_DEFAULT_FRAME);
mutex_lock(&hdev->vport_lock);
/* VF's mps must fit within hdev->mps */
if (vport->vport_id && max_frm_size > hdev->mps) {
mutex_unlock(&hdev->vport_lock);
return -EINVAL;
} else if (vport->vport_id) {
vport->mps = max_frm_size;
mutex_unlock(&hdev->vport_lock);
return 0;
}
/* PF's mps must be greater then VF's mps */
for (i = 1; i < hdev->num_alloc_vport; i++)
if (max_frm_size < hdev->vport[i].mps) {
dev_err(&hdev->pdev->dev,
"failed to set pf mtu for less than vport %d, mps = %u.\n",
i, hdev->vport[i].mps);
mutex_unlock(&hdev->vport_lock);
return -EINVAL;
}
hclge_notify_client(hdev, HNAE3_DOWN_CLIENT);
ret = hclge_set_mac_mtu(hdev, max_frm_size);
if (ret) {
dev_err(&hdev->pdev->dev,
"Change mtu fail, ret =%d\n", ret);
goto out;
}
hdev->mps = max_frm_size;
vport->mps = max_frm_size;
ret = hclge_buffer_alloc(hdev);
if (ret)
dev_err(&hdev->pdev->dev,
"Allocate buffer fail, ret =%d\n", ret);
out:
hclge_notify_client(hdev, HNAE3_UP_CLIENT);
mutex_unlock(&hdev->vport_lock);
return ret;
}
static int hclge_reset_tqp_cmd_send(struct hclge_dev *hdev, u16 queue_id,
bool enable)
{
struct hclge_reset_tqp_queue_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RESET_TQP_QUEUE, false);
req = (struct hclge_reset_tqp_queue_cmd *)desc.data;
req->tqp_id = cpu_to_le16(queue_id);
if (enable)
hnae3_set_bit(req->reset_req, HCLGE_TQP_RESET_B, 1U);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"Send tqp reset cmd error, status =%d\n", ret);
return ret;
}
return 0;
}
static int hclge_get_reset_status(struct hclge_dev *hdev, u16 queue_id,
u8 *reset_status)
{
struct hclge_reset_tqp_queue_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RESET_TQP_QUEUE, true);
req = (struct hclge_reset_tqp_queue_cmd *)desc.data;
req->tqp_id = cpu_to_le16(queue_id);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"Get reset status error, status =%d\n", ret);
return ret;
}
*reset_status = hnae3_get_bit(req->ready_to_reset, HCLGE_TQP_RESET_B);
return 0;
}
u16 hclge_covert_handle_qid_global(struct hnae3_handle *handle, u16 queue_id)
{
struct hclge_comm_tqp *tqp;
struct hnae3_queue *queue;
queue = handle->kinfo.tqp[queue_id];
tqp = container_of(queue, struct hclge_comm_tqp, q);
return tqp->index;
}
static int hclge_reset_tqp_cmd(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u16 reset_try_times = 0;
u8 reset_status;
u16 queue_gid;
int ret;
u16 i;
for (i = 0; i < handle->kinfo.num_tqps; i++) {
queue_gid = hclge_covert_handle_qid_global(handle, i);
ret = hclge_reset_tqp_cmd_send(hdev, queue_gid, true);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to send reset tqp cmd, ret = %d\n",
ret);
return ret;
}
while (reset_try_times++ < HCLGE_TQP_RESET_TRY_TIMES) {
ret = hclge_get_reset_status(hdev, queue_gid,
&reset_status);
if (ret)
return ret;
if (reset_status)
break;
/* Wait for tqp hw reset */
usleep_range(1000, 1200);
}
if (reset_try_times >= HCLGE_TQP_RESET_TRY_TIMES) {
dev_err(&hdev->pdev->dev,
"wait for tqp hw reset timeout\n");
return -ETIME;
}
ret = hclge_reset_tqp_cmd_send(hdev, queue_gid, false);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to deassert soft reset, ret = %d\n",
ret);
return ret;
}
reset_try_times = 0;
}
return 0;
}
static int hclge_reset_rcb(struct hnae3_handle *handle)
{
#define HCLGE_RESET_RCB_NOT_SUPPORT 0U
#define HCLGE_RESET_RCB_SUCCESS 1U
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_reset_cmd *req;
struct hclge_desc desc;
u8 return_status;
u16 queue_gid;
int ret;
queue_gid = hclge_covert_handle_qid_global(handle, 0);
req = (struct hclge_reset_cmd *)desc.data;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_RST_TRIGGER, false);
hnae3_set_bit(req->fun_reset_rcb, HCLGE_CFG_RESET_RCB_B, 1);
req->fun_reset_rcb_vqid_start = cpu_to_le16(queue_gid);
req->fun_reset_rcb_vqid_num = cpu_to_le16(handle->kinfo.num_tqps);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to send rcb reset cmd, ret = %d\n", ret);
return ret;
}
return_status = req->fun_reset_rcb_return_status;
if (return_status == HCLGE_RESET_RCB_SUCCESS)
return 0;
if (return_status != HCLGE_RESET_RCB_NOT_SUPPORT) {
dev_err(&hdev->pdev->dev, "failed to reset rcb, ret = %u\n",
return_status);
return -EIO;
}
/* if reset rcb cmd is unsupported, we need to send reset tqp cmd
* again to reset all tqps
*/
return hclge_reset_tqp_cmd(handle);
}
int hclge_reset_tqp(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int ret;
/* only need to disable PF's tqp */
if (!vport->vport_id) {
ret = hclge_tqp_enable(handle, false);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to disable tqp, ret = %d\n", ret);
return ret;
}
}
return hclge_reset_rcb(handle);
}
static u32 hclge_get_fw_version(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return hdev->fw_version;
}
static void hclge_set_flowctrl_adv(struct hclge_dev *hdev, u32 rx_en, u32 tx_en)
{
struct phy_device *phydev = hdev->hw.mac.phydev;
if (!phydev)
return;
phy_set_asym_pause(phydev, rx_en, tx_en);
}
static int hclge_cfg_pauseparam(struct hclge_dev *hdev, u32 rx_en, u32 tx_en)
{
int ret;
if (hdev->tm_info.fc_mode == HCLGE_FC_PFC)
return 0;
ret = hclge_mac_pause_en_cfg(hdev, tx_en, rx_en);
if (ret)
dev_err(&hdev->pdev->dev,
"configure pauseparam error, ret = %d.\n", ret);
return ret;
}
int hclge_cfg_flowctrl(struct hclge_dev *hdev)
{
struct phy_device *phydev = hdev->hw.mac.phydev;
u16 remote_advertising = 0;
u16 local_advertising;
u32 rx_pause, tx_pause;
u8 flowctl;
if (!phydev->link)
return 0;
if (!phydev->autoneg)
return hclge_mac_pause_setup_hw(hdev);
local_advertising = linkmode_adv_to_lcl_adv_t(phydev->advertising);
if (phydev->pause)
remote_advertising = LPA_PAUSE_CAP;
if (phydev->asym_pause)
remote_advertising |= LPA_PAUSE_ASYM;
flowctl = mii_resolve_flowctrl_fdx(local_advertising,
remote_advertising);
tx_pause = flowctl & FLOW_CTRL_TX;
rx_pause = flowctl & FLOW_CTRL_RX;
if (phydev->duplex == HCLGE_MAC_HALF) {
tx_pause = 0;
rx_pause = 0;
}
return hclge_cfg_pauseparam(hdev, rx_pause, tx_pause);
}
static void hclge_get_pauseparam(struct hnae3_handle *handle, u32 *auto_neg,
u32 *rx_en, u32 *tx_en)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u8 media_type = hdev->hw.mac.media_type;
*auto_neg = (media_type == HNAE3_MEDIA_TYPE_COPPER) ?
hclge_get_autoneg(handle) : 0;
if (hdev->tm_info.fc_mode == HCLGE_FC_PFC) {
*rx_en = 0;
*tx_en = 0;
return;
}
if (hdev->tm_info.fc_mode == HCLGE_FC_RX_PAUSE) {
*rx_en = 1;
*tx_en = 0;
} else if (hdev->tm_info.fc_mode == HCLGE_FC_TX_PAUSE) {
*tx_en = 1;
*rx_en = 0;
} else if (hdev->tm_info.fc_mode == HCLGE_FC_FULL) {
*rx_en = 1;
*tx_en = 1;
} else {
*rx_en = 0;
*tx_en = 0;
}
}
static void hclge_record_user_pauseparam(struct hclge_dev *hdev,
u32 rx_en, u32 tx_en)
{
if (rx_en && tx_en)
hdev->fc_mode_last_time = HCLGE_FC_FULL;
else if (rx_en && !tx_en)
hdev->fc_mode_last_time = HCLGE_FC_RX_PAUSE;
else if (!rx_en && tx_en)
hdev->fc_mode_last_time = HCLGE_FC_TX_PAUSE;
else
hdev->fc_mode_last_time = HCLGE_FC_NONE;
hdev->tm_info.fc_mode = hdev->fc_mode_last_time;
}
static int hclge_set_pauseparam(struct hnae3_handle *handle, u32 auto_neg,
u32 rx_en, u32 tx_en)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct phy_device *phydev = hdev->hw.mac.phydev;
u32 fc_autoneg;
if (phydev || hnae3_dev_phy_imp_supported(hdev)) {
fc_autoneg = hclge_get_autoneg(handle);
if (auto_neg != fc_autoneg) {
dev_info(&hdev->pdev->dev,
"To change autoneg please use: ethtool -s <dev> autoneg <on|off>\n");
return -EOPNOTSUPP;
}
}
if (hdev->tm_info.fc_mode == HCLGE_FC_PFC) {
dev_info(&hdev->pdev->dev,
"Priority flow control enabled. Cannot set link flow control.\n");
return -EOPNOTSUPP;
}
hclge_set_flowctrl_adv(hdev, rx_en, tx_en);
hclge_record_user_pauseparam(hdev, rx_en, tx_en);
if (!auto_neg || hnae3_dev_phy_imp_supported(hdev))
return hclge_cfg_pauseparam(hdev, rx_en, tx_en);
if (phydev)
return phy_start_aneg(phydev);
return -EOPNOTSUPP;
}
static void hclge_get_ksettings_an_result(struct hnae3_handle *handle,
u8 *auto_neg, u32 *speed, u8 *duplex, u32 *lane_num)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
if (speed)
*speed = hdev->hw.mac.speed;
if (duplex)
*duplex = hdev->hw.mac.duplex;
if (auto_neg)
*auto_neg = hdev->hw.mac.autoneg;
if (lane_num)
*lane_num = hdev->hw.mac.lane_num;
}
static void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type,
u8 *module_type)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
/* When nic is down, the service task is not running, doesn't update
* the port information per second. Query the port information before
* return the media type, ensure getting the correct media information.
*/
hclge_update_port_info(hdev);
if (media_type)
*media_type = hdev->hw.mac.media_type;
if (module_type)
*module_type = hdev->hw.mac.module_type;
}
static void hclge_get_mdix_mode(struct hnae3_handle *handle,
u8 *tp_mdix_ctrl, u8 *tp_mdix)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct phy_device *phydev = hdev->hw.mac.phydev;
int mdix_ctrl, mdix, is_resolved;
unsigned int retval;
if (!phydev) {
*tp_mdix_ctrl = ETH_TP_MDI_INVALID;
*tp_mdix = ETH_TP_MDI_INVALID;
return;
}
phy_write(phydev, HCLGE_PHY_PAGE_REG, HCLGE_PHY_PAGE_MDIX);
retval = phy_read(phydev, HCLGE_PHY_CSC_REG);
mdix_ctrl = hnae3_get_field(retval, HCLGE_PHY_MDIX_CTRL_M,
HCLGE_PHY_MDIX_CTRL_S);
retval = phy_read(phydev, HCLGE_PHY_CSS_REG);
mdix = hnae3_get_bit(retval, HCLGE_PHY_MDIX_STATUS_B);
is_resolved = hnae3_get_bit(retval, HCLGE_PHY_SPEED_DUP_RESOLVE_B);
phy_write(phydev, HCLGE_PHY_PAGE_REG, HCLGE_PHY_PAGE_COPPER);
switch (mdix_ctrl) {
case 0x0:
*tp_mdix_ctrl = ETH_TP_MDI;
break;
case 0x1:
*tp_mdix_ctrl = ETH_TP_MDI_X;
break;
case 0x3:
*tp_mdix_ctrl = ETH_TP_MDI_AUTO;
break;
default:
*tp_mdix_ctrl = ETH_TP_MDI_INVALID;
break;
}
if (!is_resolved)
*tp_mdix = ETH_TP_MDI_INVALID;
else if (mdix)
*tp_mdix = ETH_TP_MDI_X;
else
*tp_mdix = ETH_TP_MDI;
}
static void hclge_info_show(struct hclge_dev *hdev)
{
struct hnae3_handle *handle = &hdev->vport->nic;
struct device *dev = &hdev->pdev->dev;
dev_info(dev, "PF info begin:\n");
dev_info(dev, "Task queue pairs numbers: %u\n", hdev->num_tqps);
dev_info(dev, "Desc num per TX queue: %u\n", hdev->num_tx_desc);
dev_info(dev, "Desc num per RX queue: %u\n", hdev->num_rx_desc);
dev_info(dev, "Numbers of vports: %u\n", hdev->num_alloc_vport);
dev_info(dev, "Numbers of VF for this PF: %u\n", hdev->num_req_vfs);
dev_info(dev, "HW tc map: 0x%x\n", hdev->hw_tc_map);
dev_info(dev, "Total buffer size for TX/RX: %u\n", hdev->pkt_buf_size);
dev_info(dev, "TX buffer size for each TC: %u\n", hdev->tx_buf_size);
dev_info(dev, "DV buffer size for each TC: %u\n", hdev->dv_buf_size);
dev_info(dev, "This is %s PF\n",
hdev->flag & HCLGE_FLAG_MAIN ? "main" : "not main");
dev_info(dev, "DCB %s\n",
handle->kinfo.tc_info.dcb_ets_active ? "enable" : "disable");
dev_info(dev, "MQPRIO %s\n",
handle->kinfo.tc_info.mqprio_active ? "enable" : "disable");
dev_info(dev, "Default tx spare buffer size: %u\n",
hdev->tx_spare_buf_size);
dev_info(dev, "PF info end.\n");
}
static int hclge_init_nic_client_instance(struct hnae3_ae_dev *ae_dev,
struct hclge_vport *vport)
{
struct hnae3_client *client = vport->nic.client;
struct hclge_dev *hdev = ae_dev->priv;
int rst_cnt = hdev->rst_stats.reset_cnt;
int ret;
ret = client->ops->init_instance(&vport->nic);
if (ret)
return ret;
set_bit(HCLGE_STATE_NIC_REGISTERED, &hdev->state);
if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
rst_cnt != hdev->rst_stats.reset_cnt) {
ret = -EBUSY;
goto init_nic_err;
}
/* Enable nic hw error interrupts */
ret = hclge_config_nic_hw_error(hdev, true);
if (ret) {
dev_err(&ae_dev->pdev->dev,
"fail(%d) to enable hw error interrupts\n", ret);
goto init_nic_err;
}
hnae3_set_client_init_flag(client, ae_dev, 1);
if (netif_msg_drv(&hdev->vport->nic))
hclge_info_show(hdev);
return ret;
init_nic_err:
clear_bit(HCLGE_STATE_NIC_REGISTERED, &hdev->state);
while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
msleep(HCLGE_WAIT_RESET_DONE);
client->ops->uninit_instance(&vport->nic, 0);
return ret;
}
static int hclge_init_roce_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_roce_supported(hdev) || !hdev->roce_client ||
!hdev->nic_client)
return 0;
client = hdev->roce_client;
ret = hclge_init_roce_base_info(vport);
if (ret)
return ret;
rst_cnt = hdev->rst_stats.reset_cnt;
ret = client->ops->init_instance(&vport->roce);
if (ret)
return ret;
set_bit(HCLGE_STATE_ROCE_REGISTERED, &hdev->state);
if (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state) ||
rst_cnt != hdev->rst_stats.reset_cnt) {
ret = -EBUSY;
goto init_roce_err;
}
/* Enable roce ras interrupts */
ret = hclge_config_rocee_ras_interrupt(hdev, true);
if (ret) {
dev_err(&ae_dev->pdev->dev,
"fail(%d) to enable roce ras interrupts\n", ret);
goto init_roce_err;
}
hnae3_set_client_init_flag(client, ae_dev, 1);
return 0;
init_roce_err:
clear_bit(HCLGE_STATE_ROCE_REGISTERED, &hdev->state);
while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
msleep(HCLGE_WAIT_RESET_DONE);
hdev->roce_client->ops->uninit_instance(&vport->roce, 0);
return ret;
}
static int hclge_init_client_instance(struct hnae3_client *client,
struct hnae3_ae_dev *ae_dev)
{
struct hclge_dev *hdev = ae_dev->priv;
struct hclge_vport *vport = &hdev->vport[0];
int ret;
switch (client->type) {
case HNAE3_CLIENT_KNIC:
hdev->nic_client = client;
vport->nic.client = client;
ret = hclge_init_nic_client_instance(ae_dev, vport);
if (ret)
goto clear_nic;
ret = hclge_init_roce_client_instance(ae_dev, vport);
if (ret)
goto clear_roce;
break;
case HNAE3_CLIENT_ROCE:
if (hnae3_dev_roce_supported(hdev)) {
hdev->roce_client = client;
vport->roce.client = client;
}
ret = hclge_init_roce_client_instance(ae_dev, vport);
if (ret)
goto clear_roce;
break;
default:
return -EINVAL;
}
return 0;
clear_nic:
hdev->nic_client = NULL;
vport->nic.client = NULL;
return ret;
clear_roce:
hdev->roce_client = NULL;
vport->roce.client = NULL;
return ret;
}
static void hclge_uninit_client_instance(struct hnae3_client *client,
struct hnae3_ae_dev *ae_dev)
{
struct hclge_dev *hdev = ae_dev->priv;
struct hclge_vport *vport = &hdev->vport[0];
if (hdev->roce_client) {
clear_bit(HCLGE_STATE_ROCE_REGISTERED, &hdev->state);
while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
msleep(HCLGE_WAIT_RESET_DONE);
hdev->roce_client->ops->uninit_instance(&vport->roce, 0);
hdev->roce_client = NULL;
vport->roce.client = NULL;
}
if (client->type == HNAE3_CLIENT_ROCE)
return;
if (hdev->nic_client && client->ops->uninit_instance) {
clear_bit(HCLGE_STATE_NIC_REGISTERED, &hdev->state);
while (test_bit(HCLGE_STATE_RST_HANDLING, &hdev->state))
msleep(HCLGE_WAIT_RESET_DONE);
client->ops->uninit_instance(&vport->nic, 0);
hdev->nic_client = NULL;
vport->nic.client = NULL;
}
}
static int hclge_dev_mem_map(struct hclge_dev *hdev)
{
struct pci_dev *pdev = hdev->pdev;
struct hclge_hw *hw = &hdev->hw;
/* for device does not have device memory, return directly */
if (!(pci_select_bars(pdev, IORESOURCE_MEM) & BIT(HCLGE_MEM_BAR)))
return 0;
hw->hw.mem_base =
devm_ioremap_wc(&pdev->dev,
pci_resource_start(pdev, HCLGE_MEM_BAR),
pci_resource_len(pdev, HCLGE_MEM_BAR));
if (!hw->hw.mem_base) {
dev_err(&pdev->dev, "failed to map device memory\n");
return -EFAULT;
}
return 0;
}
static int hclge_pci_init(struct hclge_dev *hdev)
{
struct pci_dev *pdev = hdev->pdev;
struct hclge_hw *hw;
int ret;
ret = pci_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "failed to enable PCI device\n");
return ret;
}
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev,
"can't set consistent PCI DMA");
goto err_disable_device;
}
dev_warn(&pdev->dev, "set DMA mask to 32 bits\n");
}
ret = pci_request_regions(pdev, HCLGE_DRIVER_NAME);
if (ret) {
dev_err(&pdev->dev, "PCI request regions failed %d\n", ret);
goto err_disable_device;
}
pci_set_master(pdev);
hw = &hdev->hw;
hw->hw.io_base = pcim_iomap(pdev, 2, 0);
if (!hw->hw.io_base) {
dev_err(&pdev->dev, "Can't map configuration register space\n");
ret = -ENOMEM;
goto err_release_regions;
}
ret = hclge_dev_mem_map(hdev);
if (ret)
goto err_unmap_io_base;
hdev->num_req_vfs = pci_sriov_get_totalvfs(pdev);
return 0;
err_unmap_io_base:
pcim_iounmap(pdev, hdev->hw.hw.io_base);
err_release_regions:
pci_release_regions(pdev);
err_disable_device:
pci_disable_device(pdev);
return ret;
}
static void hclge_pci_uninit(struct hclge_dev *hdev)
{
struct pci_dev *pdev = hdev->pdev;
if (hdev->hw.hw.mem_base)
devm_iounmap(&pdev->dev, hdev->hw.hw.mem_base);
pcim_iounmap(pdev, hdev->hw.hw.io_base);
pci_free_irq_vectors(pdev);
pci_release_mem_regions(pdev);
pci_disable_device(pdev);
}
static void hclge_state_init(struct hclge_dev *hdev)
{
set_bit(HCLGE_STATE_SERVICE_INITED, &hdev->state);
set_bit(HCLGE_STATE_DOWN, &hdev->state);
clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state);
clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
clear_bit(HCLGE_STATE_RST_FAIL, &hdev->state);
clear_bit(HCLGE_STATE_MBX_SERVICE_SCHED, &hdev->state);
clear_bit(HCLGE_STATE_MBX_HANDLING, &hdev->state);
}
static void hclge_state_uninit(struct hclge_dev *hdev)
{
set_bit(HCLGE_STATE_DOWN, &hdev->state);
set_bit(HCLGE_STATE_REMOVING, &hdev->state);
if (hdev->reset_timer.function)
del_timer_sync(&hdev->reset_timer);
if (hdev->service_task.work.func)
cancel_delayed_work_sync(&hdev->service_task);
}
static void hclge_reset_prepare_general(struct hnae3_ae_dev *ae_dev,
enum hnae3_reset_type rst_type)
{
#define HCLGE_RESET_RETRY_WAIT_MS 500
#define HCLGE_RESET_RETRY_CNT 5
struct hclge_dev *hdev = ae_dev->priv;
int retry_cnt = 0;
int ret;
while (retry_cnt++ < HCLGE_RESET_RETRY_CNT) {
down(&hdev->reset_sem);
set_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
hdev->reset_type = rst_type;
ret = hclge_reset_prepare(hdev);
if (!ret && !hdev->reset_pending)
break;
dev_err(&hdev->pdev->dev,
"failed to prepare to reset, ret=%d, reset_pending:0x%lx, retry_cnt:%d\n",
ret, hdev->reset_pending, retry_cnt);
clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
up(&hdev->reset_sem);
msleep(HCLGE_RESET_RETRY_WAIT_MS);
}
/* disable misc vector before reset done */
hclge_enable_vector(&hdev->misc_vector, false);
set_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state);
if (hdev->reset_type == HNAE3_FLR_RESET)
hdev->rst_stats.flr_rst_cnt++;
}
static void hclge_reset_done(struct hnae3_ae_dev *ae_dev)
{
struct hclge_dev *hdev = ae_dev->priv;
int ret;
hclge_enable_vector(&hdev->misc_vector, true);
ret = hclge_reset_rebuild(hdev);
if (ret)
dev_err(&hdev->pdev->dev, "fail to rebuild, ret=%d\n", ret);
hdev->reset_type = HNAE3_NONE_RESET;
clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state);
up(&hdev->reset_sem);
}
static void hclge_clear_resetting_state(struct hclge_dev *hdev)
{
u16 i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
struct hclge_vport *vport = &hdev->vport[i];
int ret;
/* Send cmd to clear vport's FUNC_RST_ING */
ret = hclge_set_vf_rst(hdev, vport->vport_id, false);
if (ret)
dev_warn(&hdev->pdev->dev,
"clear vport(%u) rst failed %d!\n",
vport->vport_id, ret);
}
}
static int hclge_clear_hw_resource(struct hclge_dev *hdev)
{
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CLEAR_HW_RESOURCE, false);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
/* This new command is only supported by new firmware, it will
* fail with older firmware. Error value -EOPNOSUPP can only be
* returned by older firmware running this command, to keep code
* backward compatible we will override this value and return
* success.
*/
if (ret && ret != -EOPNOTSUPP) {
dev_err(&hdev->pdev->dev,
"failed to clear hw resource, ret = %d\n", ret);
return ret;
}
return 0;
}
static void hclge_init_rxd_adv_layout(struct hclge_dev *hdev)
{
if (hnae3_ae_dev_rxd_adv_layout_supported(hdev->ae_dev))
hclge_write_dev(&hdev->hw, HCLGE_RXD_ADV_LAYOUT_EN_REG, 1);
}
static void hclge_uninit_rxd_adv_layout(struct hclge_dev *hdev)
{
if (hnae3_ae_dev_rxd_adv_layout_supported(hdev->ae_dev))
hclge_write_dev(&hdev->hw, HCLGE_RXD_ADV_LAYOUT_EN_REG, 0);
}
static struct hclge_wol_info *hclge_get_wol_info(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
return &vport->back->hw.mac.wol;
}
static int hclge_get_wol_supported_mode(struct hclge_dev *hdev,
u32 *wol_supported)
{
struct hclge_query_wol_supported_cmd *wol_supported_cmd;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_WOL_GET_SUPPORTED_MODE,
true);
wol_supported_cmd = (struct hclge_query_wol_supported_cmd *)desc.data;
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to query wol supported, ret = %d\n", ret);
return ret;
}
*wol_supported = le32_to_cpu(wol_supported_cmd->supported_wake_mode);
return 0;
}
static int hclge_set_wol_cfg(struct hclge_dev *hdev,
struct hclge_wol_info *wol_info)
{
struct hclge_wol_cfg_cmd *wol_cfg_cmd;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_WOL_CFG, false);
wol_cfg_cmd = (struct hclge_wol_cfg_cmd *)desc.data;
wol_cfg_cmd->wake_on_lan_mode = cpu_to_le32(wol_info->wol_current_mode);
wol_cfg_cmd->sopass_size = wol_info->wol_sopass_size;
memcpy(wol_cfg_cmd->sopass, wol_info->wol_sopass, SOPASS_MAX);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to set wol config, ret = %d\n", ret);
return ret;
}
static int hclge_update_wol(struct hclge_dev *hdev)
{
struct hclge_wol_info *wol_info = &hdev->hw.mac.wol;
if (!hnae3_ae_dev_wol_supported(hdev->ae_dev))
return 0;
return hclge_set_wol_cfg(hdev, wol_info);
}
static int hclge_init_wol(struct hclge_dev *hdev)
{
struct hclge_wol_info *wol_info = &hdev->hw.mac.wol;
int ret;
if (!hnae3_ae_dev_wol_supported(hdev->ae_dev))
return 0;
memset(wol_info, 0, sizeof(struct hclge_wol_info));
ret = hclge_get_wol_supported_mode(hdev,
&wol_info->wol_support_mode);
if (ret) {
wol_info->wol_support_mode = 0;
return ret;
}
return hclge_update_wol(hdev);
}
static void hclge_get_wol(struct hnae3_handle *handle,
struct ethtool_wolinfo *wol)
{
struct hclge_wol_info *wol_info = hclge_get_wol_info(handle);
wol->supported = wol_info->wol_support_mode;
wol->wolopts = wol_info->wol_current_mode;
if (wol_info->wol_current_mode & WAKE_MAGICSECURE)
memcpy(wol->sopass, wol_info->wol_sopass, SOPASS_MAX);
}
static int hclge_set_wol(struct hnae3_handle *handle,
struct ethtool_wolinfo *wol)
{
struct hclge_wol_info *wol_info = hclge_get_wol_info(handle);
struct hclge_vport *vport = hclge_get_vport(handle);
u32 wol_mode;
int ret;
wol_mode = wol->wolopts;
if (wol_mode & ~wol_info->wol_support_mode)
return -EINVAL;
wol_info->wol_current_mode = wol_mode;
if (wol_mode & WAKE_MAGICSECURE) {
memcpy(wol_info->wol_sopass, wol->sopass, SOPASS_MAX);
wol_info->wol_sopass_size = SOPASS_MAX;
} else {
wol_info->wol_sopass_size = 0;
}
ret = hclge_set_wol_cfg(vport->back, wol_info);
if (ret)
wol_info->wol_current_mode = 0;
return ret;
}
static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev)
{
struct pci_dev *pdev = ae_dev->pdev;
struct hclge_dev *hdev;
int ret;
hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL);
if (!hdev)
return -ENOMEM;
hdev->pdev = pdev;
hdev->ae_dev = ae_dev;
hdev->reset_type = HNAE3_NONE_RESET;
hdev->reset_level = HNAE3_FUNC_RESET;
ae_dev->priv = hdev;
/* HW supprt 2 layer vlan */
hdev->mps = ETH_FRAME_LEN + ETH_FCS_LEN + 2 * VLAN_HLEN;
mutex_init(&hdev->vport_lock);
spin_lock_init(&hdev->fd_rule_lock);
sema_init(&hdev->reset_sem, 1);
ret = hclge_pci_init(hdev);
if (ret)
goto out;
ret = hclge_devlink_init(hdev);
if (ret)
goto err_pci_uninit;
/* Firmware command queue initialize */
ret = hclge_comm_cmd_queue_init(hdev->pdev, &hdev->hw.hw);
if (ret)
goto err_devlink_uninit;
/* Firmware command initialize */
ret = hclge_comm_cmd_init(hdev->ae_dev, &hdev->hw.hw, &hdev->fw_version,
true, hdev->reset_pending);
if (ret)
goto err_cmd_uninit;
ret = hclge_clear_hw_resource(hdev);
if (ret)
goto err_cmd_uninit;
ret = hclge_get_cap(hdev);
if (ret)
goto err_cmd_uninit;
ret = hclge_query_dev_specs(hdev);
if (ret) {
dev_err(&pdev->dev, "failed to query dev specifications, ret = %d.\n",
ret);
goto err_cmd_uninit;
}
ret = hclge_configure(hdev);
if (ret) {
dev_err(&pdev->dev, "Configure dev error, ret = %d.\n", ret);
goto err_cmd_uninit;
}
ret = hclge_init_msi(hdev);
if (ret) {
dev_err(&pdev->dev, "Init MSI/MSI-X error, ret = %d.\n", ret);
goto err_cmd_uninit;
}
ret = hclge_misc_irq_init(hdev);
if (ret)
goto err_msi_uninit;
ret = hclge_alloc_tqps(hdev);
if (ret) {
dev_err(&pdev->dev, "Allocate TQPs error, ret = %d.\n", ret);
goto err_msi_irq_uninit;
}
ret = hclge_alloc_vport(hdev);
if (ret)
goto err_msi_irq_uninit;
ret = hclge_map_tqp(hdev);
if (ret)
goto err_msi_irq_uninit;
if (hdev->hw.mac.media_type == HNAE3_MEDIA_TYPE_COPPER) {
clear_bit(HNAE3_DEV_SUPPORT_FEC_B, ae_dev->caps);
if (hnae3_dev_phy_imp_supported(hdev))
ret = hclge_update_tp_port_info(hdev);
else
ret = hclge_mac_mdio_config(hdev);
if (ret)
goto err_msi_irq_uninit;
}
ret = hclge_init_umv_space(hdev);
if (ret)
goto err_mdiobus_unreg;
ret = hclge_mac_init(hdev);
if (ret) {
dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_config_tso(hdev, HCLGE_TSO_MSS_MIN, HCLGE_TSO_MSS_MAX);
if (ret) {
dev_err(&pdev->dev, "Enable tso fail, ret =%d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_config_gro(hdev);
if (ret)
goto err_mdiobus_unreg;
ret = hclge_init_vlan_config(hdev);
if (ret) {
dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_tm_schd_init(hdev);
if (ret) {
dev_err(&pdev->dev, "tm schd init fail, ret =%d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_comm_rss_init_cfg(&hdev->vport->nic, hdev->ae_dev,
&hdev->rss_cfg);
if (ret) {
dev_err(&pdev->dev, "failed to init rss cfg, ret = %d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_rss_init_hw(hdev);
if (ret) {
dev_err(&pdev->dev, "Rss init fail, ret =%d\n", ret);
goto err_mdiobus_unreg;
}
ret = init_mgr_tbl(hdev);
if (ret) {
dev_err(&pdev->dev, "manager table init fail, ret =%d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_init_fd_config(hdev);
if (ret) {
dev_err(&pdev->dev,
"fd table init fail, ret=%d\n", ret);
goto err_mdiobus_unreg;
}
ret = hclge_ptp_init(hdev);
if (ret)
goto err_mdiobus_unreg;
ret = hclge_update_port_info(hdev);
if (ret)
goto err_mdiobus_unreg;
INIT_KFIFO(hdev->mac_tnl_log);
hclge_dcb_ops_set(hdev);
timer_setup(&hdev->reset_timer, hclge_reset_timer, 0);
INIT_DELAYED_WORK(&hdev->service_task, hclge_service_task);
hclge_clear_all_event_cause(hdev);
hclge_clear_resetting_state(hdev);
/* Log and clear the hw errors those already occurred */
if (hnae3_dev_ras_imp_supported(hdev))
hclge_handle_occurred_error(hdev);
else
hclge_handle_all_hns_hw_errors(ae_dev);
/* request delayed reset for the error recovery because an immediate
* global reset on a PF affecting pending initialization of other PFs
*/
if (ae_dev->hw_err_reset_req) {
enum hnae3_reset_type reset_level;
reset_level = hclge_get_reset_level(ae_dev,
&ae_dev->hw_err_reset_req);
hclge_set_def_reset_request(ae_dev, reset_level);
mod_timer(&hdev->reset_timer, jiffies + HCLGE_RESET_INTERVAL);
}
hclge_init_rxd_adv_layout(hdev);
/* Enable MISC vector(vector0) */
hclge_enable_vector(&hdev->misc_vector, true);
ret = hclge_init_wol(hdev);
if (ret)
dev_warn(&pdev->dev,
"failed to wake on lan init, ret = %d\n", ret);
hclge_state_init(hdev);
hdev->last_reset_time = jiffies;
dev_info(&hdev->pdev->dev, "%s driver initialization finished.\n",
HCLGE_DRIVER_NAME);
hclge_task_schedule(hdev, round_jiffies_relative(HZ));
return 0;
err_mdiobus_unreg:
if (hdev->hw.mac.phydev)
mdiobus_unregister(hdev->hw.mac.mdio_bus);
err_msi_irq_uninit:
hclge_misc_irq_uninit(hdev);
err_msi_uninit:
pci_free_irq_vectors(pdev);
err_cmd_uninit:
hclge_comm_cmd_uninit(hdev->ae_dev, &hdev->hw.hw);
err_devlink_uninit:
hclge_devlink_uninit(hdev);
err_pci_uninit:
pcim_iounmap(pdev, hdev->hw.hw.io_base);
pci_release_regions(pdev);
pci_disable_device(pdev);
out:
mutex_destroy(&hdev->vport_lock);
return ret;
}
static void hclge_stats_clear(struct hclge_dev *hdev)
{
memset(&hdev->mac_stats, 0, sizeof(hdev->mac_stats));
memset(&hdev->fec_stats, 0, sizeof(hdev->fec_stats));
}
static int hclge_set_mac_spoofchk(struct hclge_dev *hdev, int vf, bool enable)
{
return hclge_config_switch_param(hdev, vf, enable,
HCLGE_SWITCH_ANTI_SPOOF_MASK);
}
static int hclge_set_vlan_spoofchk(struct hclge_dev *hdev, int vf, bool enable)
{
return hclge_set_vlan_filter_ctrl(hdev, HCLGE_FILTER_TYPE_VF,
HCLGE_FILTER_FE_NIC_INGRESS_B,
enable, vf);
}
static int hclge_set_vf_spoofchk_hw(struct hclge_dev *hdev, int vf, bool enable)
{
int ret;
ret = hclge_set_mac_spoofchk(hdev, vf, enable);
if (ret) {
dev_err(&hdev->pdev->dev,
"Set vf %d mac spoof check %s failed, ret=%d\n",
vf, enable ? "on" : "off", ret);
return ret;
}
ret = hclge_set_vlan_spoofchk(hdev, vf, enable);
if (ret)
dev_err(&hdev->pdev->dev,
"Set vf %d vlan spoof check %s failed, ret=%d\n",
vf, enable ? "on" : "off", ret);
return ret;
}
static int hclge_set_vf_spoofchk(struct hnae3_handle *handle, int vf,
bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u32 new_spoofchk = enable ? 1 : 0;
int ret;
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
return -EOPNOTSUPP;
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
if (vport->vf_info.spoofchk == new_spoofchk)
return 0;
if (enable && test_bit(vport->vport_id, hdev->vf_vlan_full))
dev_warn(&hdev->pdev->dev,
"vf %d vlan table is full, enable spoof check may cause its packet send fail\n",
vf);
else if (enable && hclge_is_umv_space_full(vport, true))
dev_warn(&hdev->pdev->dev,
"vf %d mac table is full, enable spoof check may cause its packet send fail\n",
vf);
ret = hclge_set_vf_spoofchk_hw(hdev, vport->vport_id, enable);
if (ret)
return ret;
vport->vf_info.spoofchk = new_spoofchk;
return 0;
}
static int hclge_reset_vport_spoofchk(struct hclge_dev *hdev)
{
struct hclge_vport *vport = hdev->vport;
int ret;
int i;
if (hdev->ae_dev->dev_version < HNAE3_DEVICE_VERSION_V2)
return 0;
/* resume the vf spoof check state after reset */
for (i = 0; i < hdev->num_alloc_vport; i++) {
ret = hclge_set_vf_spoofchk_hw(hdev, vport->vport_id,
vport->vf_info.spoofchk);
if (ret)
return ret;
vport++;
}
return 0;
}
static int hclge_set_vf_trust(struct hnae3_handle *handle, int vf, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u32 new_trusted = enable ? 1 : 0;
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
if (vport->vf_info.trusted == new_trusted)
return 0;
vport->vf_info.trusted = new_trusted;
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
hclge_task_schedule(hdev, 0);
return 0;
}
static void hclge_reset_vf_rate(struct hclge_dev *hdev)
{
int ret;
int vf;
/* reset vf rate to default value */
for (vf = HCLGE_VF_VPORT_START_NUM; vf < hdev->num_alloc_vport; vf++) {
struct hclge_vport *vport = &hdev->vport[vf];
vport->vf_info.max_tx_rate = 0;
ret = hclge_tm_qs_shaper_cfg(vport, vport->vf_info.max_tx_rate);
if (ret)
dev_err(&hdev->pdev->dev,
"vf%d failed to reset to default, ret=%d\n",
vf - HCLGE_VF_VPORT_START_NUM, ret);
}
}
static int hclge_vf_rate_param_check(struct hclge_dev *hdev,
int min_tx_rate, int max_tx_rate)
{
if (min_tx_rate != 0 ||
max_tx_rate < 0 || max_tx_rate > hdev->hw.mac.max_speed) {
dev_err(&hdev->pdev->dev,
"min_tx_rate:%d [0], max_tx_rate:%d [0, %u]\n",
min_tx_rate, max_tx_rate, hdev->hw.mac.max_speed);
return -EINVAL;
}
return 0;
}
static int hclge_set_vf_rate(struct hnae3_handle *handle, int vf,
int min_tx_rate, int max_tx_rate, bool force)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
int ret;
ret = hclge_vf_rate_param_check(hdev, min_tx_rate, max_tx_rate);
if (ret)
return ret;
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
if (!force && max_tx_rate == vport->vf_info.max_tx_rate)
return 0;
ret = hclge_tm_qs_shaper_cfg(vport, max_tx_rate);
if (ret)
return ret;
vport->vf_info.max_tx_rate = max_tx_rate;
return 0;
}
static int hclge_resume_vf_rate(struct hclge_dev *hdev)
{
struct hnae3_handle *handle = &hdev->vport->nic;
struct hclge_vport *vport;
int ret;
int vf;
/* resume the vf max_tx_rate after reset */
for (vf = 0; vf < pci_num_vf(hdev->pdev); vf++) {
vport = hclge_get_vf_vport(hdev, vf);
if (!vport)
return -EINVAL;
/* zero means max rate, after reset, firmware already set it to
* max rate, so just continue.
*/
if (!vport->vf_info.max_tx_rate)
continue;
ret = hclge_set_vf_rate(handle, vf, 0,
vport->vf_info.max_tx_rate, true);
if (ret) {
dev_err(&hdev->pdev->dev,
"vf%d failed to resume tx_rate:%u, ret=%d\n",
vf, vport->vf_info.max_tx_rate, ret);
return ret;
}
}
return 0;
}
static void hclge_reset_vport_state(struct hclge_dev *hdev)
{
struct hclge_vport *vport = hdev->vport;
int i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
vport++;
}
}
static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
{
struct hclge_dev *hdev = ae_dev->priv;
struct pci_dev *pdev = ae_dev->pdev;
int ret;
set_bit(HCLGE_STATE_DOWN, &hdev->state);
hclge_stats_clear(hdev);
/* NOTE: pf reset needn't to clear or restore pf and vf table entry.
* so here should not clean table in memory.
*/
if (hdev->reset_type == HNAE3_IMP_RESET ||
hdev->reset_type == HNAE3_GLOBAL_RESET) {
memset(hdev->vlan_table, 0, sizeof(hdev->vlan_table));
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);
}
ret = hclge_comm_cmd_init(hdev->ae_dev, &hdev->hw.hw, &hdev->fw_version,
true, hdev->reset_pending);
if (ret) {
dev_err(&pdev->dev, "Cmd queue init failed\n");
return ret;
}
ret = hclge_map_tqp(hdev);
if (ret) {
dev_err(&pdev->dev, "Map tqp error, ret = %d.\n", ret);
return ret;
}
ret = hclge_mac_init(hdev);
if (ret) {
dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret);
return ret;
}
ret = hclge_tp_port_init(hdev);
if (ret) {
dev_err(&pdev->dev, "failed to init tp port, ret = %d\n",
ret);
return ret;
}
ret = hclge_config_tso(hdev, HCLGE_TSO_MSS_MIN, HCLGE_TSO_MSS_MAX);
if (ret) {
dev_err(&pdev->dev, "Enable tso fail, ret =%d\n", ret);
return ret;
}
ret = hclge_config_gro(hdev);
if (ret)
return ret;
ret = hclge_init_vlan_config(hdev);
if (ret) {
dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret);
return ret;
}
hclge_reset_tc_config(hdev);
ret = hclge_tm_init_hw(hdev, true);
if (ret) {
dev_err(&pdev->dev, "tm init hw fail, ret =%d\n", ret);
return ret;
}
ret = hclge_rss_init_hw(hdev);
if (ret) {
dev_err(&pdev->dev, "Rss init fail, ret =%d\n", ret);
return ret;
}
ret = init_mgr_tbl(hdev);
if (ret) {
dev_err(&pdev->dev,
"failed to reinit manager table, ret = %d\n", ret);
return ret;
}
ret = hclge_init_fd_config(hdev);
if (ret) {
dev_err(&pdev->dev, "fd table init fail, ret=%d\n", ret);
return ret;
}
ret = hclge_ptp_init(hdev);
if (ret)
return ret;
/* Log and clear the hw errors those already occurred */
if (hnae3_dev_ras_imp_supported(hdev))
hclge_handle_occurred_error(hdev);
else
hclge_handle_all_hns_hw_errors(ae_dev);
/* Re-enable the hw error interrupts because
* the interrupts get disabled on global reset.
*/
ret = hclge_config_nic_hw_error(hdev, true);
if (ret) {
dev_err(&pdev->dev,
"fail(%d) to re-enable NIC hw error interrupts\n",
ret);
return ret;
}
if (hdev->roce_client) {
ret = hclge_config_rocee_ras_interrupt(hdev, true);
if (ret) {
dev_err(&pdev->dev,
"fail(%d) to re-enable roce ras interrupts\n",
ret);
return ret;
}
}
hclge_reset_vport_state(hdev);
ret = hclge_reset_vport_spoofchk(hdev);
if (ret)
return ret;
ret = hclge_resume_vf_rate(hdev);
if (ret)
return ret;
hclge_init_rxd_adv_layout(hdev);
ret = hclge_update_wol(hdev);
if (ret)
dev_warn(&pdev->dev,
"failed to update wol config, ret = %d\n", ret);
dev_info(&pdev->dev, "Reset done, %s driver initialization finished.\n",
HCLGE_DRIVER_NAME);
return 0;
}
static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
{
struct hclge_dev *hdev = ae_dev->priv;
struct hclge_mac *mac = &hdev->hw.mac;
hclge_reset_vf_rate(hdev);
hclge_clear_vf_vlan(hdev);
hclge_state_uninit(hdev);
hclge_ptp_uninit(hdev);
hclge_uninit_rxd_adv_layout(hdev);
hclge_uninit_mac_table(hdev);
hclge_del_all_fd_entries(hdev);
if (mac->phydev)
mdiobus_unregister(mac->mdio_bus);
/* Disable MISC vector(vector0) */
hclge_enable_vector(&hdev->misc_vector, false);
synchronize_irq(hdev->misc_vector.vector_irq);
/* Disable all hw interrupts */
hclge_config_mac_tnl_int(hdev, false);
hclge_config_nic_hw_error(hdev, false);
hclge_config_rocee_ras_interrupt(hdev, false);
hclge_comm_cmd_uninit(hdev->ae_dev, &hdev->hw.hw);
hclge_misc_irq_uninit(hdev);
hclge_devlink_uninit(hdev);
hclge_pci_uninit(hdev);
hclge_uninit_vport_vlan_table(hdev);
mutex_destroy(&hdev->vport_lock);
ae_dev->priv = NULL;
}
static u32 hclge_get_max_channels(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
return min_t(u32, hdev->pf_rss_size_max, vport->alloc_tqps);
}
static void hclge_get_channels(struct hnae3_handle *handle,
struct ethtool_channels *ch)
{
ch->max_combined = hclge_get_max_channels(handle);
ch->other_count = 1;
ch->max_other = 1;
ch->combined_count = handle->kinfo.rss_size;
}
static void hclge_get_tqps_and_rss_info(struct hnae3_handle *handle,
u16 *alloc_tqps, u16 *max_rss_size)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
*alloc_tqps = vport->alloc_tqps;
*max_rss_size = hdev->pf_rss_size_max;
}
static int hclge_set_rss_tc_mode_cfg(struct hnae3_handle *handle)
{
struct hclge_vport *vport = hclge_get_vport(handle);
u16 tc_offset[HCLGE_MAX_TC_NUM] = {0};
struct hclge_dev *hdev = vport->back;
u16 tc_size[HCLGE_MAX_TC_NUM] = {0};
u16 tc_valid[HCLGE_MAX_TC_NUM];
u16 roundup_size;
unsigned int i;
roundup_size = roundup_pow_of_two(vport->nic.kinfo.rss_size);
roundup_size = ilog2(roundup_size);
/* Set the RSS TC mode according to the new RSS size */
for (i = 0; i < HCLGE_MAX_TC_NUM; i++) {
tc_valid[i] = 0;
if (!(hdev->hw_tc_map & BIT(i)))
continue;
tc_valid[i] = 1;
tc_size[i] = roundup_size;
tc_offset[i] = vport->nic.kinfo.rss_size * i;
}
return hclge_comm_set_rss_tc_mode(&hdev->hw.hw, tc_offset, tc_valid,
tc_size);
}
static int hclge_set_channels(struct hnae3_handle *handle, u32 new_tqps_num,
bool rxfh_configured)
{
struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
struct hclge_vport *vport = hclge_get_vport(handle);
struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo;
struct hclge_dev *hdev = vport->back;
u16 cur_rss_size = kinfo->rss_size;
u16 cur_tqps = kinfo->num_tqps;
u32 *rss_indir;
unsigned int i;
int ret;
kinfo->req_rss_size = new_tqps_num;
ret = hclge_tm_vport_map_update(hdev);
if (ret) {
dev_err(&hdev->pdev->dev, "tm vport map fail, ret =%d\n", ret);
return ret;
}
ret = hclge_set_rss_tc_mode_cfg(handle);
if (ret)
return ret;
/* RSS indirection table has been configured by user */
if (rxfh_configured)
goto out;
/* Reinitializes the rss indirect table according to the new RSS size */
rss_indir = kcalloc(ae_dev->dev_specs.rss_ind_tbl_size, sizeof(u32),
GFP_KERNEL);
if (!rss_indir)
return -ENOMEM;
for (i = 0; i < ae_dev->dev_specs.rss_ind_tbl_size; i++)
rss_indir[i] = i % kinfo->rss_size;
ret = hclge_set_rss(handle, rss_indir, NULL, 0);
if (ret)
dev_err(&hdev->pdev->dev, "set rss indir table fail, ret=%d\n",
ret);
kfree(rss_indir);
out:
if (!ret)
dev_info(&hdev->pdev->dev,
"Channels changed, rss_size from %u to %u, tqps from %u to %u",
cur_rss_size, kinfo->rss_size,
cur_tqps, kinfo->rss_size * kinfo->tc_info.num_tc);
return ret;
}
static int hclge_set_led_status(struct hclge_dev *hdev, u8 locate_led_status)
{
struct hclge_set_led_state_cmd *req;
struct hclge_desc desc;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_LED_STATUS_CFG, false);
req = (struct hclge_set_led_state_cmd *)desc.data;
hnae3_set_field(req->locate_led_config, HCLGE_LED_LOCATE_STATE_M,
HCLGE_LED_LOCATE_STATE_S, locate_led_status);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret)
dev_err(&hdev->pdev->dev,
"Send set led state cmd error, ret =%d\n", ret);
return ret;
}
enum hclge_led_status {
HCLGE_LED_OFF,
HCLGE_LED_ON,
HCLGE_LED_NO_CHANGE = 0xFF,
};
static int hclge_set_led_id(struct hnae3_handle *handle,
enum ethtool_phys_id_state status)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
switch (status) {
case ETHTOOL_ID_ACTIVE:
return hclge_set_led_status(hdev, HCLGE_LED_ON);
case ETHTOOL_ID_INACTIVE:
return hclge_set_led_status(hdev, HCLGE_LED_OFF);
default:
return -EINVAL;
}
}
static void hclge_get_link_mode(struct hnae3_handle *handle,
unsigned long *supported,
unsigned long *advertising)
{
unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS);
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
unsigned int idx = 0;
for (; idx < size; idx++) {
supported[idx] = hdev->hw.mac.supported[idx];
advertising[idx] = hdev->hw.mac.advertising[idx];
}
}
static int hclge_gro_en(struct hnae3_handle *handle, bool enable)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
bool gro_en_old = hdev->gro_en;
int ret;
hdev->gro_en = enable;
ret = hclge_config_gro(hdev);
if (ret)
hdev->gro_en = gro_en_old;
return ret;
}
static int hclge_sync_vport_promisc_mode(struct hclge_vport *vport)
{
struct hnae3_handle *handle = &vport->nic;
struct hclge_dev *hdev = vport->back;
bool uc_en = false;
bool mc_en = false;
u8 tmp_flags;
bool bc_en;
int ret;
if (vport->last_promisc_flags != vport->overflow_promisc_flags) {
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
vport->last_promisc_flags = vport->overflow_promisc_flags;
}
if (!test_and_clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
&vport->state))
return 0;
/* for PF */
if (!vport->vport_id) {
tmp_flags = handle->netdev_flags | vport->last_promisc_flags;
ret = hclge_set_promisc_mode(handle, tmp_flags & HNAE3_UPE,
tmp_flags & HNAE3_MPE);
if (!ret)
set_bit(HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE,
&vport->state);
else
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
&vport->state);
return ret;
}
/* for VF */
if (vport->vf_info.trusted) {
uc_en = vport->vf_info.request_uc_en > 0 ||
vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE;
mc_en = vport->vf_info.request_mc_en > 0 ||
vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE;
}
bc_en = vport->vf_info.request_bc_en > 0;
ret = hclge_cmd_set_promisc_mode(hdev, vport->vport_id, uc_en,
mc_en, bc_en);
if (ret) {
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
return ret;
}
hclge_set_vport_vlan_fltr_change(vport);
return 0;
}
static void hclge_sync_promisc_mode(struct hclge_dev *hdev)
{
struct hclge_vport *vport;
int ret;
u16 i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
vport = &hdev->vport[i];
ret = hclge_sync_vport_promisc_mode(vport);
if (ret)
return;
}
}
static bool hclge_module_existed(struct hclge_dev *hdev)
{
struct hclge_desc desc;
u32 existed;
int ret;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GET_SFP_EXIST, true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to get SFP exist state, ret = %d\n", ret);
return false;
}
existed = le32_to_cpu(desc.data[0]);
return existed != 0;
}
/* need 6 bds(total 140 bytes) in one reading
* return the number of bytes actually read, 0 means read failed.
*/
static u16 hclge_get_sfp_eeprom_info(struct hclge_dev *hdev, u32 offset,
u32 len, u8 *data)
{
struct hclge_desc desc[HCLGE_SFP_INFO_CMD_NUM];
struct hclge_sfp_info_bd0_cmd *sfp_info_bd0;
u16 read_len;
u16 copy_len;
int ret;
int i;
/* setup all 6 bds to read module eeprom info. */
for (i = 0; i < HCLGE_SFP_INFO_CMD_NUM; i++) {
hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_GET_SFP_EEPROM,
true);
/* bd0~bd4 need next flag */
if (i < HCLGE_SFP_INFO_CMD_NUM - 1)
desc[i].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT);
}
/* setup bd0, this bd contains offset and read length. */
sfp_info_bd0 = (struct hclge_sfp_info_bd0_cmd *)desc[0].data;
sfp_info_bd0->offset = cpu_to_le16((u16)offset);
read_len = min_t(u16, len, HCLGE_SFP_INFO_MAX_LEN);
sfp_info_bd0->read_len = cpu_to_le16(read_len);
ret = hclge_cmd_send(&hdev->hw, desc, i);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to get SFP eeprom info, ret = %d\n", ret);
return 0;
}
/* copy sfp info from bd0 to out buffer. */
copy_len = min_t(u16, len, HCLGE_SFP_INFO_BD0_LEN);
memcpy(data, sfp_info_bd0->data, copy_len);
read_len = copy_len;
/* copy sfp info from bd1~bd5 to out buffer if needed. */
for (i = 1; i < HCLGE_SFP_INFO_CMD_NUM; i++) {
if (read_len >= len)
return read_len;
copy_len = min_t(u16, len - read_len, HCLGE_SFP_INFO_BDX_LEN);
memcpy(data + read_len, desc[i].data, copy_len);
read_len += copy_len;
}
return read_len;
}
static int hclge_get_module_eeprom(struct hnae3_handle *handle, u32 offset,
u32 len, u8 *data)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
u32 read_len = 0;
u16 data_len;
if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_FIBER)
return -EOPNOTSUPP;
if (!hclge_module_existed(hdev))
return -ENXIO;
while (read_len < len) {
data_len = hclge_get_sfp_eeprom_info(hdev,
offset + read_len,
len - read_len,
data + read_len);
if (!data_len)
return -EIO;
read_len += data_len;
}
return 0;
}
static int hclge_get_link_diagnosis_info(struct hnae3_handle *handle,
u32 *status_code)
{
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
struct hclge_desc desc;
int ret;
if (hdev->ae_dev->dev_version <= HNAE3_DEVICE_VERSION_V2)
return -EOPNOTSUPP;
hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_LINK_DIAGNOSIS, true);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
if (ret) {
dev_err(&hdev->pdev->dev,
"failed to query link diagnosis info, ret = %d\n", ret);
return ret;
}
*status_code = le32_to_cpu(desc.data[0]);
return 0;
}
/* After disable sriov, VF still has some config and info need clean,
* which configed by PF.
*/
static void hclge_clear_vport_vf_info(struct hclge_vport *vport, int vfid)
{
struct hclge_dev *hdev = vport->back;
struct hclge_vlan_info vlan_info;
int ret;
clear_bit(HCLGE_VPORT_STATE_INITED, &vport->state);
clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
vport->need_notify = 0;
vport->mps = 0;
/* after disable sriov, clean VF rate configured by PF */
ret = hclge_tm_qs_shaper_cfg(vport, 0);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to clean vf%d rate config, ret = %d\n",
vfid, ret);
vlan_info.vlan_tag = 0;
vlan_info.qos = 0;
vlan_info.vlan_proto = ETH_P_8021Q;
ret = hclge_update_port_base_vlan_cfg(vport,
HNAE3_PORT_BASE_VLAN_DISABLE,
&vlan_info);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to clean vf%d port base vlan, ret = %d\n",
vfid, ret);
ret = hclge_set_vf_spoofchk_hw(hdev, vport->vport_id, false);
if (ret)
dev_err(&hdev->pdev->dev,
"failed to clean vf%d spoof config, ret = %d\n",
vfid, ret);
memset(&vport->vf_info, 0, sizeof(vport->vf_info));
}
static void hclge_clean_vport_config(struct hnae3_ae_dev *ae_dev, int num_vfs)
{
struct hclge_dev *hdev = ae_dev->priv;
struct hclge_vport *vport;
int i;
for (i = 0; i < num_vfs; i++) {
vport = &hdev->vport[i + HCLGE_VF_VPORT_START_NUM];
hclge_clear_vport_vf_info(vport, i);
}
}
static int hclge_get_dscp_prio(struct hnae3_handle *h, u8 dscp, u8 *tc_mode,
u8 *priority)
{
struct hclge_vport *vport = hclge_get_vport(h);
if (dscp >= HNAE3_MAX_DSCP)
return -EINVAL;
if (tc_mode)
*tc_mode = vport->nic.kinfo.tc_map_mode;
if (priority)
*priority = vport->nic.kinfo.dscp_prio[dscp] == HNAE3_PRIO_ID_INVALID ? 0 :
vport->nic.kinfo.dscp_prio[dscp];
return 0;
}
static const struct hnae3_ae_ops hclge_ops = {
.init_ae_dev = hclge_init_ae_dev,
.uninit_ae_dev = hclge_uninit_ae_dev,
.reset_prepare = hclge_reset_prepare_general,
.reset_done = hclge_reset_done,
.init_client_instance = hclge_init_client_instance,
.uninit_client_instance = hclge_uninit_client_instance,
.map_ring_to_vector = hclge_map_ring_to_vector,
.unmap_ring_from_vector = hclge_unmap_ring_frm_vector,
.get_vector = hclge_get_vector,
.put_vector = hclge_put_vector,
.set_promisc_mode = hclge_set_promisc_mode,
.request_update_promisc_mode = hclge_request_update_promisc_mode,
.set_loopback = hclge_set_loopback,
.start = hclge_ae_start,
.stop = hclge_ae_stop,
.client_start = hclge_client_start,
.client_stop = hclge_client_stop,
.get_status = hclge_get_status,
.get_ksettings_an_result = hclge_get_ksettings_an_result,
.cfg_mac_speed_dup_h = hclge_cfg_mac_speed_dup_h,
.get_media_type = hclge_get_media_type,
.check_port_speed = hclge_check_port_speed,
.get_fec_stats = hclge_get_fec_stats,
.get_fec = hclge_get_fec,
.set_fec = hclge_set_fec,
.get_rss_key_size = hclge_comm_get_rss_key_size,
.get_rss = hclge_get_rss,
.set_rss = hclge_set_rss,
.set_rss_tuple = hclge_set_rss_tuple,
.get_rss_tuple = hclge_get_rss_tuple,
.get_tc_size = hclge_get_tc_size,
.get_mac_addr = hclge_get_mac_addr,
.set_mac_addr = hclge_set_mac_addr,
.do_ioctl = hclge_do_ioctl,
.add_uc_addr = hclge_add_uc_addr,
.rm_uc_addr = hclge_rm_uc_addr,
.add_mc_addr = hclge_add_mc_addr,
.rm_mc_addr = hclge_rm_mc_addr,
.set_autoneg = hclge_set_autoneg,
.get_autoneg = hclge_get_autoneg,
.restart_autoneg = hclge_restart_autoneg,
.halt_autoneg = hclge_halt_autoneg,
.get_pauseparam = hclge_get_pauseparam,
.set_pauseparam = hclge_set_pauseparam,
.set_mtu = hclge_set_mtu,
.reset_queue = hclge_reset_tqp,
.get_stats = hclge_get_stats,
.get_mac_stats = hclge_get_mac_stat,
.update_stats = hclge_update_stats,
.get_strings = hclge_get_strings,
.get_sset_count = hclge_get_sset_count,
.get_fw_version = hclge_get_fw_version,
.get_mdix_mode = hclge_get_mdix_mode,
.enable_vlan_filter = hclge_enable_vlan_filter,
.set_vlan_filter = hclge_set_vlan_filter,
.set_vf_vlan_filter = hclge_set_vf_vlan_filter,
.enable_hw_strip_rxvtag = hclge_en_hw_strip_rxvtag,
.reset_event = hclge_reset_event,
.get_reset_level = hclge_get_reset_level,
.set_default_reset_request = hclge_set_def_reset_request,
.get_tqps_and_rss_info = hclge_get_tqps_and_rss_info,
.set_channels = hclge_set_channels,
.get_channels = hclge_get_channels,
.get_regs_len = hclge_get_regs_len,
.get_regs = hclge_get_regs,
.set_led_id = hclge_set_led_id,
.get_link_mode = hclge_get_link_mode,
.add_fd_entry = hclge_add_fd_entry,
.del_fd_entry = hclge_del_fd_entry,
.get_fd_rule_cnt = hclge_get_fd_rule_cnt,
.get_fd_rule_info = hclge_get_fd_rule_info,
.get_fd_all_rules = hclge_get_all_rules,
.enable_fd = hclge_enable_fd,
.add_arfs_entry = hclge_add_fd_entry_by_arfs,
.dbg_read_cmd = hclge_dbg_read_cmd,
.handle_hw_ras_error = hclge_handle_hw_ras_error,
.get_hw_reset_stat = hclge_get_hw_reset_stat,
.ae_dev_resetting = hclge_ae_dev_resetting,
.ae_dev_reset_cnt = hclge_ae_dev_reset_cnt,
.set_gro_en = hclge_gro_en,
.get_global_queue_id = hclge_covert_handle_qid_global,
.set_timer_task = hclge_set_timer_task,
net: hns3: Fix NULL deref when unloading driver When the driver is unloading, if there is a calling of ndo_open occurs between phy_disconnect() and unregister_netdev(), it will end up causing the kernel to eventually hit a NULL deref: [14942.417828] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000048 [14942.529878] Mem abort info: [14942.551166] ESR = 0x96000006 [14942.567070] Exception class = DABT (current EL), IL = 32 bits [14942.623081] SET = 0, FnV = 0 [14942.639112] EA = 0, S1PTW = 0 [14942.643628] Data abort info: [14942.659227] ISV = 0, ISS = 0x00000006 [14942.674870] CM = 0, WnR = 0 [14942.679449] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000224ad6ad [14942.695595] [0000000000000048] pgd=00000021e6673003, pud=00000021dbf01003, pmd=0000000000000000 [14942.723163] Internal error: Oops: 96000006 [#1] PREEMPT SMP [14942.729358] Modules linked in: hns3(O) hclge(O) pv680_mii(O) hnae3(O) [last unloaded: hclge] [14942.738907] CPU: 1 PID: 26629 Comm: kworker/u4:13 Tainted: G O 4.18.0-rc1-12928-ga960791-dirty #145 [14942.749491] Hardware name: Huawei Technologies Co., Ltd. D05/D05, BIOS Hi1620 FPGA TB BOOT BIOS B763 08/17/2018 [14942.760392] Workqueue: events_power_efficient phy_state_machine [14942.766644] pstate: 80c00009 (Nzcv daif +PAN +UAO) [14942.771918] pc : test_and_set_bit+0x18/0x38 [14942.776589] lr : netif_carrier_off+0x24/0x70 [14942.781033] sp : ffff0000121abd20 [14942.784518] x29: ffff0000121abd20 x28: 0000000000000000 [14942.790208] x27: ffff0000164d3cd8 x26: ffff8021da68b7b8 [14942.795832] x25: 0000000000000000 x24: ffff8021eb407800 [14942.801445] x23: 0000000000000000 x22: 0000000000000000 [14942.807046] x21: 0000000000000001 x20: 0000000000000000 [14942.812672] x19: 0000000000000000 x18: ffff000009781708 [14942.818284] x17: 00000000004970e8 x16: ffff00000816ad48 [14942.823900] x15: 0000000000000000 x14: 0000000000000008 [14942.829528] x13: 0000000000000000 x12: 0000000000000f65 [14942.835149] x11: 0000000000000001 x10: 00000000000009d0 [14942.840753] x9 : ffff0000121abaa0 x8 : 0000000000000000 [14942.846360] x7 : ffff000009781708 x6 : 0000000000000003 [14942.851970] x5 : 0000000000000020 x4 : 0000000000000004 [14942.857575] x3 : 0000000000000002 x2 : 0000000000000001 [14942.863180] x1 : 0000000000000048 x0 : 0000000000000000 [14942.868875] Process kworker/u4:13 (pid: 26629, stack limit = 0x00000000c909dbf3) [14942.876464] Call trace: [14942.879200] test_and_set_bit+0x18/0x38 [14942.883376] phy_link_change+0x38/0x78 [14942.887378] phy_state_machine+0x3dc/0x4f8 [14942.891968] process_one_work+0x158/0x470 [14942.896223] worker_thread+0x50/0x470 [14942.900219] kthread+0x104/0x130 [14942.903905] ret_from_fork+0x10/0x1c [14942.907755] Code: d2800022 8b400c21 f9800031 9ac32044 (c85f7c22) [14942.914185] ---[ end trace 968c9e12eb740b23 ]--- So this patch fixes it by modifying the timing to do phy_connect_direct() and phy_disconnect(). Fixes: 256727da7395 ("net: hns3: Add MDIO support to HNS3 Ethernet driver for hip08 SoC") Signed-off-by: Huazhong Tan <tanhuazhong@huawei.com> Signed-off-by: Peng Li <lipeng321@huawei.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2019-01-30 20:55:46 +00:00
.mac_connect_phy = hclge_mac_connect_phy,
.mac_disconnect_phy = hclge_mac_disconnect_phy,
.get_vf_config = hclge_get_vf_config,
.set_vf_link_state = hclge_set_vf_link_state,
.set_vf_spoofchk = hclge_set_vf_spoofchk,
.set_vf_trust = hclge_set_vf_trust,
.set_vf_rate = hclge_set_vf_rate,
.set_vf_mac = hclge_set_vf_mac,
.get_module_eeprom = hclge_get_module_eeprom,
.get_cmdq_stat = hclge_get_cmdq_stat,
.add_cls_flower = hclge_add_cls_flower,
.del_cls_flower = hclge_del_cls_flower,
.cls_flower_active = hclge_is_cls_flower_active,
.get_phy_link_ksettings = hclge_get_phy_link_ksettings,
.set_phy_link_ksettings = hclge_set_phy_link_ksettings,
.set_tx_hwts_info = hclge_ptp_set_tx_info,
.get_rx_hwts = hclge_ptp_get_rx_hwts,
.get_ts_info = hclge_ptp_get_ts_info,
.get_link_diagnosis_info = hclge_get_link_diagnosis_info,
.clean_vf_config = hclge_clean_vport_config,
.get_dscp_prio = hclge_get_dscp_prio,
.get_wol = hclge_get_wol,
.set_wol = hclge_set_wol,
};
static struct hnae3_ae_algo ae_algo = {
.ops = &hclge_ops,
.pdev_id_table = ae_algo_pci_tbl,
};
static int __init hclge_init(void)
{
pr_info("%s is initializing\n", HCLGE_NAME);
hclge_wq = alloc_workqueue("%s", WQ_UNBOUND, 0, HCLGE_NAME);
if (!hclge_wq) {
pr_err("%s: failed to create workqueue\n", HCLGE_NAME);
return -ENOMEM;
}
hnae3_register_ae_algo(&ae_algo);
return 0;
}
static void __exit hclge_exit(void)
{
hnae3_unregister_ae_algo_prepare(&ae_algo);
hnae3_unregister_ae_algo(&ae_algo);
destroy_workqueue(hclge_wq);
}
module_init(hclge_init);
module_exit(hclge_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
MODULE_DESCRIPTION("HCLGE Driver");
MODULE_VERSION(HCLGE_MOD_VERSION);