diff options
Diffstat (limited to 'drivers/net/ethernet/huawei/hinic')
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_dev.h | 28 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_ethtool.c | 762 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 12 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 56 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 60 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h | 53 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_main.c | 339 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_port.c | 638 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_port.h | 371 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_rx.c | 82 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_rx.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_tx.c | 25 | ||||
-rw-r--r-- | drivers/net/ethernet/huawei/hinic/hinic_tx.h | 1 |
15 files changed, 2275 insertions, 166 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 99de5b6607d5..fe88ab88cacc 100644 --- a/drivers/net/ethernet/huawei/hinic/Makefile +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_HINIC) += hinic.o hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ hinic_hw_io.o hinic_hw_qp.o hinic_hw_cmdq.o hinic_hw_wq.o \ hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o \ - hinic_common.o + hinic_common.o hinic_ethtool.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 353276fdcaed..a209b14160cc 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -22,6 +22,7 @@ enum hinic_flags { HINIC_LINK_UP = BIT(0), HINIC_INTF_UP = BIT(1), + HINIC_RSS_ENABLE = BIT(2), }; struct hinic_rx_mode_work { @@ -29,6 +30,23 @@ struct hinic_rx_mode_work { u32 rx_mode; }; +struct hinic_rss_type { + u8 tcp_ipv6_ext; + u8 ipv6_ext; + u8 tcp_ipv6; + u8 ipv6; + u8 tcp_ipv4; + u8 ipv4; + u8 udp_ipv6; + u8 udp_ipv4; +}; + +enum hinic_rss_hash_type { + HINIC_RSS_HASH_ENGINE_TYPE_XOR, + HINIC_RSS_HASH_ENGINE_TYPE_TOEP, + HINIC_RSS_HASH_ENGINE_TYPE_MAX, +}; + struct hinic_dev { struct net_device *netdev; struct hinic_hwdev *hwdev; @@ -36,6 +54,8 @@ struct hinic_dev { u32 msg_enable; unsigned int tx_weight; unsigned int rx_weight; + u16 num_qps; + u16 max_qps; unsigned int flags; @@ -50,6 +70,14 @@ struct hinic_dev { struct hinic_txq_stats tx_stats; struct hinic_rxq_stats rx_stats; + + u8 rss_tmpl_idx; + u8 rss_hash_engine; + u16 num_rss; + u16 rss_limit; + struct hinic_rss_type rss_type; + u8 *rss_hkey_user; + s32 *rss_indir_user; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c new file mode 100644 index 000000000000..60ec48fe4144 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -0,0 +1,762 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/if_vlan.h> +#include <linux/ethtool.h> +#include <linux/vmalloc.h> + +#include "hinic_hw_qp.h" +#include "hinic_hw_dev.h" +#include "hinic_port.h" +#include "hinic_tx.h" +#include "hinic_rx.h" +#include "hinic_dev.h" + +static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, + enum hinic_speed speed) +{ + switch (speed) { + case HINIC_SPEED_10MB_LINK: + link_ksettings->base.speed = SPEED_10; + break; + + case HINIC_SPEED_100MB_LINK: + link_ksettings->base.speed = SPEED_100; + break; + + case HINIC_SPEED_1000MB_LINK: + link_ksettings->base.speed = SPEED_1000; + break; + + case HINIC_SPEED_10GB_LINK: + link_ksettings->base.speed = SPEED_10000; + break; + + case HINIC_SPEED_25GB_LINK: + link_ksettings->base.speed = SPEED_25000; + break; + + case HINIC_SPEED_40GB_LINK: + link_ksettings->base.speed = SPEED_40000; + break; + + case HINIC_SPEED_100GB_LINK: + link_ksettings->base.speed = SPEED_100000; + break; + + default: + link_ksettings->base.speed = SPEED_UNKNOWN; + break; + } +} + +static int hinic_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings + *link_ksettings) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + struct hinic_port_cap port_cap; + int err; + + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + ethtool_link_ksettings_add_link_mode(link_ksettings, supported, + Autoneg); + + link_ksettings->base.speed = SPEED_UNKNOWN; + link_ksettings->base.autoneg = AUTONEG_DISABLE; + link_ksettings->base.duplex = DUPLEX_UNKNOWN; + + err = hinic_port_get_cap(nic_dev, &port_cap); + if (err) + return err; + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) + return err; + + if (link_state != HINIC_LINK_STATE_UP) + return err; + + set_link_speed(link_ksettings, port_cap.speed); + + if (!!(port_cap.autoneg_cap & HINIC_AUTONEG_SUPPORTED)) + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, Autoneg); + + if (port_cap.autoneg_state == HINIC_AUTONEG_ACTIVE) + link_ksettings->base.autoneg = AUTONEG_ENABLE; + + link_ksettings->base.duplex = (port_cap.duplex == HINIC_DUPLEX_FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + return 0; +} + +static void hinic_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u8 mgmt_ver[HINIC_MGMT_VERSION_MAX_LEN] = {0}; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + int err; + + strlcpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); + strlcpy(info->bus_info, pci_name(hwif->pdev), sizeof(info->bus_info)); + + err = hinic_get_mgmt_version(nic_dev, mgmt_ver); + if (err) + return; + + snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver); +} + +static void hinic_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + ring->rx_max_pending = HINIC_RQ_DEPTH; + ring->tx_max_pending = HINIC_SQ_DEPTH; + ring->rx_pending = HINIC_RQ_DEPTH; + ring->tx_pending = HINIC_SQ_DEPTH; +} + +static void hinic_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + + channels->max_rx = hwdev->nic_cap.max_qps; + channels->max_tx = hwdev->nic_cap.max_qps; + channels->max_other = 0; + channels->max_combined = 0; + channels->rx_count = hinic_hwdev_num_qps(hwdev); + channels->tx_count = hinic_hwdev_num_qps(hwdev); + channels->other_count = 0; + channels->combined_count = 0; +} + +static int hinic_get_rss_hash_opts(struct hinic_dev *nic_dev, + struct ethtool_rxnfc *cmd) +{ + struct hinic_rss_type rss_type = { 0 }; + int err; + + cmd->data = 0; + + if (!(nic_dev->flags & HINIC_RSS_ENABLE)) + return 0; + + err = hinic_get_rss_type(nic_dev, nic_dev->rss_tmpl_idx, + &rss_type); + if (err) + return err; + + cmd->data = RXH_IP_SRC | RXH_IP_DST; + switch (cmd->flow_type) { + case TCP_V4_FLOW: + if (rss_type.tcp_ipv4) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case TCP_V6_FLOW: + if (rss_type.tcp_ipv6) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case UDP_V4_FLOW: + if (rss_type.udp_ipv4) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case UDP_V6_FLOW: + if (rss_type.udp_ipv6) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case IPV4_FLOW: + case IPV6_FLOW: + break; + default: + cmd->data = 0; + return -EINVAL; + } + + return 0; +} + +static int set_l4_rss_hash_ops(struct ethtool_rxnfc *cmd, + struct hinic_rss_type *rss_type) +{ + u8 rss_l4_en = 0; + + switch (cmd->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + case 0: + rss_l4_en = 0; + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rss_l4_en = 1; + break; + default: + return -EINVAL; + } + + switch (cmd->flow_type) { + case TCP_V4_FLOW: + rss_type->tcp_ipv4 = rss_l4_en; + break; + case TCP_V6_FLOW: + rss_type->tcp_ipv6 = rss_l4_en; + break; + case UDP_V4_FLOW: + rss_type->udp_ipv4 = rss_l4_en; + break; + case UDP_V6_FLOW: + rss_type->udp_ipv6 = rss_l4_en; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int hinic_set_rss_hash_opts(struct hinic_dev *nic_dev, + struct ethtool_rxnfc *cmd) +{ + struct hinic_rss_type *rss_type = &nic_dev->rss_type; + int err; + + if (!(nic_dev->flags & HINIC_RSS_ENABLE)) { + cmd->data = 0; + return -EOPNOTSUPP; + } + + /* RSS does not support anything other than hashing + * to queues on src and dst IPs and ports + */ + if (cmd->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | + RXH_L4_B_2_3)) + return -EINVAL; + + /* We need at least the IP SRC and DEST fields for hashing */ + if (!(cmd->data & RXH_IP_SRC) || !(cmd->data & RXH_IP_DST)) + return -EINVAL; + + err = hinic_get_rss_type(nic_dev, + nic_dev->rss_tmpl_idx, rss_type); + if (err) + return -EFAULT; + + switch (cmd->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V4_FLOW: + case UDP_V6_FLOW: + err = set_l4_rss_hash_ops(cmd, rss_type); + if (err) + return err; + break; + case IPV4_FLOW: + rss_type->ipv4 = 1; + break; + case IPV6_FLOW: + rss_type->ipv6 = 1; + break; + default: + return -EINVAL; + } + + err = hinic_set_rss_type(nic_dev, nic_dev->rss_tmpl_idx, + *rss_type); + if (err) + return -EFAULT; + + return 0; +} + +static int __set_rss_rxfh(struct net_device *netdev, + const u32 *indir, const u8 *key) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err; + + if (indir) { + if (!nic_dev->rss_indir_user) { + nic_dev->rss_indir_user = + kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, + GFP_KERNEL); + if (!nic_dev->rss_indir_user) + return -ENOMEM; + } + + memcpy(nic_dev->rss_indir_user, indir, + sizeof(u32) * HINIC_RSS_INDIR_SIZE); + + err = hinic_rss_set_indir_tbl(nic_dev, + nic_dev->rss_tmpl_idx, indir); + if (err) + return -EFAULT; + } + + if (key) { + if (!nic_dev->rss_hkey_user) { + nic_dev->rss_hkey_user = + kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL); + + if (!nic_dev->rss_hkey_user) + return -ENOMEM; + } + + memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE); + + err = hinic_rss_set_template_tbl(nic_dev, + nic_dev->rss_tmpl_idx, key); + if (err) + return -EFAULT; + } + + return 0; +} + +static int hinic_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *cmd, u32 *rule_locs) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = nic_dev->num_qps; + break; + case ETHTOOL_GRXFH: + err = hinic_get_rss_hash_opts(nic_dev, cmd); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int hinic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err = 0; + + switch (cmd->cmd) { + case ETHTOOL_SRXFH: + err = hinic_set_rss_hash_opts(nic_dev, cmd); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int hinic_get_rxfh(struct net_device *netdev, + u32 *indir, u8 *key, u8 *hfunc) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u8 hash_engine_type = 0; + int err = 0; + + if (!(nic_dev->flags & HINIC_RSS_ENABLE)) + return -EOPNOTSUPP; + + if (hfunc) { + err = hinic_rss_get_hash_engine(nic_dev, + nic_dev->rss_tmpl_idx, + &hash_engine_type); + if (err) + return -EFAULT; + + *hfunc = hash_engine_type ? ETH_RSS_HASH_TOP : ETH_RSS_HASH_XOR; + } + + if (indir) { + err = hinic_rss_get_indir_tbl(nic_dev, + nic_dev->rss_tmpl_idx, indir); + if (err) + return -EFAULT; + } + + if (key) + err = hinic_rss_get_template_tbl(nic_dev, + nic_dev->rss_tmpl_idx, key); + + return err; +} + +static int hinic_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err = 0; + + if (!(nic_dev->flags & HINIC_RSS_ENABLE)) + return -EOPNOTSUPP; + + if (hfunc != ETH_RSS_HASH_NO_CHANGE) { + if (hfunc != ETH_RSS_HASH_TOP && hfunc != ETH_RSS_HASH_XOR) + return -EOPNOTSUPP; + + nic_dev->rss_hash_engine = (hfunc == ETH_RSS_HASH_XOR) ? + HINIC_RSS_HASH_ENGINE_TYPE_XOR : + HINIC_RSS_HASH_ENGINE_TYPE_TOEP; + err = hinic_rss_set_hash_engine + (nic_dev, nic_dev->rss_tmpl_idx, + nic_dev->rss_hash_engine); + if (err) + return -EFAULT; + } + + err = __set_rss_rxfh(netdev, indir, key); + + return err; +} + +static u32 hinic_get_rxfh_key_size(struct net_device *netdev) +{ + return HINIC_RSS_KEY_SIZE; +} + +static u32 hinic_get_rxfh_indir_size(struct net_device *netdev) +{ + return HINIC_RSS_INDIR_SIZE; +} + +#define ARRAY_LEN(arr) ((int)((int)sizeof(arr) / (int)sizeof(arr[0]))) + +#define HINIC_FUNC_STAT(_stat_item) { \ + .name = #_stat_item, \ + .size = FIELD_SIZEOF(struct hinic_vport_stats, _stat_item), \ + .offset = offsetof(struct hinic_vport_stats, _stat_item) \ +} + +static struct hinic_stats hinic_function_stats[] = { + HINIC_FUNC_STAT(tx_unicast_pkts_vport), + HINIC_FUNC_STAT(tx_unicast_bytes_vport), + HINIC_FUNC_STAT(tx_multicast_pkts_vport), + HINIC_FUNC_STAT(tx_multicast_bytes_vport), + HINIC_FUNC_STAT(tx_broadcast_pkts_vport), + HINIC_FUNC_STAT(tx_broadcast_bytes_vport), + + HINIC_FUNC_STAT(rx_unicast_pkts_vport), + HINIC_FUNC_STAT(rx_unicast_bytes_vport), + HINIC_FUNC_STAT(rx_multicast_pkts_vport), + HINIC_FUNC_STAT(rx_multicast_bytes_vport), + HINIC_FUNC_STAT(rx_broadcast_pkts_vport), + HINIC_FUNC_STAT(rx_broadcast_bytes_vport), + + HINIC_FUNC_STAT(tx_discard_vport), + HINIC_FUNC_STAT(rx_discard_vport), + HINIC_FUNC_STAT(tx_err_vport), + HINIC_FUNC_STAT(rx_err_vport), +}; + +#define HINIC_PORT_STAT(_stat_item) { \ + .name = #_stat_item, \ + .size = FIELD_SIZEOF(struct hinic_phy_port_stats, _stat_item), \ + .offset = offsetof(struct hinic_phy_port_stats, _stat_item) \ +} + +static struct hinic_stats hinic_port_stats[] = { + HINIC_PORT_STAT(mac_rx_total_pkt_num), + HINIC_PORT_STAT(mac_rx_total_oct_num), + HINIC_PORT_STAT(mac_rx_bad_pkt_num), + HINIC_PORT_STAT(mac_rx_bad_oct_num), + HINIC_PORT_STAT(mac_rx_good_pkt_num), + HINIC_PORT_STAT(mac_rx_good_oct_num), + HINIC_PORT_STAT(mac_rx_uni_pkt_num), + HINIC_PORT_STAT(mac_rx_multi_pkt_num), + HINIC_PORT_STAT(mac_rx_broad_pkt_num), + HINIC_PORT_STAT(mac_tx_total_pkt_num), + HINIC_PORT_STAT(mac_tx_total_oct_num), + HINIC_PORT_STAT(mac_tx_bad_pkt_num), + HINIC_PORT_STAT(mac_tx_bad_oct_num), + HINIC_PORT_STAT(mac_tx_good_pkt_num), + HINIC_PORT_STAT(mac_tx_good_oct_num), + HINIC_PORT_STAT(mac_tx_uni_pkt_num), + HINIC_PORT_STAT(mac_tx_multi_pkt_num), + HINIC_PORT_STAT(mac_tx_broad_pkt_num), + HINIC_PORT_STAT(mac_rx_fragment_pkt_num), + HINIC_PORT_STAT(mac_rx_undersize_pkt_num), + HINIC_PORT_STAT(mac_rx_undermin_pkt_num), + HINIC_PORT_STAT(mac_rx_64_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_65_127_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_128_255_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_256_511_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_512_1023_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_1024_1518_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_1519_2047_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_2048_4095_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_4096_8191_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_8192_9216_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_9217_12287_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_12288_16383_oct_pkt_num), + HINIC_PORT_STAT(mac_rx_1519_max_good_pkt_num), + HINIC_PORT_STAT(mac_rx_1519_max_bad_pkt_num), + HINIC_PORT_STAT(mac_rx_oversize_pkt_num), + HINIC_PORT_STAT(mac_rx_jabber_pkt_num), + HINIC_PORT_STAT(mac_rx_pause_num), + HINIC_PORT_STAT(mac_rx_pfc_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri0_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri1_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri2_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri3_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri4_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri5_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri6_pkt_num), + HINIC_PORT_STAT(mac_rx_pfc_pri7_pkt_num), + HINIC_PORT_STAT(mac_rx_control_pkt_num), + HINIC_PORT_STAT(mac_rx_sym_err_pkt_num), + HINIC_PORT_STAT(mac_rx_fcs_err_pkt_num), + HINIC_PORT_STAT(mac_rx_send_app_good_pkt_num), + HINIC_PORT_STAT(mac_rx_send_app_bad_pkt_num), + HINIC_PORT_STAT(mac_tx_fragment_pkt_num), + HINIC_PORT_STAT(mac_tx_undersize_pkt_num), + HINIC_PORT_STAT(mac_tx_undermin_pkt_num), + HINIC_PORT_STAT(mac_tx_64_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_65_127_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_128_255_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_256_511_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_512_1023_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_1024_1518_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_1519_2047_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_2048_4095_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_4096_8191_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_8192_9216_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_9217_12287_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_12288_16383_oct_pkt_num), + HINIC_PORT_STAT(mac_tx_1519_max_good_pkt_num), + HINIC_PORT_STAT(mac_tx_1519_max_bad_pkt_num), + HINIC_PORT_STAT(mac_tx_oversize_pkt_num), + HINIC_PORT_STAT(mac_tx_jabber_pkt_num), + HINIC_PORT_STAT(mac_tx_pause_num), + HINIC_PORT_STAT(mac_tx_pfc_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri0_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri1_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri2_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri3_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri4_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri5_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri6_pkt_num), + HINIC_PORT_STAT(mac_tx_pfc_pri7_pkt_num), + HINIC_PORT_STAT(mac_tx_control_pkt_num), + HINIC_PORT_STAT(mac_tx_err_all_pkt_num), + HINIC_PORT_STAT(mac_tx_from_app_good_pkt_num), + HINIC_PORT_STAT(mac_tx_from_app_bad_pkt_num), +}; + +#define HINIC_TXQ_STAT(_stat_item) { \ + .name = "txq%d_"#_stat_item, \ + .size = FIELD_SIZEOF(struct hinic_txq_stats, _stat_item), \ + .offset = offsetof(struct hinic_txq_stats, _stat_item) \ +} + +static struct hinic_stats hinic_tx_queue_stats[] = { + HINIC_TXQ_STAT(pkts), + HINIC_TXQ_STAT(bytes), + HINIC_TXQ_STAT(tx_busy), + HINIC_TXQ_STAT(tx_wake), + HINIC_TXQ_STAT(tx_dropped), + HINIC_TXQ_STAT(big_frags_pkts), +}; + +#define HINIC_RXQ_STAT(_stat_item) { \ + .name = "rxq%d_"#_stat_item, \ + .size = FIELD_SIZEOF(struct hinic_rxq_stats, _stat_item), \ + .offset = offsetof(struct hinic_rxq_stats, _stat_item) \ +} + +static struct hinic_stats hinic_rx_queue_stats[] = { + HINIC_RXQ_STAT(pkts), + HINIC_RXQ_STAT(bytes), + HINIC_RXQ_STAT(errors), + HINIC_RXQ_STAT(csum_errors), + HINIC_RXQ_STAT(other_errors), +}; + +static void get_drv_queue_stats(struct hinic_dev *nic_dev, u64 *data) +{ + struct hinic_txq_stats txq_stats; + struct hinic_rxq_stats rxq_stats; + u16 i = 0, j = 0, qid = 0; + char *p; + + for (qid = 0; qid < nic_dev->num_qps; qid++) { + if (!nic_dev->txqs) + break; + + hinic_txq_get_stats(&nic_dev->txqs[qid], &txq_stats); + for (j = 0; j < ARRAY_LEN(hinic_tx_queue_stats); j++, i++) { + p = (char *)&txq_stats + + hinic_tx_queue_stats[j].offset; + data[i] = (hinic_tx_queue_stats[j].size == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + } + + for (qid = 0; qid < nic_dev->num_qps; qid++) { + if (!nic_dev->rxqs) + break; + + hinic_rxq_get_stats(&nic_dev->rxqs[qid], &rxq_stats); + for (j = 0; j < ARRAY_LEN(hinic_rx_queue_stats); j++, i++) { + p = (char *)&rxq_stats + + hinic_rx_queue_stats[j].offset; + data[i] = (hinic_rx_queue_stats[j].size == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + } +} + +static void hinic_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_vport_stats vport_stats = {0}; + struct hinic_phy_port_stats *port_stats; + u16 i = 0, j = 0; + char *p; + int err; + + err = hinic_get_vport_stats(nic_dev, &vport_stats); + if (err) + netif_err(nic_dev, drv, netdev, + "Failed to get vport stats from firmware\n"); + + for (j = 0; j < ARRAY_LEN(hinic_function_stats); j++, i++) { + p = (char *)&vport_stats + hinic_function_stats[j].offset; + data[i] = (hinic_function_stats[j].size == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + + port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); + if (!port_stats) { + memset(&data[i], 0, + ARRAY_LEN(hinic_port_stats) * sizeof(*data)); + i += ARRAY_LEN(hinic_port_stats); + goto get_drv_stats; + } + + err = hinic_get_phy_port_stats(nic_dev, port_stats); + if (err) + netif_err(nic_dev, drv, netdev, + "Failed to get port stats from firmware\n"); + + for (j = 0; j < ARRAY_LEN(hinic_port_stats); j++, i++) { + p = (char *)port_stats + hinic_port_stats[j].offset; + data[i] = (hinic_port_stats[j].size == + sizeof(u64)) ? *(u64 *)p : *(u32 *)p; + } + + kfree(port_stats); + +get_drv_stats: + get_drv_queue_stats(nic_dev, data + i); +} + +static int hinic_get_sset_count(struct net_device *netdev, int sset) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int count, q_num; + + switch (sset) { + case ETH_SS_STATS: + q_num = nic_dev->num_qps; + count = ARRAY_LEN(hinic_function_stats) + + (ARRAY_LEN(hinic_tx_queue_stats) + + ARRAY_LEN(hinic_rx_queue_stats)) * q_num; + + count += ARRAY_LEN(hinic_port_stats); + + return count; + default: + return -EOPNOTSUPP; + } +} + +static void hinic_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + char *p = (char *)data; + u16 i, j; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) { + memcpy(p, hinic_function_stats[i].name, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + for (i = 0; i < ARRAY_LEN(hinic_port_stats); i++) { + memcpy(p, hinic_port_stats[i].name, + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + + for (i = 0; i < nic_dev->num_qps; i++) { + for (j = 0; j < ARRAY_LEN(hinic_tx_queue_stats); j++) { + sprintf(p, hinic_tx_queue_stats[j].name, i); + p += ETH_GSTRING_LEN; + } + } + + for (i = 0; i < nic_dev->num_qps; i++) { + for (j = 0; j < ARRAY_LEN(hinic_rx_queue_stats); j++) { + sprintf(p, hinic_rx_queue_stats[j].name, i); + p += ETH_GSTRING_LEN; + } + } + + return; + default: + return; + } +} + +static const struct ethtool_ops hinic_ethtool_ops = { + .get_link_ksettings = hinic_get_link_ksettings, + .get_drvinfo = hinic_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ringparam = hinic_get_ringparam, + .get_channels = hinic_get_channels, + .get_rxnfc = hinic_get_rxnfc, + .set_rxnfc = hinic_set_rxnfc, + .get_rxfh_key_size = hinic_get_rxfh_key_size, + .get_rxfh_indir_size = hinic_get_rxfh_indir_size, + .get_rxfh = hinic_get_rxfh, + .set_rxfh = hinic_set_rxfh, + .get_sset_count = hinic_get_sset_count, + .get_ethtool_stats = hinic_get_ethtool_stats, + .get_strings = hinic_get_strings, +}; + +void hinic_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &hinic_ethtool_ops; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index 408705687de6..6f2cf569a283 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -89,9 +89,6 @@ static int get_capability(struct hinic_hwdev *hwdev, if (nic_cap->num_qps > HINIC_Q_CTXT_MAX) nic_cap->num_qps = HINIC_Q_CTXT_MAX; - /* num_qps must be power of 2 */ - nic_cap->num_qps = BIT(fls(nic_cap->num_qps) - 1); - nic_cap->max_qps = dev_cap->max_sqs + 1; if (nic_cap->max_qps != (dev_cap->max_rqs + 1)) return -EFAULT; @@ -304,6 +301,8 @@ static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth, hw_ioctxt.set_cmdq_depth = HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT; hw_ioctxt.cmdq_depth = 0; + hw_ioctxt.lro_en = 1; + hw_ioctxt.rq_depth = ilog2(rq_depth); hw_ioctxt.rx_buf_sz_idx = HINIC_RX_BUF_SZ_IDX; @@ -872,6 +871,13 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) hinic_free_hwif(hwdev->hwif); } +int hinic_hwdev_max_num_qps(struct hinic_hwdev *hwdev) +{ + struct hinic_cap *nic_cap = &hwdev->nic_cap; + + return nic_cap->max_qps; +} + /** * hinic_hwdev_num_qps - return the number QPs available for use * @hwdev: the NIC HW device diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index a0a5b7434ad7..b069045de416 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -41,21 +41,73 @@ enum hinic_port_cmd { HINIC_PORT_CMD_GET_LINK_STATE = 24, + HINIC_PORT_CMD_SET_LRO = 25, + HINIC_PORT_CMD_SET_RX_CSUM = 26, + HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD = 27, + + HINIC_PORT_CMD_GET_PORT_STATISTICS = 28, + + HINIC_PORT_CMD_CLEAR_PORT_STATISTICS = 29, + + HINIC_PORT_CMD_GET_VPORT_STAT = 30, + + HINIC_PORT_CMD_CLEAN_VPORT_STAT = 31, + + HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL = 37, + HINIC_PORT_CMD_SET_PORT_STATE = 41, + HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL = 43, + + HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL = 44, + + HINIC_PORT_CMD_SET_RSS_HASH_ENGINE = 45, + + HINIC_PORT_CMD_GET_RSS_HASH_ENGINE = 46, + + HINIC_PORT_CMD_GET_RSS_CTX_TBL = 47, + + HINIC_PORT_CMD_SET_RSS_CTX_TBL = 48, + + HINIC_PORT_CMD_RSS_TEMP_MGR = 49, + + HINIC_PORT_CMD_RSS_CFG = 66, + HINIC_PORT_CMD_FWCTXT_INIT = 69, + HINIC_PORT_CMD_GET_MGMT_VERSION = 88, + HINIC_PORT_CMD_SET_FUNC_STATE = 93, HINIC_PORT_CMD_GET_GLOBAL_QPN = 102, HINIC_PORT_CMD_SET_TSO = 112, + HINIC_PORT_CMD_SET_RQ_IQ_MAP = 115, + HINIC_PORT_CMD_GET_CAP = 170, + + HINIC_PORT_CMD_SET_LRO_TIMER = 244, }; +enum hinic_ucode_cmd { + HINIC_UCODE_CMD_MODIFY_QUEUE_CONTEXT = 0, + HINIC_UCODE_CMD_CLEAN_QUEUE_CONTEXT, + HINIC_UCODE_CMD_ARM_SQ, + HINIC_UCODE_CMD_ARM_RQ, + HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, + HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE, + HINIC_UCODE_CMD_GET_RSS_INDIR_TABLE, + HINIC_UCODE_CMD_GET_RSS_CONTEXT_TABLE, + HINIC_UCODE_CMD_SET_IQ_ENABLE, + HINIC_UCODE_CMD_SET_RQ_FLUSH = 10 +}; + +#define NIC_RSS_CMD_TEMP_ALLOC 0x01 +#define NIC_RSS_CMD_TEMP_FREE 0x02 + enum hinic_mgmt_msg_cmd { HINIC_MGMT_MSG_CMD_BASE = 160, @@ -97,7 +149,7 @@ struct hinic_cmd_hw_ioctxt { u8 set_cmdq_depth; u8 cmdq_depth; - u8 rsvd2; + u8 lro_en; u8 rsvd3; u8 rsvd4; u8 rsvd5; @@ -215,6 +267,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); void hinic_free_hwdev(struct hinic_hwdev *hwdev); +int hinic_hwdev_max_num_qps(struct hinic_hwdev *hwdev); + int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev); struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c index 2d07bdd17432..d66f86fa3f46 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -36,6 +36,7 @@ enum io_cmd { IO_CMD_MODIFY_QUEUE_CTXT = 0, + IO_CMD_CLEAN_QUEUE_CTXT, }; static void init_db_area_idx(struct hinic_free_db_area *free_db_area) @@ -201,6 +202,59 @@ static int write_qp_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, write_rq_ctxts(func_to_io, base_qpn, num_qps)); } +static int hinic_clean_queue_offload_ctxt(struct hinic_func_to_io *func_to_io, + enum hinic_qp_ctxt_type ctxt_type) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct hinic_clean_queue_ctxt *ctxt_block; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_buf cmdq_buf; + u64 out_param = 0; + int err; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + ctxt_block = cmdq_buf.buf; + ctxt_block->cmdq_hdr.num_queues = func_to_io->max_qps; + ctxt_block->cmdq_hdr.queue_type = ctxt_type; + ctxt_block->cmdq_hdr.addr_offset = 0; + + /* TSO/LRO ctxt size: 0x0:0B; 0x1:160B; 0x2:200B; 0x3:240B */ + ctxt_block->ctxt_size = 0x3; + + hinic_cpu_to_be32(ctxt_block, sizeof(*ctxt_block)); + + cmdq_buf.size = sizeof(*ctxt_block); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + IO_CMD_CLEAN_QUEUE_CTXT, + &cmdq_buf, &out_param); + + if (err || out_param) { + dev_err(&pdev->dev, "Failed to clean offload ctxts, err: %d, out_param: 0x%llx\n", + err, out_param); + + err = -EFAULT; + } + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + + return err; +} + +static int hinic_clean_qp_offload_ctxt(struct hinic_func_to_io *func_to_io) +{ + /* clean LRO/TSO context space */ + return (hinic_clean_queue_offload_ctxt(func_to_io, + HINIC_QP_CTXT_TYPE_SQ) || + hinic_clean_queue_offload_ctxt(func_to_io, + HINIC_QP_CTXT_TYPE_RQ)); +} + /** * init_qp - Initialize a Queue Pair * @func_to_io: func to io channel that holds the IO components @@ -372,6 +426,12 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, goto err_write_qp_ctxts; } + err = hinic_clean_qp_offload_ctxt(func_to_io); + if (err) { + dev_err(&pdev->dev, "Failed to clean QP contexts space\n"); + goto err_write_qp_ctxts; + } + return 0; err_write_qp_ctxts: diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h index 1856fdcc1e32..00900a6640ad 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h @@ -192,6 +192,11 @@ struct hinic_rq_ctxt { u32 wq_block_lo_pfn; }; +struct hinic_clean_queue_ctxt { + struct hinic_qp_ctxt_header cmdq_hdr; + u32 ctxt_size; +}; + struct hinic_sq_ctxt_block { struct hinic_qp_ctxt_header hdr; struct hinic_sq_ctxt sq_ctxt[HINIC_Q_CTXT_MAX]; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h index 8991c9a5ef04..f4b6d2c1061f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h @@ -210,6 +210,57 @@ #define HINIC_MSS_DEFAULT 0x3E00 #define HINIC_MSS_MIN 0x50 +#define RQ_CQE_STATUS_NUM_LRO_SHIFT 16 +#define RQ_CQE_STATUS_NUM_LRO_MASK 0xFFU + +#define RQ_CQE_STATUS_GET(val, member) (((val) >> \ + RQ_CQE_STATUS_##member##_SHIFT) & \ + RQ_CQE_STATUS_##member##_MASK) + +#define HINIC_GET_RX_NUM_LRO(status) \ + RQ_CQE_STATUS_GET(status, NUM_LRO) + +#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_SHIFT 0 +#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK 0xFFFU +#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_SHIFT 21 +#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_MASK 0x1U + +#define RQ_CQE_OFFOLAD_TYPE_GET(val, member) (((val) >> \ + RQ_CQE_OFFOLAD_TYPE_##member##_SHIFT) & \ + RQ_CQE_OFFOLAD_TYPE_##member##_MASK) + +#define HINIC_GET_RX_PKT_TYPE(offload_type) \ + RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_TYPE) + +#define HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type) \ + RQ_CQE_OFFOLAD_TYPE_GET(offload_type, VLAN_EN) + +#define RQ_CQE_SGE_VLAN_MASK 0xFFFFU +#define RQ_CQE_SGE_VLAN_SHIFT 0 + +#define RQ_CQE_SGE_GET(val, member) (((val) >> \ + RQ_CQE_SGE_##member##_SHIFT) & \ + RQ_CQE_SGE_##member##_MASK) + +#define HINIC_GET_RX_VLAN_TAG(vlan_len) \ + RQ_CQE_SGE_GET(vlan_len, VLAN) + +#define HINIC_RSS_TYPE_VALID_SHIFT 23 +#define HINIC_RSS_TYPE_TCP_IPV6_EXT_SHIFT 24 +#define HINIC_RSS_TYPE_IPV6_EXT_SHIFT 25 +#define HINIC_RSS_TYPE_TCP_IPV6_SHIFT 26 +#define HINIC_RSS_TYPE_IPV6_SHIFT 27 +#define HINIC_RSS_TYPE_TCP_IPV4_SHIFT 28 +#define HINIC_RSS_TYPE_IPV4_SHIFT 29 +#define HINIC_RSS_TYPE_UDP_IPV6_SHIFT 30 +#define HINIC_RSS_TYPE_UDP_IPV4_SHIFT 31 + +#define HINIC_RSS_TYPE_SET(val, member) \ + (((u32)(val) & 0x1) << HINIC_RSS_TYPE_##member##_SHIFT) + +#define HINIC_RSS_TYPE_GET(val, member) \ + (((u32)(val) >> HINIC_RSS_TYPE_##member##_SHIFT) & 0x1) + enum hinic_l4offload_type { HINIC_L4_OFF_DISABLE = 0, HINIC_TCP_OFFLOAD_ENABLE = 1, @@ -363,7 +414,7 @@ struct hinic_rq_cqe { u32 status; u32 len; - u32 rsvd2; + u32 offload_type; u32 rsvd3; u32 rsvd4; u32 rsvd5; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index b695d29d364c..2411ad270c98 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -53,6 +53,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); NETIF_MSG_IFUP | \ NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) +#define HINIC_LRO_MAX_WQE_NUM_DEFAULT 8 + +#define HINIC_LRO_RX_TIMER_DEFAULT 16 + #define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8) #define work_to_rx_mode_work(work) \ @@ -63,137 +67,9 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); static int change_mac_addr(struct net_device *netdev, const u8 *addr); -static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, - enum hinic_speed speed) -{ - switch (speed) { - case HINIC_SPEED_10MB_LINK: - link_ksettings->base.speed = SPEED_10; - break; - - case HINIC_SPEED_100MB_LINK: - link_ksettings->base.speed = SPEED_100; - break; - - case HINIC_SPEED_1000MB_LINK: - link_ksettings->base.speed = SPEED_1000; - break; - - case HINIC_SPEED_10GB_LINK: - link_ksettings->base.speed = SPEED_10000; - break; - - case HINIC_SPEED_25GB_LINK: - link_ksettings->base.speed = SPEED_25000; - break; - - case HINIC_SPEED_40GB_LINK: - link_ksettings->base.speed = SPEED_40000; - break; - - case HINIC_SPEED_100GB_LINK: - link_ksettings->base.speed = SPEED_100000; - break; - - default: - link_ksettings->base.speed = SPEED_UNKNOWN; - break; - } -} - -static int hinic_get_link_ksettings(struct net_device *netdev, - struct ethtool_link_ksettings - *link_ksettings) -{ - struct hinic_dev *nic_dev = netdev_priv(netdev); - enum hinic_port_link_state link_state; - struct hinic_port_cap port_cap; - int err; - - ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); - ethtool_link_ksettings_add_link_mode(link_ksettings, supported, - Autoneg); - - link_ksettings->base.speed = SPEED_UNKNOWN; - link_ksettings->base.autoneg = AUTONEG_DISABLE; - link_ksettings->base.duplex = DUPLEX_UNKNOWN; - - err = hinic_port_get_cap(nic_dev, &port_cap); - if (err) { - netif_err(nic_dev, drv, netdev, - "Failed to get port capabilities\n"); - return err; - } - - err = hinic_port_link_state(nic_dev, &link_state); - if (err) { - netif_err(nic_dev, drv, netdev, - "Failed to get port link state\n"); - return err; - } - - if (link_state != HINIC_LINK_STATE_UP) { - netif_info(nic_dev, drv, netdev, "No link\n"); - return err; - } - - set_link_speed(link_ksettings, port_cap.speed); - - if (!!(port_cap.autoneg_cap & HINIC_AUTONEG_SUPPORTED)) - ethtool_link_ksettings_add_link_mode(link_ksettings, - advertising, Autoneg); - - if (port_cap.autoneg_state == HINIC_AUTONEG_ACTIVE) - link_ksettings->base.autoneg = AUTONEG_ENABLE; - - link_ksettings->base.duplex = (port_cap.duplex == HINIC_DUPLEX_FULL) ? - DUPLEX_FULL : DUPLEX_HALF; - return 0; -} - -static void hinic_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info) -{ - struct hinic_dev *nic_dev = netdev_priv(netdev); - struct hinic_hwdev *hwdev = nic_dev->hwdev; - struct hinic_hwif *hwif = hwdev->hwif; - - strlcpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); - strlcpy(info->bus_info, pci_name(hwif->pdev), sizeof(info->bus_info)); -} - -static void hinic_get_ringparam(struct net_device *netdev, - struct ethtool_ringparam *ring) -{ - ring->rx_max_pending = HINIC_RQ_DEPTH; - ring->tx_max_pending = HINIC_SQ_DEPTH; - ring->rx_pending = HINIC_RQ_DEPTH; - ring->tx_pending = HINIC_SQ_DEPTH; -} - -static void hinic_get_channels(struct net_device *netdev, - struct ethtool_channels *channels) -{ - struct hinic_dev *nic_dev = netdev_priv(netdev); - struct hinic_hwdev *hwdev = nic_dev->hwdev; - - channels->max_rx = hwdev->nic_cap.max_qps; - channels->max_tx = hwdev->nic_cap.max_qps; - channels->max_other = 0; - channels->max_combined = 0; - channels->rx_count = hinic_hwdev_num_qps(hwdev); - channels->tx_count = hinic_hwdev_num_qps(hwdev); - channels->other_count = 0; - channels->combined_count = 0; -} - -static const struct ethtool_ops hinic_ethtool_ops = { - .get_link_ksettings = hinic_get_link_ksettings, - .get_drvinfo = hinic_get_drvinfo, - .get_link = ethtool_op_get_link, - .get_ringparam = hinic_get_ringparam, - .get_channels = hinic_get_channels, -}; +static int set_features(struct hinic_dev *nic_dev, + netdev_features_t pre_features, + netdev_features_t features, bool force_change); static void update_rx_stats(struct hinic_dev *nic_dev, struct hinic_rxq *rxq) { @@ -207,6 +83,9 @@ static void update_rx_stats(struct hinic_dev *nic_dev, struct hinic_rxq *rxq) u64_stats_update_begin(&nic_rx_stats->syncp); nic_rx_stats->bytes += rx_stats.bytes; nic_rx_stats->pkts += rx_stats.pkts; + nic_rx_stats->errors += rx_stats.errors; + nic_rx_stats->csum_errors += rx_stats.csum_errors; + nic_rx_stats->other_errors += rx_stats.other_errors; u64_stats_update_end(&nic_rx_stats->syncp); hinic_rxq_clean_stats(rxq); @@ -227,6 +106,7 @@ static void update_tx_stats(struct hinic_dev *nic_dev, struct hinic_txq *txq) nic_tx_stats->tx_busy += tx_stats.tx_busy; nic_tx_stats->tx_wake += tx_stats.tx_wake; nic_tx_stats->tx_dropped += tx_stats.tx_dropped; + nic_tx_stats->big_frags_pkts += tx_stats.big_frags_pkts; u64_stats_update_end(&nic_tx_stats->syncp); hinic_txq_clean_stats(txq); @@ -363,11 +243,135 @@ static void free_rxqs(struct hinic_dev *nic_dev) nic_dev->rxqs = NULL; } +static int hinic_configure_max_qnum(struct hinic_dev *nic_dev) +{ + int err; + + err = hinic_set_max_qnum(nic_dev, nic_dev->hwdev->nic_cap.max_qps); + if (err) + return err; + + return 0; +} + +static int hinic_rss_init(struct hinic_dev *nic_dev) +{ + u8 default_rss_key[HINIC_RSS_KEY_SIZE]; + u8 tmpl_idx = nic_dev->rss_tmpl_idx; + u32 *indir_tbl; + int err, i; + + indir_tbl = kcalloc(HINIC_RSS_INDIR_SIZE, sizeof(u32), GFP_KERNEL); + if (!indir_tbl) + return -ENOMEM; + + netdev_rss_key_fill(default_rss_key, sizeof(default_rss_key)); + for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) + indir_tbl[i] = ethtool_rxfh_indir_default(i, nic_dev->num_rss); + + err = hinic_rss_set_template_tbl(nic_dev, tmpl_idx, default_rss_key); + if (err) + goto out; + + err = hinic_rss_set_indir_tbl(nic_dev, tmpl_idx, indir_tbl); + if (err) + goto out; + + err = hinic_set_rss_type(nic_dev, tmpl_idx, nic_dev->rss_type); + if (err) + goto out; + + err = hinic_rss_set_hash_engine(nic_dev, tmpl_idx, + nic_dev->rss_hash_engine); + if (err) + goto out; + + err = hinic_rss_cfg(nic_dev, 1, tmpl_idx); + if (err) + goto out; + +out: + kfree(indir_tbl); + return err; +} + +static void hinic_rss_deinit(struct hinic_dev *nic_dev) +{ + hinic_rss_cfg(nic_dev, 0, nic_dev->rss_tmpl_idx); +} + +static void hinic_init_rss_parameters(struct hinic_dev *nic_dev) +{ + nic_dev->rss_hash_engine = HINIC_RSS_HASH_ENGINE_TYPE_XOR; + nic_dev->rss_type.tcp_ipv6_ext = 1; + nic_dev->rss_type.ipv6_ext = 1; + nic_dev->rss_type.tcp_ipv6 = 1; + nic_dev->rss_type.ipv6 = 1; + nic_dev->rss_type.tcp_ipv4 = 1; + nic_dev->rss_type.ipv4 = 1; + nic_dev->rss_type.udp_ipv6 = 1; + nic_dev->rss_type.udp_ipv4 = 1; +} + +static void hinic_enable_rss(struct hinic_dev *nic_dev) +{ + struct net_device *netdev = nic_dev->netdev; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int i, node, err = 0; + u16 num_cpus = 0; + + nic_dev->max_qps = hinic_hwdev_max_num_qps(hwdev); + if (nic_dev->max_qps <= 1) { + nic_dev->flags &= ~HINIC_RSS_ENABLE; + nic_dev->rss_limit = nic_dev->max_qps; + nic_dev->num_qps = nic_dev->max_qps; + nic_dev->num_rss = nic_dev->max_qps; + + return; + } + + err = hinic_rss_template_alloc(nic_dev, &nic_dev->rss_tmpl_idx); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to alloc tmpl_idx for rss, can't enable rss for this function\n"); + nic_dev->flags &= ~HINIC_RSS_ENABLE; + nic_dev->max_qps = 1; + nic_dev->rss_limit = nic_dev->max_qps; + nic_dev->num_qps = nic_dev->max_qps; + nic_dev->num_rss = nic_dev->max_qps; + + return; + } + + nic_dev->flags |= HINIC_RSS_ENABLE; + + for (i = 0; i < num_online_cpus(); i++) { + node = cpu_to_node(i); + if (node == dev_to_node(&pdev->dev)) + num_cpus++; + } + + if (!num_cpus) + num_cpus = num_online_cpus(); + + nic_dev->num_qps = min_t(u16, nic_dev->max_qps, num_cpus); + + nic_dev->rss_limit = nic_dev->num_qps; + nic_dev->num_rss = nic_dev->num_qps; + + hinic_init_rss_parameters(nic_dev); + err = hinic_rss_init(nic_dev); + if (err) + netif_err(nic_dev, drv, netdev, "Failed to init rss\n"); +} + static int hinic_open(struct net_device *netdev) { struct hinic_dev *nic_dev = netdev_priv(netdev); enum hinic_port_link_state link_state; - int err, ret, num_qps; + int err, ret; if (!(nic_dev->flags & HINIC_INTF_UP)) { err = hinic_hwdev_ifup(nic_dev->hwdev); @@ -392,9 +396,17 @@ static int hinic_open(struct net_device *netdev) goto err_create_rxqs; } - num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); - netif_set_real_num_tx_queues(netdev, num_qps); - netif_set_real_num_rx_queues(netdev, num_qps); + hinic_enable_rss(nic_dev); + + err = hinic_configure_max_qnum(nic_dev); + if (err) { + netif_err(nic_dev, drv, nic_dev->netdev, + "Failed to configure the maximum number of queues\n"); + goto err_port_state; + } + + netif_set_real_num_tx_queues(netdev, nic_dev->num_qps); + netif_set_real_num_rx_queues(netdev, nic_dev->num_qps); err = hinic_port_set_state(nic_dev, HINIC_PORT_ENABLE); if (err) { @@ -450,9 +462,12 @@ err_func_port_state: if (ret) netif_warn(nic_dev, drv, netdev, "Failed to revert port state\n"); - err_port_state: free_rxqs(nic_dev); + if (nic_dev->flags & HINIC_RSS_ENABLE) { + hinic_rss_deinit(nic_dev); + hinic_rss_template_free(nic_dev, nic_dev->rss_tmpl_idx); + } err_create_rxqs: free_txqs(nic_dev); @@ -496,6 +511,11 @@ static int hinic_close(struct net_device *netdev) return err; } + if (nic_dev->flags & HINIC_RSS_ENABLE) { + hinic_rss_deinit(nic_dev); + hinic_rss_template_free(nic_dev, nic_dev->rss_tmpl_idx); + } + free_rxqs(nic_dev); free_txqs(nic_dev); @@ -715,7 +735,6 @@ static void set_rx_mode(struct work_struct *work) { struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work); struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work); - struct netdev_hw_addr *ha; netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n"); @@ -723,9 +742,6 @@ static void set_rx_mode(struct work_struct *work) __dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr); __dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr); - - netdev_for_each_mc_addr(ha, nic_dev->netdev) - add_mac_addr(nic_dev->netdev, ha->addr); } static void hinic_set_rx_mode(struct net_device *netdev) @@ -776,12 +792,36 @@ static void hinic_get_stats64(struct net_device *netdev, stats->rx_bytes = nic_rx_stats->bytes; stats->rx_packets = nic_rx_stats->pkts; + stats->rx_errors = nic_rx_stats->errors; stats->tx_bytes = nic_tx_stats->bytes; stats->tx_packets = nic_tx_stats->pkts; stats->tx_errors = nic_tx_stats->tx_dropped; } +static int hinic_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + + return set_features(nic_dev, nic_dev->netdev->features, + features, false); +} + +static netdev_features_t hinic_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + + /* If Rx checksum is disabled, then LRO should also be disabled */ + if (!(features & NETIF_F_RXCSUM)) { + netif_info(nic_dev, drv, netdev, "disabling LRO as RXCSUM is off\n"); + features &= ~NETIF_F_LRO; + } + + return features; +} + static const struct net_device_ops hinic_netdev_ops = { .ndo_open = hinic_open, .ndo_stop = hinic_close, @@ -794,13 +834,16 @@ static const struct net_device_ops hinic_netdev_ops = { .ndo_start_xmit = hinic_xmit_frame, .ndo_tx_timeout = hinic_tx_timeout, .ndo_get_stats64 = hinic_get_stats64, + .ndo_fix_features = hinic_fix_features, + .ndo_set_features = hinic_set_features, }; static void netdev_features_init(struct net_device *netdev) { netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 | - NETIF_F_RXCSUM; + NETIF_F_RXCSUM | NETIF_F_LRO | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; netdev->vlan_features = netdev->hw_features; @@ -873,6 +916,18 @@ static int set_features(struct hinic_dev *nic_dev, if (changed & NETIF_F_RXCSUM) err = hinic_set_rx_csum_offload(nic_dev, csum_en); + if (changed & NETIF_F_LRO) { + err = hinic_set_rx_lro_state(nic_dev, + !!(features & NETIF_F_LRO), + HINIC_LRO_RX_TIMER_DEFAULT, + HINIC_LRO_MAX_WQE_NUM_DEFAULT); + } + + if (changed & NETIF_F_HW_VLAN_CTAG_RX) + err = hinic_set_rx_vlan_offload(nic_dev, + !!(features & + NETIF_F_HW_VLAN_CTAG_RX)); + return err; } @@ -912,8 +967,8 @@ static int nic_dev_init(struct pci_dev *pdev) goto err_alloc_etherdev; } + hinic_set_ethtool_ops(netdev); netdev->netdev_ops = &hinic_netdev_ops; - netdev->ethtool_ops = &hinic_ethtool_ops; netdev->max_mtu = ETH_MAX_MTU; nic_dev = netdev_priv(netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 4b3b7d39e437..1e389a004e50 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -430,3 +430,641 @@ int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en) return 0; } + +int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_vlan_cfg vlan_cfg; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size; + int err; + + if (!hwdev) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + vlan_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); + vlan_cfg.vlan_rx_offload = en; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD, + &vlan_cfg, sizeof(vlan_cfg), + &vlan_cfg, &out_size); + if (err || !out_size || vlan_cfg.status) { + dev_err(&pdev->dev, + "Failed to set rx vlan offload, err: %d, status: 0x%x, out size: 0x%x\n", + err, vlan_cfg.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_rq_num rq_num = { 0 }; + u16 out_size = sizeof(rq_num); + int err; + + rq_num.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rq_num.num_rqs = num_rqs; + rq_num.rq_depth = ilog2(HINIC_SQ_DEPTH); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RQ_IQ_MAP, + &rq_num, sizeof(rq_num), + &rq_num, &out_size); + if (err || !out_size || rq_num.status) { + dev_err(&pdev->dev, + "Failed to rxq number, ret = %d\n", + rq_num.status); + return -EINVAL; + } + + return 0; +} + +static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en, + u8 max_wqe_num) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_lro_config lro_cfg = { 0 }; + struct pci_dev *pdev = hwif->pdev; + u16 out_size = sizeof(lro_cfg); + int err; + + lro_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); + lro_cfg.lro_ipv4_en = ipv4_en; + lro_cfg.lro_ipv6_en = ipv6_en; + lro_cfg.lro_max_wqe_num = max_wqe_num; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO, + &lro_cfg, sizeof(lro_cfg), + &lro_cfg, &out_size); + if (err || !out_size || lro_cfg.status) { + dev_err(&pdev->dev, + "Failed to set lro offload, ret = %d\n", + lro_cfg.status); + return -EINVAL; + } + + return 0; +} + +static int hinic_set_rx_lro_timer(struct hinic_dev *nic_dev, u32 timer_value) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_lro_timer lro_timer = { 0 }; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size = sizeof(lro_timer); + int err; + + lro_timer.status = 0; + lro_timer.type = 0; + lro_timer.enable = 1; + lro_timer.timer = timer_value; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO_TIMER, + &lro_timer, sizeof(lro_timer), + &lro_timer, &out_size); + if (lro_timer.status == 0xFF) { + /* For this case, we think status (0xFF) is OK */ + lro_timer.status = 0; + dev_dbg(&pdev->dev, + "Set lro timer not supported by the current FW version, it will be 1ms default\n"); + } + + if (err || !out_size || lro_timer.status) { + dev_err(&pdev->dev, + "Failed to set lro timer, ret = %d\n", + lro_timer.status); + + return -EINVAL; + } + + return 0; +} + +int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en, + u32 lro_timer, u32 wqe_num) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + u8 ipv4_en; + u8 ipv6_en; + int err; + + if (!hwdev) + return -EINVAL; + + ipv4_en = lro_en ? 1 : 0; + ipv6_en = lro_en ? 1 : 0; + + err = hinic_set_rx_lro(nic_dev, ipv4_en, ipv6_en, (u8)wqe_num); + if (err) + return err; + + err = hinic_set_rx_lro_timer(nic_dev, lro_timer); + if (err) + return err; + + return 0; +} + +int hinic_rss_set_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + const u32 *indir_table) +{ + struct hinic_rss_indirect_tbl *indir_tbl; + struct hinic_func_to_io *func_to_io; + struct hinic_cmdq_buf cmd_buf; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u32 indir_size; + u64 out_param; + int err, i; + u32 *temp; + + hwdev = nic_dev->hwdev; + func_to_io = &hwdev->func_to_io; + hwif = hwdev->hwif; + pdev = hwif->pdev; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + cmd_buf.size = sizeof(*indir_tbl); + + indir_tbl = cmd_buf.buf; + indir_tbl->group_index = cpu_to_be32(tmpl_idx); + + for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) { + indir_tbl->entry[i] = indir_table[i]; + + if (0x3 == (i & 0x3)) { + temp = (u32 *)&indir_tbl->entry[i - 3]; + *temp = cpu_to_be32(*temp); + } + } + + /* cfg the rss indirect table by command queue */ + indir_size = HINIC_RSS_INDIR_SIZE / 2; + indir_tbl->offset = 0; + indir_tbl->size = cpu_to_be32(indir_size); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, + &cmd_buf, &out_param); + if (err || out_param != 0) { + dev_err(&pdev->dev, "Failed to set rss indir table\n"); + err = -EFAULT; + goto free_buf; + } + + indir_tbl->offset = cpu_to_be32(indir_size); + indir_tbl->size = cpu_to_be32(indir_size); + memcpy(&indir_tbl->entry[0], &indir_tbl->entry[indir_size], indir_size); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, + &cmd_buf, &out_param); + if (err || out_param != 0) { + dev_err(&pdev->dev, "Failed to set rss indir table\n"); + err = -EFAULT; + } + +free_buf: + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + + return err; +} + +int hinic_rss_get_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + u32 *indir_table) +{ + struct hinic_rss_indir_table rss_cfg = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size = sizeof(rss_cfg); + int err = 0, i; + + rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_cfg.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, + HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL, + &rss_cfg, sizeof(rss_cfg), &rss_cfg, + &out_size); + if (err || !out_size || rss_cfg.status) { + dev_err(&pdev->dev, "Failed to get indir table, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_cfg.status, out_size); + return -EINVAL; + } + + hinic_be32_to_cpu(rss_cfg.indir, HINIC_RSS_INDIR_SIZE); + for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) + indir_table[i] = rss_cfg.indir[i]; + + return 0; +} + +int hinic_set_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, + struct hinic_rss_type rss_type) +{ + struct hinic_rss_context_tbl *ctx_tbl; + struct hinic_func_to_io *func_to_io; + struct hinic_cmdq_buf cmd_buf; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u64 out_param; + u32 ctx = 0; + int err; + + hwdev = nic_dev->hwdev; + func_to_io = &hwdev->func_to_io; + hwif = hwdev->hwif; + pdev = hwif->pdev; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmd buf\n"); + return -ENOMEM; + } + + ctx |= HINIC_RSS_TYPE_SET(1, VALID) | + HINIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) | + HINIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) | + HINIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) | + HINIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) | + HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) | + HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) | + HINIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) | + HINIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6); + + cmd_buf.size = sizeof(struct hinic_rss_context_tbl); + + ctx_tbl = (struct hinic_rss_context_tbl *)cmd_buf.buf; + ctx_tbl->group_index = cpu_to_be32(tmpl_idx); + ctx_tbl->offset = 0; + ctx_tbl->size = sizeof(u32); + ctx_tbl->size = cpu_to_be32(ctx_tbl->size); + ctx_tbl->rsvd = 0; + ctx_tbl->ctx = cpu_to_be32(ctx); + + /* cfg the rss context table by command queue */ + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE, + &cmd_buf, &out_param); + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); + + if (err || out_param != 0) { + dev_err(&pdev->dev, "Failed to set rss context table, err: %d\n", + err); + return -EFAULT; + } + + return 0; +} + +int hinic_get_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, + struct hinic_rss_type *rss_type) +{ + struct hinic_rss_context_table ctx_tbl = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size = sizeof(ctx_tbl); + int err; + + if (!hwdev || !rss_type) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + ctx_tbl.func_id = HINIC_HWIF_FUNC_IDX(hwif); + ctx_tbl.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_CTX_TBL, + &ctx_tbl, sizeof(ctx_tbl), + &ctx_tbl, &out_size); + if (err || !out_size || ctx_tbl.status) { + dev_err(&pdev->dev, "Failed to get hash type, err: %d, status: 0x%x, out size: 0x%x\n", + err, ctx_tbl.status, out_size); + return -EINVAL; + } + + rss_type->ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV4); + rss_type->ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6); + rss_type->ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT); + rss_type->tcp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4); + rss_type->tcp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6); + rss_type->tcp_ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, + TCP_IPV6_EXT); + rss_type->udp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4); + rss_type->udp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6); + + return 0; +} + +int hinic_rss_set_template_tbl(struct hinic_dev *nic_dev, u32 template_id, + const u8 *temp) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_rss_key rss_key = { 0 }; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + rss_key.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_key.template_id = template_id; + memcpy(rss_key.key, temp, HINIC_RSS_KEY_SIZE); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL, + &rss_key, sizeof(rss_key), + &rss_key, &out_size); + if (err || !out_size || rss_key.status) { + dev_err(&pdev->dev, + "Failed to set rss hash key, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_key.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_rss_get_template_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + u8 *temp) +{ + struct hinic_rss_template_key temp_key = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size = sizeof(temp_key); + int err; + + if (!hwdev || !temp) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + temp_key.func_id = HINIC_HWIF_FUNC_IDX(hwif); + temp_key.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, + &temp_key, sizeof(temp_key), + &temp_key, &out_size); + if (err || !out_size || temp_key.status) { + dev_err(&pdev->dev, "Failed to set hash key, err: %d, status: 0x%x, out size: 0x%x\n", + err, temp_key.status, out_size); + return -EINVAL; + } + + memcpy(temp, temp_key.key, HINIC_RSS_KEY_SIZE); + + return 0; +} + +int hinic_rss_set_hash_engine(struct hinic_dev *nic_dev, u8 template_id, + u8 type) +{ + struct hinic_rss_engine_type rss_engine = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + rss_engine.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_engine.hash_engine = type; + rss_engine.template_id = template_id; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, + &rss_engine, sizeof(rss_engine), + &rss_engine, &out_size); + if (err || !out_size || rss_engine.status) { + dev_err(&pdev->dev, + "Failed to set hash engine, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_engine.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_rss_get_hash_engine(struct hinic_dev *nic_dev, u8 tmpl_idx, u8 *type) +{ + struct hinic_rss_engine_type hash_type = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size = sizeof(hash_type); + int err; + + if (!hwdev || !type) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + hash_type.func_id = HINIC_HWIF_FUNC_IDX(hwif); + hash_type.template_id = tmpl_idx; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, + &hash_type, sizeof(hash_type), + &hash_type, &out_size); + if (err || !out_size || hash_type.status) { + dev_err(&pdev->dev, "Failed to get hash engine, err: %d, status: 0x%x, out size: 0x%x\n", + err, hash_type.status, out_size); + return -EINVAL; + } + + *type = hash_type.hash_engine; + return 0; +} + +int hinic_rss_cfg(struct hinic_dev *nic_dev, u8 rss_en, u8 template_id) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_rss_config rss_cfg = { 0 }; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); + rss_cfg.rss_en = rss_en; + rss_cfg.template_id = template_id; + rss_cfg.rq_priority_number = 0; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_CFG, + &rss_cfg, sizeof(rss_cfg), + &rss_cfg, &out_size); + if (err || !out_size || rss_cfg.status) { + dev_err(&pdev->dev, + "Failed to set rss cfg, err: %d, status: 0x%x, out size: 0x%x\n", + err, rss_cfg.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_rss_template_alloc(struct hinic_dev *nic_dev, u8 *tmpl_idx) +{ + struct hinic_rss_template_mgmt template_mgmt = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif); + template_mgmt.cmd = NIC_RSS_CMD_TEMP_ALLOC; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, + &template_mgmt, sizeof(template_mgmt), + &template_mgmt, &out_size); + if (err || !out_size || template_mgmt.status) { + dev_err(&pdev->dev, "Failed to alloc rss template, err: %d, status: 0x%x, out size: 0x%x\n", + err, template_mgmt.status, out_size); + return -EINVAL; + } + + *tmpl_idx = template_mgmt.template_id; + + return 0; +} + +int hinic_rss_template_free(struct hinic_dev *nic_dev, u8 tmpl_idx) +{ + struct hinic_rss_template_mgmt template_mgmt = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif); + template_mgmt.template_id = tmpl_idx; + template_mgmt.cmd = NIC_RSS_CMD_TEMP_FREE; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, + &template_mgmt, sizeof(template_mgmt), + &template_mgmt, &out_size); + if (err || !out_size || template_mgmt.status) { + dev_err(&pdev->dev, "Failed to free rss template, err: %d, status: 0x%x, out size: 0x%x\n", + err, template_mgmt.status, out_size); + return -EINVAL; + } + + return 0; +} + +int hinic_get_vport_stats(struct hinic_dev *nic_dev, + struct hinic_vport_stats *stats) +{ + struct hinic_cmd_vport_stats vport_stats = { 0 }; + struct hinic_port_stats_info stats_info = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + u16 out_size = sizeof(vport_stats); + struct pci_dev *pdev = hwif->pdev; + int err; + + stats_info.stats_version = HINIC_PORT_STATS_VERSION; + stats_info.func_id = HINIC_HWIF_FUNC_IDX(hwif); + stats_info.stats_size = sizeof(vport_stats); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_VPORT_STAT, + &stats_info, sizeof(stats_info), + &vport_stats, &out_size); + if (err || !out_size || vport_stats.status) { + dev_err(&pdev->dev, + "Failed to get function statistics, err: %d, status: 0x%x, out size: 0x%x\n", + err, vport_stats.status, out_size); + return -EFAULT; + } + + memcpy(stats, &vport_stats.stats, sizeof(*stats)); + return 0; +} + +int hinic_get_phy_port_stats(struct hinic_dev *nic_dev, + struct hinic_phy_port_stats *stats) +{ + struct hinic_port_stats_info stats_info = { 0 }; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_port_stats *port_stats; + u16 out_size = sizeof(*port_stats); + struct pci_dev *pdev = hwif->pdev; + int err; + + port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); + if (!port_stats) + return -ENOMEM; + + stats_info.stats_version = HINIC_PORT_STATS_VERSION; + stats_info.stats_size = sizeof(*port_stats); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PORT_STATISTICS, + &stats_info, sizeof(stats_info), + port_stats, &out_size); + if (err || !out_size || port_stats->status) { + dev_err(&pdev->dev, + "Failed to get port statistics, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_stats->status, out_size); + err = -EINVAL; + goto out; + } + + memcpy(stats, &port_stats->stats, sizeof(*stats)); + +out: + kfree(port_stats); + + return err; +} + +int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_version_info up_ver = {0}; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u16 out_size; + int err; + + if (!hwdev) + return -EINVAL; + + hwif = hwdev->hwif; + pdev = hwif->pdev; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MGMT_VERSION, + &up_ver, sizeof(up_ver), &up_ver, + &out_size); + if (err || !out_size || up_ver.status) { + dev_err(&pdev->dev, + "Failed to get mgmt version, err: %d, status: 0x%x, out size: 0x%x\n", + err, up_ver.status, out_size); + return -EINVAL; + } + + snprintf(mgmt_ver, HINIC_MGMT_VERSION_MAX_LEN, "%s", up_ver.ver); + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index c562afd206be..44772fd47fc1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -13,6 +13,22 @@ #include "hinic_dev.h" +#define HINIC_RSS_KEY_SIZE 40 +#define HINIC_RSS_INDIR_SIZE 256 +#define HINIC_PORT_STATS_VERSION 0 +#define HINIC_FW_VERSION_NAME 16 +#define HINIC_COMPILE_TIME_LEN 20 +#define HINIC_MGMT_VERSION_MAX_LEN 32 + +struct hinic_version_info { + u8 status; + u8 version; + u8 rsvd[6]; + + u8 ver[HINIC_FW_VERSION_NAME]; + u8 time[HINIC_COMPILE_TIME_LEN]; +}; + enum hinic_rx_mode { HINIC_RX_MODE_UC = BIT(0), HINIC_RX_MODE_MC = BIT(1), @@ -183,6 +199,313 @@ struct hinic_checksum_offload { u16 rsvd1; u32 rx_csum_offload; }; + +struct hinic_rq_num { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1[33]; + u32 num_rqs; + u32 rq_depth; +}; + +struct hinic_lro_config { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + u8 lro_ipv4_en; + u8 lro_ipv6_en; + u8 lro_max_wqe_num; + u8 resv2[13]; +}; + +struct hinic_lro_timer { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 type; /* 0: set timer value, 1: get timer value */ + u8 enable; /* when set lro time, enable should be 1 */ + u16 rsvd1; + u32 timer; +}; + +struct hinic_vlan_cfg { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 vlan_rx_offload; + u8 rsvd1[5]; +}; + +struct hinic_rss_template_mgmt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 cmd; + u8 template_id; + u8 rsvd1[4]; +}; + +struct hinic_rss_template_key { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 template_id; + u8 rsvd1; + u8 key[HINIC_RSS_KEY_SIZE]; +}; + +struct hinic_rss_context_tbl { + u32 group_index; + u32 offset; + u32 size; + u32 rsvd; + u32 ctx; +}; + +struct hinic_rss_context_table { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 template_id; + u8 rsvd1; + u32 context; +}; + +struct hinic_rss_indirect_tbl { + u32 group_index; + u32 offset; + u32 size; + u32 rsvd; + u8 entry[HINIC_RSS_INDIR_SIZE]; +}; + +struct hinic_rss_indir_table { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 template_id; + u8 rsvd1; + u8 indir[HINIC_RSS_INDIR_SIZE]; +}; + +struct hinic_rss_key { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 template_id; + u8 rsvd1; + u8 key[HINIC_RSS_KEY_SIZE]; +}; + +struct hinic_rss_engine_type { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 template_id; + u8 hash_engine; + u8 rsvd1[4]; +}; + +struct hinic_rss_config { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 rss_en; + u8 template_id; + u8 rq_priority_number; + u8 rsvd1[11]; +}; + +struct hinic_stats { + char name[ETH_GSTRING_LEN]; + u32 size; + int offset; +}; + +struct hinic_vport_stats { + u64 tx_unicast_pkts_vport; + u64 tx_unicast_bytes_vport; + u64 tx_multicast_pkts_vport; + u64 tx_multicast_bytes_vport; + u64 tx_broadcast_pkts_vport; + u64 tx_broadcast_bytes_vport; + + u64 rx_unicast_pkts_vport; + u64 rx_unicast_bytes_vport; + u64 rx_multicast_pkts_vport; + u64 rx_multicast_bytes_vport; + u64 rx_broadcast_pkts_vport; + u64 rx_broadcast_bytes_vport; + + u64 tx_discard_vport; + u64 rx_discard_vport; + u64 tx_err_vport; + u64 rx_err_vport; +}; + +struct hinic_phy_port_stats { + u64 mac_rx_total_pkt_num; + u64 mac_rx_total_oct_num; + u64 mac_rx_bad_pkt_num; + u64 mac_rx_bad_oct_num; + u64 mac_rx_good_pkt_num; + u64 mac_rx_good_oct_num; + u64 mac_rx_uni_pkt_num; + u64 mac_rx_multi_pkt_num; + u64 mac_rx_broad_pkt_num; + + u64 mac_tx_total_pkt_num; + u64 mac_tx_total_oct_num; + u64 mac_tx_bad_pkt_num; + u64 mac_tx_bad_oct_num; + u64 mac_tx_good_pkt_num; + u64 mac_tx_good_oct_num; + u64 mac_tx_uni_pkt_num; + u64 mac_tx_multi_pkt_num; + u64 mac_tx_broad_pkt_num; + + u64 mac_rx_fragment_pkt_num; + u64 mac_rx_undersize_pkt_num; + u64 mac_rx_undermin_pkt_num; + u64 mac_rx_64_oct_pkt_num; + u64 mac_rx_65_127_oct_pkt_num; + u64 mac_rx_128_255_oct_pkt_num; + u64 mac_rx_256_511_oct_pkt_num; + u64 mac_rx_512_1023_oct_pkt_num; + u64 mac_rx_1024_1518_oct_pkt_num; + u64 mac_rx_1519_2047_oct_pkt_num; + u64 mac_rx_2048_4095_oct_pkt_num; + u64 mac_rx_4096_8191_oct_pkt_num; + u64 mac_rx_8192_9216_oct_pkt_num; + u64 mac_rx_9217_12287_oct_pkt_num; + u64 mac_rx_12288_16383_oct_pkt_num; + u64 mac_rx_1519_max_bad_pkt_num; + u64 mac_rx_1519_max_good_pkt_num; + u64 mac_rx_oversize_pkt_num; + u64 mac_rx_jabber_pkt_num; + + u64 mac_rx_pause_num; + u64 mac_rx_pfc_pkt_num; + u64 mac_rx_pfc_pri0_pkt_num; + u64 mac_rx_pfc_pri1_pkt_num; + u64 mac_rx_pfc_pri2_pkt_num; + u64 mac_rx_pfc_pri3_pkt_num; + u64 mac_rx_pfc_pri4_pkt_num; + u64 mac_rx_pfc_pri5_pkt_num; + u64 mac_rx_pfc_pri6_pkt_num; + u64 mac_rx_pfc_pri7_pkt_num; + u64 mac_rx_control_pkt_num; + u64 mac_rx_y1731_pkt_num; + u64 mac_rx_sym_err_pkt_num; + u64 mac_rx_fcs_err_pkt_num; + u64 mac_rx_send_app_good_pkt_num; + u64 mac_rx_send_app_bad_pkt_num; + + u64 mac_tx_fragment_pkt_num; + u64 mac_tx_undersize_pkt_num; + u64 mac_tx_undermin_pkt_num; + u64 mac_tx_64_oct_pkt_num; + u64 mac_tx_65_127_oct_pkt_num; + u64 mac_tx_128_255_oct_pkt_num; + u64 mac_tx_256_511_oct_pkt_num; + u64 mac_tx_512_1023_oct_pkt_num; + u64 mac_tx_1024_1518_oct_pkt_num; + u64 mac_tx_1519_2047_oct_pkt_num; + u64 mac_tx_2048_4095_oct_pkt_num; + u64 mac_tx_4096_8191_oct_pkt_num; + u64 mac_tx_8192_9216_oct_pkt_num; + u64 mac_tx_9217_12287_oct_pkt_num; + u64 mac_tx_12288_16383_oct_pkt_num; + u64 mac_tx_1519_max_bad_pkt_num; + u64 mac_tx_1519_max_good_pkt_num; + u64 mac_tx_oversize_pkt_num; + u64 mac_tx_jabber_pkt_num; + + u64 mac_tx_pause_num; + u64 mac_tx_pfc_pkt_num; + u64 mac_tx_pfc_pri0_pkt_num; + u64 mac_tx_pfc_pri1_pkt_num; + u64 mac_tx_pfc_pri2_pkt_num; + u64 mac_tx_pfc_pri3_pkt_num; + u64 mac_tx_pfc_pri4_pkt_num; + u64 mac_tx_pfc_pri5_pkt_num; + u64 mac_tx_pfc_pri6_pkt_num; + u64 mac_tx_pfc_pri7_pkt_num; + u64 mac_tx_control_pkt_num; + u64 mac_tx_y1731_pkt_num; + u64 mac_tx_1588_pkt_num; + u64 mac_tx_err_all_pkt_num; + u64 mac_tx_from_app_good_pkt_num; + u64 mac_tx_from_app_bad_pkt_num; + + u64 mac_rx_higig2_ext_pkt_num; + u64 mac_rx_higig2_message_pkt_num; + u64 mac_rx_higig2_error_pkt_num; + u64 mac_rx_higig2_cpu_ctrl_pkt_num; + u64 mac_rx_higig2_unicast_pkt_num; + u64 mac_rx_higig2_broadcast_pkt_num; + u64 mac_rx_higig2_l2_multicast_pkt_num; + u64 mac_rx_higig2_l3_multicast_pkt_num; + + u64 mac_tx_higig2_message_pkt_num; + u64 mac_tx_higig2_ext_pkt_num; + u64 mac_tx_higig2_cpu_ctrl_pkt_num; + u64 mac_tx_higig2_unicast_pkt_num; + u64 mac_tx_higig2_broadcast_pkt_num; + u64 mac_tx_higig2_l2_multicast_pkt_num; + u64 mac_tx_higig2_l3_multicast_pkt_num; +}; + +struct hinic_port_stats_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + u32 stats_version; + u32 stats_size; +}; + +struct hinic_port_stats { + u8 status; + u8 version; + u8 rsvd[6]; + + struct hinic_phy_port_stats stats; +}; + +struct hinic_cmd_vport_stats { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct hinic_vport_stats stats; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -211,7 +534,55 @@ int hinic_port_set_func_state(struct hinic_dev *nic_dev, int hinic_port_get_cap(struct hinic_dev *nic_dev, struct hinic_port_cap *port_cap); +int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs); + int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state); int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en); + +int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en, + u32 lro_timer, u32 wqe_num); + +int hinic_set_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, + struct hinic_rss_type rss_type); + +int hinic_rss_set_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + const u32 *indir_table); + +int hinic_rss_set_template_tbl(struct hinic_dev *nic_dev, u32 template_id, + const u8 *temp); + +int hinic_rss_set_hash_engine(struct hinic_dev *nic_dev, u8 template_id, + u8 type); + +int hinic_rss_cfg(struct hinic_dev *nic_dev, u8 rss_en, u8 template_id); + +int hinic_rss_template_alloc(struct hinic_dev *nic_dev, u8 *tmpl_idx); + +int hinic_rss_template_free(struct hinic_dev *nic_dev, u8 tmpl_idx); + +void hinic_set_ethtool_ops(struct net_device *netdev); + +int hinic_get_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, + struct hinic_rss_type *rss_type); + +int hinic_rss_get_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + u32 *indir_table); + +int hinic_rss_get_template_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, + u8 *temp); + +int hinic_rss_get_hash_engine(struct hinic_dev *nic_dev, u8 tmpl_idx, + u8 *type); + +int hinic_get_phy_port_stats(struct hinic_dev *nic_dev, + struct hinic_phy_port_stats *stats); + +int hinic_get_vport_stats(struct hinic_dev *nic_dev, + struct hinic_vport_stats *stats); + +int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en); + +int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index 0850ea83d6c1..56ea6d692f1c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -18,6 +18,7 @@ #include <linux/dma-mapping.h> #include <linux/prefetch.h> #include <linux/cpumask.h> +#include <linux/if_vlan.h> #include <asm/barrier.h> #include "hinic_common.h" @@ -36,6 +37,15 @@ #define RX_IRQ_NO_RESEND_TIMER 0 #define HINIC_RX_BUFFER_WRITE 16 +#define HINIC_RX_IPV6_PKT 7 +#define LRO_PKT_HDR_LEN_IPV4 66 +#define LRO_PKT_HDR_LEN_IPV6 86 +#define LRO_REPLENISH_THLD 256 + +#define LRO_PKT_HDR_LEN(cqe) \ + (HINIC_GET_RX_PKT_TYPE(be32_to_cpu((cqe)->offload_type)) == \ + HINIC_RX_IPV6_PKT ? LRO_PKT_HDR_LEN_IPV6 : LRO_PKT_HDR_LEN_IPV4) + /** * hinic_rxq_clean_stats - Clean the statistics of specific queue * @rxq: Logical Rx Queue @@ -47,6 +57,9 @@ void hinic_rxq_clean_stats(struct hinic_rxq *rxq) u64_stats_update_begin(&rxq_stats->syncp); rxq_stats->pkts = 0; rxq_stats->bytes = 0; + rxq_stats->errors = 0; + rxq_stats->csum_errors = 0; + rxq_stats->other_errors = 0; u64_stats_update_end(&rxq_stats->syncp); } @@ -65,6 +78,10 @@ void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) start = u64_stats_fetch_begin(&rxq_stats->syncp); stats->pkts = rxq_stats->pkts; stats->bytes = rxq_stats->bytes; + stats->errors = rxq_stats->csum_errors + + rxq_stats->other_errors; + stats->csum_errors = rxq_stats->csum_errors; + stats->other_errors = rxq_stats->other_errors; } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); u64_stats_update_end(&stats->syncp); } @@ -81,27 +98,25 @@ static void rxq_stats_init(struct hinic_rxq *rxq) hinic_rxq_clean_stats(rxq); } -static void rx_csum(struct hinic_rxq *rxq, u16 cons_idx, +static void rx_csum(struct hinic_rxq *rxq, u32 status, struct sk_buff *skb) { struct net_device *netdev = rxq->netdev; - struct hinic_rq_cqe *cqe; - struct hinic_rq *rq; u32 csum_err; - u32 status; - rq = rxq->rq; - cqe = rq->cqe[cons_idx]; - status = be32_to_cpu(cqe->status); csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR); if (!(netdev->features & NETIF_F_RXCSUM)) return; - if (!csum_err) + if (!csum_err) { skb->ip_summed = CHECKSUM_UNNECESSARY; - else + } else { + if (!(csum_err & (HINIC_RX_CSUM_HW_CHECK_NONE | + HINIC_RX_CSUM_IPSU_OTHER_ERR))) + rxq->rxq_stats.csum_errors++; skb->ip_summed = CHECKSUM_NONE; + } } /** * rx_alloc_skb - allocate skb and map it to dma address @@ -311,13 +326,21 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb, static int rxq_recv(struct hinic_rxq *rxq, int budget) { struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq); + struct net_device *netdev = rxq->netdev; u64 pkt_len = 0, rx_bytes = 0; + struct hinic_rq *rq = rxq->rq; struct hinic_rq_wqe *rq_wqe; unsigned int free_wqebbs; + struct hinic_rq_cqe *cqe; int num_wqes, pkts = 0; struct hinic_sge sge; + unsigned int status; struct sk_buff *skb; - u16 ci; + u32 offload_type; + u16 ci, num_lro; + u16 num_wqe = 0; + u32 vlan_len; + u16 vid; while (pkts < budget) { num_wqes = 0; @@ -327,11 +350,13 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) if (!rq_wqe) break; + cqe = rq->cqe[ci]; + status = be32_to_cpu(cqe->status); hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); - rx_csum(rxq, ci, skb); + rx_csum(rxq, status, skb); prefetch(skb->data); @@ -345,9 +370,17 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) HINIC_RX_BUF_SZ, ci); } - hinic_rq_put_wqe(rxq->rq, ci, + hinic_rq_put_wqe(rq, ci, (num_wqes + 1) * HINIC_RQ_WQE_SIZE); + offload_type = be32_to_cpu(cqe->offload_type); + vlan_len = be32_to_cpu(cqe->len); + if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)) { + vid = HINIC_GET_RX_VLAN_TAG(vlan_len); + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); + } + skb_record_rx_queue(skb, qp->q_id); skb->protocol = eth_type_trans(skb, rxq->netdev); @@ -355,6 +388,21 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) pkts++; rx_bytes += pkt_len; + + num_lro = HINIC_GET_RX_NUM_LRO(status); + if (num_lro) { + rx_bytes += ((num_lro - 1) * + LRO_PKT_HDR_LEN(cqe)); + + num_wqe += + (u16)(pkt_len >> rxq->rx_buff_shift) + + ((pkt_len & (rxq->buf_len - 1)) ? 1 : 0); + } + + cqe->status = 0; + + if (num_wqe >= LRO_REPLENISH_THLD) + break; } free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq); @@ -469,20 +517,20 @@ int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, struct net_device *netdev) { struct hinic_qp *qp = container_of(rq, struct hinic_qp, rq); - int err, pkts, irqname_len; + int err, pkts; rxq->netdev = netdev; rxq->rq = rq; + rxq->buf_len = HINIC_RX_BUF_SZ; + rxq->rx_buff_shift = ilog2(HINIC_RX_BUF_SZ); rxq_stats_init(rxq); - irqname_len = snprintf(NULL, 0, "hinic_rxq%d", qp->q_id) + 1; - rxq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); + rxq->irq_name = devm_kasprintf(&netdev->dev, GFP_KERNEL, + "hinic_rxq%d", qp->q_id); if (!rxq->irq_name) return -ENOMEM; - sprintf(rxq->irq_name, "hinic_rxq%d", qp->q_id); - pkts = rx_alloc_pkts(rxq); if (!pkts) { err = -ENOMEM; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h index bc797498a87f..507dcbae9085 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h @@ -21,7 +21,10 @@ struct hinic_rxq_stats { u64 pkts; u64 bytes; - + u64 errors; + u64 csum_errors; + u64 other_errors; + u64 alloc_skb_err; struct u64_stats_sync syncp; }; @@ -32,6 +35,8 @@ struct hinic_rxq { struct hinic_rxq_stats rxq_stats; char *irq_name; + u16 buf_len; + u32 rx_buff_shift; struct napi_struct napi; }; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index b9fd8d720349..9c78251f9c39 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -83,6 +83,7 @@ void hinic_txq_clean_stats(struct hinic_txq *txq) txq_stats->tx_busy = 0; txq_stats->tx_wake = 0; txq_stats->tx_dropped = 0; + txq_stats->big_frags_pkts = 0; u64_stats_update_end(&txq_stats->syncp); } @@ -104,6 +105,7 @@ void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) stats->tx_busy = txq_stats->tx_busy; stats->tx_wake = txq_stats->tx_wake; stats->tx_dropped = txq_stats->tx_dropped; + stats->big_frags_pkts = txq_stats->big_frags_pkts; } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); u64_stats_update_end(&stats->syncp); } @@ -405,10 +407,20 @@ static int offload_csum(struct hinic_sq_task *task, u32 *queue_info, return 1; } +static void offload_vlan(struct hinic_sq_task *task, u32 *queue_info, + u16 vlan_tag, u16 vlan_pri) +{ + task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(vlan_tag, VLAN_TAG) | + HINIC_SQ_TASK_INFO0_SET(1U, VLAN_OFFLOAD); + + *queue_info |= HINIC_SQ_CTRL_SET(vlan_pri, QUEUE_INFO_PRI); +} + static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task, u32 *queue_info) { enum hinic_offload_type offload = 0; + u16 vlan_tag; int enabled; enabled = offload_tso(task, queue_info, skb); @@ -422,6 +434,13 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task, return -EPROTONOSUPPORT; } + if (unlikely(skb_vlan_tag_present(skb))) { + vlan_tag = skb_vlan_tag_get(skb); + offload_vlan(task, queue_info, vlan_tag, + vlan_tag >> VLAN_PRIO_SHIFT); + offload |= TX_OFFLOAD_VLAN; + } + if (offload) hinic_task_set_l2hdr(task, skb_network_offset(skb)); @@ -464,6 +483,12 @@ netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) } nr_sges = skb_shinfo(skb)->nr_frags + 1; + if (nr_sges > 17) { + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.big_frags_pkts++; + u64_stats_update_end(&txq->txq_stats.syncp); + } + if (nr_sges > txq->max_sges) { netdev_err(netdev, "Too many Tx sges\n"); goto skb_error; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h index ca5f537fc383..f158b7db7fb8 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -21,6 +21,7 @@ struct hinic_txq_stats { u64 tx_busy; u64 tx_wake; u64 tx_dropped; + u64 big_frags_pkts; struct u64_stats_sync syncp; }; |