diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-06 06:13:21 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-06 06:13:21 +0300 |
commit | 47ec5303d73ea344e84f46660fff693c57641386 (patch) | |
tree | a2252debab749de29620c43285295d60c4741119 /drivers/net/ethernet/huawei | |
parent | 8186749621ed6b8fc42644c399e8c755a2b6f630 (diff) | |
parent | c1055b76ad00aed0e8b79417080f212d736246b6 (diff) | |
download | linux-47ec5303d73ea344e84f46660fff693c57641386.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller:
1) Support 6Ghz band in ath11k driver, from Rajkumar Manoharan.
2) Support UDP segmentation in code TSO code, from Eric Dumazet.
3) Allow flashing different flash images in cxgb4 driver, from Vishal
Kulkarni.
4) Add drop frames counter and flow status to tc flower offloading,
from Po Liu.
5) Support n-tuple filters in cxgb4, from Vishal Kulkarni.
6) Various new indirect call avoidance, from Eric Dumazet and Brian
Vazquez.
7) Fix BPF verifier failures on 32-bit pointer arithmetic, from
Yonghong Song.
8) Support querying and setting hardware address of a port function via
devlink, use this in mlx5, from Parav Pandit.
9) Support hw ipsec offload on bonding slaves, from Jarod Wilson.
10) Switch qca8k driver over to phylink, from Jonathan McDowell.
11) In bpftool, show list of processes holding BPF FD references to
maps, programs, links, and btf objects. From Andrii Nakryiko.
12) Several conversions over to generic power management, from Vaibhav
Gupta.
13) Add support for SO_KEEPALIVE et al. to bpf_setsockopt(), from Dmitry
Yakunin.
14) Various https url conversions, from Alexander A. Klimov.
15) Timestamping and PHC support for mscc PHY driver, from Antoine
Tenart.
16) Support bpf iterating over tcp and udp sockets, from Yonghong Song.
17) Support 5GBASE-T i40e NICs, from Aleksandr Loktionov.
18) Add kTLS RX HW offload support to mlx5e, from Tariq Toukan.
19) Fix the ->ndo_start_xmit() return type to be netdev_tx_t in several
drivers. From Luc Van Oostenryck.
20) XDP support for xen-netfront, from Denis Kirjanov.
21) Support receive buffer autotuning in MPTCP, from Florian Westphal.
22) Support EF100 chip in sfc driver, from Edward Cree.
23) Add XDP support to mvpp2 driver, from Matteo Croce.
24) Support MPTCP in sock_diag, from Paolo Abeni.
25) Commonize UDP tunnel offloading code by creating udp_tunnel_nic
infrastructure, from Jakub Kicinski.
26) Several pci_ --> dma_ API conversions, from Christophe JAILLET.
27) Add FLOW_ACTION_POLICE support to mlxsw, from Ido Schimmel.
28) Add SK_LOOKUP bpf program type, from Jakub Sitnicki.
29) Refactor a lot of networking socket option handling code in order to
avoid set_fs() calls, from Christoph Hellwig.
30) Add rfc4884 support to icmp code, from Willem de Bruijn.
31) Support TBF offload in dpaa2-eth driver, from Ioana Ciornei.
32) Support XDP_REDIRECT in qede driver, from Alexander Lobakin.
33) Support PCI relaxed ordering in mlx5 driver, from Aya Levin.
34) Support TCP syncookies in MPTCP, from Flowian Westphal.
35) Fix several tricky cases of PMTU handling wrt. briding, from Stefano
Brivio.
* git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2056 commits)
net: thunderx: initialize VF's mailbox mutex before first usage
usb: hso: remove bogus check for EINPROGRESS
usb: hso: no complaint about kmalloc failure
hso: fix bailout in error case of probe
ip_tunnel_core: Fix build for archs without _HAVE_ARCH_IPV6_CSUM
selftests/net: relax cpu affinity requirement in msg_zerocopy test
mptcp: be careful on subflow creation
selftests: rtnetlink: make kci_test_encap() return sub-test result
selftests: rtnetlink: correct the final return value for the test
net: dsa: sja1105: use detected device id instead of DT one on mismatch
tipc: set ub->ifindex for local ipv6 address
ipv6: add ipv6_dev_find()
net: openvswitch: silence suspicious RCU usage warning
Revert "vxlan: fix tos value before xmit"
ptp: only allow phase values lower than 1 period
farsync: switch from 'pci_' to 'dma_' API
wan: wanxl: switch from 'pci_' to 'dma_' API
hv_netvsc: do not use VF device if link is down
dpaa2-eth: Fix passing zero to 'PTR_ERR' warning
net: macb: Properly handle phylink on at91sam9x
...
Diffstat (limited to 'drivers/net/ethernet/huawei')
27 files changed, 3073 insertions, 80 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile index 32a011ca44c3..67b59d0ba769 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_ethtool.o hinic_hw_mbox.o hinic_sriov.o + hinic_common.o hinic_ethtool.o hinic_devlink.o hinic_hw_mbox.o hinic_sriov.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index 48b40be3e84d..0a1e20edf7cf 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -20,11 +20,14 @@ #define HINIC_DRV_NAME "hinic" +#define LP_PKT_CNT 64 + enum hinic_flags { HINIC_LINK_UP = BIT(0), HINIC_INTF_UP = BIT(1), HINIC_RSS_ENABLE = BIT(2), HINIC_LINK_DOWN = BIT(3), + HINIC_LP_TEST = BIT(4), }; struct hinic_rx_mode_work { @@ -49,6 +52,12 @@ enum hinic_rss_hash_type { HINIC_RSS_HASH_ENGINE_TYPE_MAX, }; +struct hinic_intr_coal_info { + u8 pending_limt; + u8 coalesce_timer_cfg; + u8 resend_timer_cfg; +}; + struct hinic_dev { struct net_device *netdev; struct hinic_hwdev *hwdev; @@ -82,7 +91,21 @@ struct hinic_dev { struct hinic_rss_type rss_type; u8 *rss_hkey_user; s32 *rss_indir_user; + struct hinic_intr_coal_info *rx_intr_coalesce; + struct hinic_intr_coal_info *tx_intr_coalesce; struct hinic_sriov_info sriov_info; + int lb_test_rx_idx; + int lb_pkt_len; + u8 *lb_test_rx_buf; + struct devlink *devlink; + bool cable_unplugged; + bool module_unrecognized; +}; + +struct hinic_devlink_priv { + struct hinic_hwdev *hwdev; + struct devlink_health_reporter *hw_fault_reporter; + struct devlink_health_reporter *fw_fault_reporter; }; #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.c b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c new file mode 100644 index 000000000000..c6adc776f3c8 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.c @@ -0,0 +1,608 @@ +// 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/netlink.h> +#include <net/devlink.h> +#include <linux/firmware.h> + +#include "hinic_port.h" +#include "hinic_devlink.h" +#include "hinic_hw_dev.h" + +static bool check_image_valid(struct hinic_devlink_priv *priv, const u8 *buf, + u32 image_size, struct host_image_st *host_image) +{ + struct fw_image_st *fw_image = NULL; + u32 len = 0; + u32 i; + + fw_image = (struct fw_image_st *)buf; + + if (fw_image->fw_magic != HINIC_MAGIC_NUM) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_magic read from file, fw_magic: 0x%x\n", + fw_image->fw_magic); + return false; + } + + if (fw_image->fw_info.fw_section_cnt > MAX_FW_TYPE_NUM) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong fw_type_num read from file, fw_type_num: 0x%x\n", + fw_image->fw_info.fw_section_cnt); + return false; + } + + for (i = 0; i < fw_image->fw_info.fw_section_cnt; i++) { + len += fw_image->fw_section_info[i].fw_section_len; + memcpy(&host_image->image_section_info[i], + &fw_image->fw_section_info[i], + sizeof(struct fw_section_info_st)); + } + + if (len != fw_image->fw_len || + (fw_image->fw_len + UPDATEFW_IMAGE_HEAD_SIZE) != image_size) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Wrong data size read from file\n"); + return false; + } + + host_image->image_info.up_total_len = fw_image->fw_len; + host_image->image_info.fw_version = fw_image->fw_version; + host_image->section_type_num = fw_image->fw_info.fw_section_cnt; + host_image->device_id = fw_image->device_id; + + return true; +} + +static bool check_image_integrity(struct hinic_devlink_priv *priv, + struct host_image_st *host_image, + u32 update_type) +{ + u32 collect_section_type = 0; + u32 i, type; + + for (i = 0; i < host_image->section_type_num; i++) { + type = host_image->image_section_info[i].fw_section_type; + if (collect_section_type & (1U << type)) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Duplicate section type: %u\n", + type); + return false; + } + collect_section_type |= (1U << type); + } + + if (update_type == FW_UPDATE_COLD && + (((collect_section_type & _IMAGE_COLD_SUB_MODULES_MUST_IN) == + _IMAGE_COLD_SUB_MODULES_MUST_IN) || + collect_section_type == _IMAGE_CFG_SUB_MODULES_MUST_IN)) + return true; + + if (update_type == FW_UPDATE_HOT && + (collect_section_type & _IMAGE_HOT_SUB_MODULES_MUST_IN) == + _IMAGE_HOT_SUB_MODULES_MUST_IN) + return true; + + if (update_type == FW_UPDATE_COLD) + dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid: 0x%x or 0x%lx, current: 0x%x\n", + _IMAGE_COLD_SUB_MODULES_MUST_IN, + _IMAGE_CFG_SUB_MODULES_MUST_IN, collect_section_type); + else + dev_err(&priv->hwdev->hwif->pdev->dev, "Check file integrity failed, valid:0x%x, current: 0x%x\n", + _IMAGE_HOT_SUB_MODULES_MUST_IN, collect_section_type); + + return false; +} + +static int check_image_device_type(struct hinic_devlink_priv *priv, + u32 image_device_type) +{ + struct hinic_comm_board_info board_info = {0}; + + if (hinic_get_board_info(priv->hwdev, &board_info)) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Get board info failed\n"); + return false; + } + + if (image_device_type == board_info.info.board_type) + return true; + + dev_err(&priv->hwdev->hwif->pdev->dev, "The device type of upgrade file doesn't match the device type of current firmware, please check the upgrade file\n"); + dev_err(&priv->hwdev->hwif->pdev->dev, "The image device type: 0x%x, firmware device type: 0x%x\n", + image_device_type, board_info.info.board_type); + + return false; +} + +static int hinic_flash_fw(struct hinic_devlink_priv *priv, const u8 *data, + struct host_image_st *host_image) +{ + u32 section_remain_send_len, send_fragment_len, send_pos, up_total_len; + struct hinic_cmd_update_fw *fw_update_msg = NULL; + u32 section_type, section_crc, section_version; + u32 i, len, section_len, section_offset; + u16 out_size = sizeof(*fw_update_msg); + int total_len_flag = 0; + int err; + + fw_update_msg = kzalloc(sizeof(*fw_update_msg), GFP_KERNEL); + if (!fw_update_msg) + return -ENOMEM; + + up_total_len = host_image->image_info.up_total_len; + + for (i = 0; i < host_image->section_type_num; i++) { + len = host_image->image_section_info[i].fw_section_len; + if (host_image->image_section_info[i].fw_section_type == + UP_FW_UPDATE_BOOT) { + up_total_len = up_total_len - len; + break; + } + } + + for (i = 0; i < host_image->section_type_num; i++) { + section_len = + host_image->image_section_info[i].fw_section_len; + section_offset = + host_image->image_section_info[i].fw_section_offset; + section_remain_send_len = section_len; + section_type = + host_image->image_section_info[i].fw_section_type; + section_crc = host_image->image_section_info[i].fw_section_crc; + section_version = + host_image->image_section_info[i].fw_section_version; + + if (section_type == UP_FW_UPDATE_BOOT) + continue; + + send_fragment_len = 0; + send_pos = 0; + + while (section_remain_send_len > 0) { + if (!total_len_flag) { + fw_update_msg->total_len = up_total_len; + total_len_flag = 1; + } else { + fw_update_msg->total_len = 0; + } + + memset(fw_update_msg->data, 0, MAX_FW_FRAGMENT_LEN); + + fw_update_msg->ctl_info.SF = + (section_remain_send_len == section_len) ? + true : false; + fw_update_msg->section_info.FW_section_CRC = section_crc; + fw_update_msg->fw_section_version = section_version; + fw_update_msg->ctl_info.flag = UP_TYPE_A; + + if (section_type <= UP_FW_UPDATE_UP_DATA_B) { + fw_update_msg->section_info.FW_section_type = + (section_type % 2) ? + UP_FW_UPDATE_UP_DATA : + UP_FW_UPDATE_UP_TEXT; + + fw_update_msg->ctl_info.flag = UP_TYPE_B; + if (section_type <= UP_FW_UPDATE_UP_DATA_A) + fw_update_msg->ctl_info.flag = UP_TYPE_A; + } else { + fw_update_msg->section_info.FW_section_type = + section_type - 0x2; + } + + fw_update_msg->setion_total_len = section_len; + fw_update_msg->section_offset = send_pos; + + if (section_remain_send_len <= MAX_FW_FRAGMENT_LEN) { + fw_update_msg->ctl_info.SL = true; + fw_update_msg->ctl_info.fragment_len = + section_remain_send_len; + send_fragment_len += section_remain_send_len; + } else { + fw_update_msg->ctl_info.SL = false; + fw_update_msg->ctl_info.fragment_len = + MAX_FW_FRAGMENT_LEN; + send_fragment_len += MAX_FW_FRAGMENT_LEN; + } + + memcpy(fw_update_msg->data, + data + UPDATEFW_IMAGE_HEAD_SIZE + + section_offset + send_pos, + fw_update_msg->ctl_info.fragment_len); + + err = hinic_port_msg_cmd(priv->hwdev, + HINIC_PORT_CMD_UPDATE_FW, + fw_update_msg, + sizeof(*fw_update_msg), + fw_update_msg, &out_size); + if (err || !out_size || fw_update_msg->status) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Failed to update firmware, err: %d, status: 0x%x, out size: 0x%x\n", + err, fw_update_msg->status, out_size); + err = fw_update_msg->status ? + fw_update_msg->status : -EIO; + kfree(fw_update_msg); + return err; + } + + send_pos = send_fragment_len; + section_remain_send_len = section_len - + send_fragment_len; + } + } + + kfree(fw_update_msg); + + return 0; +} + +static int hinic_firmware_update(struct hinic_devlink_priv *priv, + const struct firmware *fw, + struct netlink_ext_ack *extack) +{ + struct host_image_st host_image; + int err; + + memset(&host_image, 0, sizeof(struct host_image_st)); + + if (!check_image_valid(priv, fw->data, fw->size, &host_image) || + !check_image_integrity(priv, &host_image, FW_UPDATE_COLD) || + !check_image_device_type(priv, host_image.device_id)) { + NL_SET_ERR_MSG_MOD(extack, "Check image failed"); + return -EINVAL; + } + + dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware begin\n"); + + err = hinic_flash_fw(priv, fw->data, &host_image); + if (err) { + if (err == HINIC_FW_DISMATCH_ERROR) { + dev_err(&priv->hwdev->hwif->pdev->dev, "Firmware image doesn't match this card, please use newer image, err: %d\n", + err); + NL_SET_ERR_MSG_MOD(extack, + "Firmware image doesn't match this card, please use newer image"); + } else { + dev_err(&priv->hwdev->hwif->pdev->dev, "Send firmware image data failed, err: %d\n", + err); + NL_SET_ERR_MSG_MOD(extack, "Send firmware image data failed"); + } + + return err; + } + + dev_info(&priv->hwdev->hwif->pdev->dev, "Flash firmware end\n"); + + return 0; +} + +static int hinic_devlink_flash_update(struct devlink *devlink, + const char *file_name, + const char *component, + struct netlink_ext_ack *extack) +{ + struct hinic_devlink_priv *priv = devlink_priv(devlink); + const struct firmware *fw; + int err; + + if (component) + return -EOPNOTSUPP; + + err = request_firmware_direct(&fw, file_name, + &priv->hwdev->hwif->pdev->dev); + if (err) + return err; + + err = hinic_firmware_update(priv, fw, extack); + release_firmware(fw); + + return err; +} + +static const struct devlink_ops hinic_devlink_ops = { + .flash_update = hinic_devlink_flash_update, +}; + +struct devlink *hinic_devlink_alloc(void) +{ + return devlink_alloc(&hinic_devlink_ops, sizeof(struct hinic_dev)); +} + +void hinic_devlink_free(struct devlink *devlink) +{ + devlink_free(devlink); +} + +int hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev) +{ + struct devlink *devlink = priv_to_devlink(priv); + + return devlink_register(devlink, dev); +} + +void hinic_devlink_unregister(struct hinic_devlink_priv *priv) +{ + struct devlink *devlink = priv_to_devlink(priv); + + devlink_unregister(devlink); +} + +static int chip_fault_show(struct devlink_fmsg *fmsg, + struct hinic_fault_event *event) +{ + char fault_level[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { + "fatal", "reset", "flr", "general", "suggestion"}; + char level_str[FAULT_SHOW_STR_LEN + 1] = {0}; + u8 level; + int err; + + level = event->event.chip.err_level; + if (level < FAULT_LEVEL_MAX) + strncpy(level_str, fault_level[level], strlen(fault_level[level])); + else + strncpy(level_str, "Unknown", strlen("Unknown")); + + if (level == FAULT_LEVEL_SERIOUS_FLR) { + err = devlink_fmsg_u32_pair_put(fmsg, "Function level err func_id", + (u32)event->event.chip.func_id); + if (err) + return err; + } + + err = devlink_fmsg_u8_pair_put(fmsg, "module_id", event->event.chip.node_id); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_type", (u32)event->event.chip.err_type); + if (err) + return err; + + err = devlink_fmsg_string_pair_put(fmsg, "err_level", level_str); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_addr", + event->event.chip.err_csr_addr); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_value", + event->event.chip.err_csr_value); + if (err) + return err; + + return 0; +} + +static int fault_report_show(struct devlink_fmsg *fmsg, + struct hinic_fault_event *event) +{ + char fault_type[FAULT_TYPE_MAX][FAULT_SHOW_STR_LEN + 1] = { + "chip", "ucode", "mem rd timeout", "mem wr timeout", + "reg rd timeout", "reg wr timeout", "phy fault"}; + char type_str[FAULT_SHOW_STR_LEN + 1] = {0}; + int err; + + if (event->type < FAULT_TYPE_MAX) + strncpy(type_str, fault_type[event->type], strlen(fault_type[event->type])); + else + strncpy(type_str, "Unknown", strlen("Unknown")); + + err = devlink_fmsg_string_pair_put(fmsg, "Fault type", type_str); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Fault raw data", + event->event.val, sizeof(event->event.val)); + if (err) + return err; + + switch (event->type) { + case FAULT_TYPE_CHIP: + err = chip_fault_show(fmsg, event); + if (err) + return err; + break; + case FAULT_TYPE_UCODE: + err = devlink_fmsg_u8_pair_put(fmsg, "Cause_id", event->event.ucode.cause_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "core_id", event->event.ucode.core_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "c_id", event->event.ucode.c_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "epc", event->event.ucode.epc); + if (err) + return err; + break; + case FAULT_TYPE_MEM_RD_TIMEOUT: + case FAULT_TYPE_MEM_WR_TIMEOUT: + err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr_ctrl", + event->event.mem_timeout.err_csr_ctrl); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "err_csr_data", + event->event.mem_timeout.err_csr_data); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "ctrl_tab", + event->event.mem_timeout.ctrl_tab); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "mem_index", + event->event.mem_timeout.mem_index); + if (err) + return err; + break; + case FAULT_TYPE_REG_RD_TIMEOUT: + case FAULT_TYPE_REG_WR_TIMEOUT: + err = devlink_fmsg_u32_pair_put(fmsg, "Err_csr", event->event.reg_timeout.err_csr); + if (err) + return err; + break; + case FAULT_TYPE_PHY_FAULT: + err = devlink_fmsg_u8_pair_put(fmsg, "Op_type", event->event.phy_fault.op_type); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "port_id", event->event.phy_fault.port_id); + if (err) + return err; + err = devlink_fmsg_u8_pair_put(fmsg, "dev_ad", event->event.phy_fault.dev_ad); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "csr_addr", event->event.phy_fault.csr_addr); + if (err) + return err; + err = devlink_fmsg_u32_pair_put(fmsg, "op_data", event->event.phy_fault.op_data); + if (err) + return err; + break; + default: + break; + } + + return 0; +} + +static int hinic_hw_reporter_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + if (priv_ctx) + return fault_report_show(fmsg, priv_ctx); + + return 0; +} + +static int mgmt_watchdog_report_show(struct devlink_fmsg *fmsg, + struct hinic_mgmt_watchdog_info *watchdog_info) +{ + int err; + + err = devlink_fmsg_u32_pair_put(fmsg, "Mgmt deadloop time_h", watchdog_info->curr_time_h); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "time_l", watchdog_info->curr_time_l); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "task_id", watchdog_info->task_id); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "sp", watchdog_info->sp); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_current_used", watchdog_info->curr_used); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "peak_used", watchdog_info->peak_used); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "\n Overflow_flag", watchdog_info->is_overflow); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_top", watchdog_info->stack_top); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "stack_bottom", watchdog_info->stack_bottom); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "mgmt_pc", watchdog_info->pc); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "lr", watchdog_info->lr); + if (err) + return err; + + err = devlink_fmsg_u32_pair_put(fmsg, "cpsr", watchdog_info->cpsr); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt register info", + watchdog_info->reg, sizeof(watchdog_info->reg)); + if (err) + return err; + + err = devlink_fmsg_binary_pair_put(fmsg, "Mgmt dump stack(start from sp)", + watchdog_info->data, sizeof(watchdog_info->data)); + if (err) + return err; + + return 0; +} + +static int hinic_fw_reporter_dump(struct devlink_health_reporter *reporter, + struct devlink_fmsg *fmsg, void *priv_ctx, + struct netlink_ext_ack *extack) +{ + if (priv_ctx) + return mgmt_watchdog_report_show(fmsg, priv_ctx); + + return 0; +} + +static const struct devlink_health_reporter_ops hinic_hw_fault_reporter_ops = { + .name = "hw", + .dump = hinic_hw_reporter_dump, +}; + +static const struct devlink_health_reporter_ops hinic_fw_fault_reporter_ops = { + .name = "fw", + .dump = hinic_fw_reporter_dump, +}; + +int hinic_health_reporters_create(struct hinic_devlink_priv *priv) +{ + struct devlink *devlink = priv_to_devlink(priv); + + priv->hw_fault_reporter = + devlink_health_reporter_create(devlink, &hinic_hw_fault_reporter_ops, + 0, priv); + if (IS_ERR(priv->hw_fault_reporter)) { + dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create hw fault reporter, err: %ld\n", + PTR_ERR(priv->hw_fault_reporter)); + return PTR_ERR(priv->hw_fault_reporter); + } + + priv->fw_fault_reporter = + devlink_health_reporter_create(devlink, &hinic_fw_fault_reporter_ops, + 0, priv); + if (IS_ERR(priv->fw_fault_reporter)) { + dev_warn(&priv->hwdev->hwif->pdev->dev, "Failed to create fw fault reporter, err: %ld\n", + PTR_ERR(priv->fw_fault_reporter)); + devlink_health_reporter_destroy(priv->hw_fault_reporter); + priv->hw_fault_reporter = NULL; + return PTR_ERR(priv->fw_fault_reporter); + } + + return 0; +} + +void hinic_health_reporters_destroy(struct hinic_devlink_priv *priv) +{ + if (!IS_ERR_OR_NULL(priv->fw_fault_reporter)) { + devlink_health_reporter_destroy(priv->fw_fault_reporter); + priv->fw_fault_reporter = NULL; + } + + if (!IS_ERR_OR_NULL(priv->hw_fault_reporter)) { + devlink_health_reporter_destroy(priv->hw_fault_reporter); + priv->hw_fault_reporter = NULL; + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_devlink.h b/drivers/net/ethernet/huawei/hinic/hinic_devlink.h new file mode 100644 index 000000000000..a090ebcfaabb --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_devlink.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + */ + +#ifndef __HINIC_DEVLINK_H__ +#define __HINIC_DEVLINK_H__ + +#include <net/devlink.h> +#include "hinic_dev.h" + +#define MAX_FW_TYPE_NUM 30 +#define HINIC_MAGIC_NUM 0x18221100 +#define UPDATEFW_IMAGE_HEAD_SIZE 1024 +#define FW_UPDATE_COLD 0 +#define FW_UPDATE_HOT 1 + +#define UP_TYPE_A 0x0 +#define UP_TYPE_B 0x1 + +#define MAX_FW_FRAGMENT_LEN 1536 +#define HINIC_FW_DISMATCH_ERROR 10 + +enum hinic_fw_type { + UP_FW_UPDATE_UP_TEXT_A = 0x0, + UP_FW_UPDATE_UP_DATA_A, + UP_FW_UPDATE_UP_TEXT_B, + UP_FW_UPDATE_UP_DATA_B, + UP_FW_UPDATE_UP_DICT, + + UP_FW_UPDATE_HLINK_ONE = 0x5, + UP_FW_UPDATE_HLINK_TWO, + UP_FW_UPDATE_HLINK_THR, + UP_FW_UPDATE_PHY, + UP_FW_UPDATE_TILE_TEXT, + + UP_FW_UPDATE_TILE_DATA = 0xa, + UP_FW_UPDATE_TILE_DICT, + UP_FW_UPDATE_PPE_STATE, + UP_FW_UPDATE_PPE_BRANCH, + UP_FW_UPDATE_PPE_EXTACT, + + UP_FW_UPDATE_CLP_LEGACY = 0xf, + UP_FW_UPDATE_PXE_LEGACY, + UP_FW_UPDATE_ISCSI_LEGACY, + UP_FW_UPDATE_CLP_EFI, + UP_FW_UPDATE_PXE_EFI, + + UP_FW_UPDATE_ISCSI_EFI = 0x14, + UP_FW_UPDATE_CFG, + UP_FW_UPDATE_BOOT, + UP_FW_UPDATE_VPD, + FILE_TYPE_TOTAL_NUM +}; + +#define _IMAGE_UP_ALL_IN ((1 << UP_FW_UPDATE_UP_TEXT_A) | \ + (1 << UP_FW_UPDATE_UP_DATA_A) | \ + (1 << UP_FW_UPDATE_UP_TEXT_B) | \ + (1 << UP_FW_UPDATE_UP_DATA_B) | \ + (1 << UP_FW_UPDATE_UP_DICT) | \ + (1 << UP_FW_UPDATE_BOOT) | \ + (1 << UP_FW_UPDATE_HLINK_ONE) | \ + (1 << UP_FW_UPDATE_HLINK_TWO) | \ + (1 << UP_FW_UPDATE_HLINK_THR)) + +#define _IMAGE_UCODE_ALL_IN ((1 << UP_FW_UPDATE_TILE_TEXT) | \ + (1 << UP_FW_UPDATE_TILE_DICT) | \ + (1 << UP_FW_UPDATE_PPE_STATE) | \ + (1 << UP_FW_UPDATE_PPE_BRANCH) | \ + (1 << UP_FW_UPDATE_PPE_EXTACT)) + +#define _IMAGE_COLD_SUB_MODULES_MUST_IN (_IMAGE_UP_ALL_IN | _IMAGE_UCODE_ALL_IN) +#define _IMAGE_HOT_SUB_MODULES_MUST_IN (_IMAGE_UP_ALL_IN | _IMAGE_UCODE_ALL_IN) +#define _IMAGE_CFG_SUB_MODULES_MUST_IN BIT(UP_FW_UPDATE_CFG) +#define UP_FW_UPDATE_UP_TEXT 0x0 +#define UP_FW_UPDATE_UP_DATA 0x1 +#define UP_FW_UPDATE_VPD_B 0x15 + +struct fw_section_info_st { + u32 fw_section_len; + u32 fw_section_offset; + u32 fw_section_version; + u32 fw_section_type; + u32 fw_section_crc; +}; + +struct fw_image_st { + u32 fw_version; + u32 fw_len; + u32 fw_magic; + struct { + u32 fw_section_cnt:16; + u32 resd:16; + } fw_info; + struct fw_section_info_st fw_section_info[MAX_FW_TYPE_NUM]; + u32 device_id; + u32 res[101]; + void *bin_data; +}; + +struct host_image_st { + struct fw_section_info_st image_section_info[MAX_FW_TYPE_NUM]; + struct { + u32 up_total_len; + u32 fw_version; + } image_info; + u32 section_type_num; + u32 device_id; +}; + +struct devlink *hinic_devlink_alloc(void); +void hinic_devlink_free(struct devlink *devlink); +int hinic_devlink_register(struct hinic_devlink_priv *priv, struct device *dev); +void hinic_devlink_unregister(struct hinic_devlink_priv *priv); + +int hinic_health_reporters_create(struct hinic_devlink_priv *priv); +void hinic_health_reporters_destroy(struct hinic_devlink_priv *priv); + +#endif /* __HINIC_DEVLINK_H__ */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index efb02e03e7da..6bb65ade1d77 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -25,6 +25,7 @@ #include <linux/if_vlan.h> #include <linux/ethtool.h> #include <linux/vmalloc.h> +#include <linux/sfp.h> #include "hinic_hw_qp.h" #include "hinic_hw_dev.h" @@ -49,6 +50,13 @@ #define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \ ((ecmd)->advertising |= ADVERTISED_##mode) +#define COALESCE_PENDING_LIMIT_UNIT 8 +#define COALESCE_TIMER_CFG_UNIT 9 +#define COALESCE_ALL_QUEUE 0xFFFF +#define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT) +#define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT) +#define OBJ_STR_MAX_LEN 32 + struct hw2ethtool_link_mode { enum ethtool_link_mode_bit_indices link_mode_bit; u32 speed; @@ -126,6 +134,16 @@ static struct hw2ethtool_link_mode }, }; +#define LP_DEFAULT_TIME 5 /* seconds */ +#define LP_PKT_LEN 1514 + +#define PORT_DOWN_ERR_IDX 0 +enum diag_test_index { + INTERNAL_LP_TEST = 0, + EXTERNAL_LP_TEST = 1, + DIAG_TEST_MAX = 2, +}; + static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, enum hinic_speed speed) { @@ -613,6 +631,255 @@ static int hinic_set_ringparam(struct net_device *netdev, return 0; } + +static int __hinic_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal, u16 queue) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_intr_coal_info *rx_intr_coal_info; + struct hinic_intr_coal_info *tx_intr_coal_info; + + if (queue == COALESCE_ALL_QUEUE) { + /* get tx/rx irq0 as default parameters */ + rx_intr_coal_info = &nic_dev->rx_intr_coalesce[0]; + tx_intr_coal_info = &nic_dev->tx_intr_coalesce[0]; + } else { + if (queue >= nic_dev->num_qps) { + netif_err(nic_dev, drv, netdev, + "Invalid queue_id: %d\n", queue); + return -EINVAL; + } + rx_intr_coal_info = &nic_dev->rx_intr_coalesce[queue]; + tx_intr_coal_info = &nic_dev->tx_intr_coalesce[queue]; + } + + /* coalesce_timer is in unit of 9us */ + coal->rx_coalesce_usecs = rx_intr_coal_info->coalesce_timer_cfg * + COALESCE_TIMER_CFG_UNIT; + /* coalesced_frames is in unit of 8 */ + coal->rx_max_coalesced_frames = rx_intr_coal_info->pending_limt * + COALESCE_PENDING_LIMIT_UNIT; + coal->tx_coalesce_usecs = tx_intr_coal_info->coalesce_timer_cfg * + COALESCE_TIMER_CFG_UNIT; + coal->tx_max_coalesced_frames = tx_intr_coal_info->pending_limt * + COALESCE_PENDING_LIMIT_UNIT; + + return 0; +} + +static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal) +{ + if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG || + coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT || + coal->tx_coalesce_usecs > COALESCE_MAX_TIMER_CFG || + coal->tx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT) + return -ERANGE; + + return 0; +} + +static int set_queue_coalesce(struct hinic_dev *nic_dev, u16 q_id, + struct hinic_intr_coal_info *coal, + bool set_rx_coal) +{ + struct hinic_intr_coal_info *intr_coal = NULL; + struct hinic_msix_config interrupt_info = {0}; + struct net_device *netdev = nic_dev->netdev; + u16 msix_idx; + int err; + + intr_coal = set_rx_coal ? &nic_dev->rx_intr_coalesce[q_id] : + &nic_dev->tx_intr_coalesce[q_id]; + + intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg; + intr_coal->pending_limt = coal->pending_limt; + + /* netdev not running or qp not in using, + * don't need to set coalesce to hw + */ + if (!(nic_dev->flags & HINIC_INTF_UP) || + q_id >= nic_dev->num_qps) + return 0; + + msix_idx = set_rx_coal ? nic_dev->rxqs[q_id].rq->msix_entry : + nic_dev->txqs[q_id].sq->msix_entry; + interrupt_info.msix_index = msix_idx; + interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; + interrupt_info.pending_cnt = intr_coal->pending_limt; + interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; + + err = hinic_set_interrupt_cfg(nic_dev->hwdev, &interrupt_info); + if (err) + netif_warn(nic_dev, drv, netdev, + "Failed to set %s queue%d coalesce", + set_rx_coal ? "rx" : "tx", q_id); + + return err; +} + +static int __set_hw_coal_param(struct hinic_dev *nic_dev, + struct hinic_intr_coal_info *intr_coal, + u16 queue, bool set_rx_coal) +{ + int err; + u16 i; + + if (queue == COALESCE_ALL_QUEUE) { + for (i = 0; i < nic_dev->max_qps; i++) { + err = set_queue_coalesce(nic_dev, i, intr_coal, + set_rx_coal); + if (err) + return err; + } + } else { + if (queue >= nic_dev->num_qps) { + netif_err(nic_dev, drv, nic_dev->netdev, + "Invalid queue_id: %d\n", queue); + return -EINVAL; + } + err = set_queue_coalesce(nic_dev, queue, intr_coal, + set_rx_coal); + if (err) + return err; + } + + return 0; +} + +static int __hinic_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal, u16 queue) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_intr_coal_info rx_intr_coal = {0}; + struct hinic_intr_coal_info tx_intr_coal = {0}; + bool set_rx_coal = false; + bool set_tx_coal = false; + int err; + + err = is_coalesce_exceed_limit(coal); + if (err) + return err; + + if (coal->rx_coalesce_usecs || coal->rx_max_coalesced_frames) { + rx_intr_coal.coalesce_timer_cfg = + (u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT); + rx_intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames / + COALESCE_PENDING_LIMIT_UNIT); + set_rx_coal = true; + } + + if (coal->tx_coalesce_usecs || coal->tx_max_coalesced_frames) { + tx_intr_coal.coalesce_timer_cfg = + (u8)(coal->tx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT); + tx_intr_coal.pending_limt = (u8)(coal->tx_max_coalesced_frames / + COALESCE_PENDING_LIMIT_UNIT); + set_tx_coal = true; + } + + /* setting coalesce timer or pending limit to zero will disable + * coalesce + */ + if (set_rx_coal && (!rx_intr_coal.coalesce_timer_cfg || + !rx_intr_coal.pending_limt)) + netif_warn(nic_dev, drv, netdev, "RX coalesce will be disabled\n"); + if (set_tx_coal && (!tx_intr_coal.coalesce_timer_cfg || + !tx_intr_coal.pending_limt)) + netif_warn(nic_dev, drv, netdev, "TX coalesce will be disabled\n"); + + if (set_rx_coal) { + err = __set_hw_coal_param(nic_dev, &rx_intr_coal, queue, true); + if (err) + return err; + } + if (set_tx_coal) { + err = __set_hw_coal_param(nic_dev, &tx_intr_coal, queue, false); + if (err) + return err; + } + return 0; +} + +static int hinic_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal) +{ + return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE); +} + +static int hinic_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal) +{ + return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE); +} + +static int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue, + struct ethtool_coalesce *coal) +{ + return __hinic_get_coalesce(netdev, coal, queue); +} + +static int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue, + struct ethtool_coalesce *coal) +{ + return __hinic_set_coalesce(netdev, coal, queue); +} + +static void hinic_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_pause_config pause_info = {0}; + struct hinic_nic_cfg *nic_cfg; + int err; + + nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg; + + err = hinic_get_hw_pause_info(nic_dev->hwdev, &pause_info); + if (!err) { + pause->autoneg = pause_info.auto_neg; + if (nic_cfg->pause_set || !pause_info.auto_neg) { + pause->rx_pause = nic_cfg->rx_pause; + pause->tx_pause = nic_cfg->tx_pause; + } else { + pause->rx_pause = pause_info.rx_pause; + pause->tx_pause = pause_info.tx_pause; + } + } +} + +static int hinic_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_pause_config pause_info = {0}; + struct hinic_port_cap port_cap = {0}; + int err; + + err = hinic_port_get_cap(nic_dev, &port_cap); + if (err) + return -EIO; + + if (pause->autoneg != port_cap.autoneg_state) + return -EOPNOTSUPP; + + pause_info.auto_neg = pause->autoneg; + pause_info.rx_pause = pause->rx_pause; + pause_info.tx_pause = pause->tx_pause; + + mutex_lock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex); + err = hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info); + if (err) { + mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex); + return err; + } + nic_dev->hwdev->func_to_io.nic_cfg.pause_set = true; + nic_dev->hwdev->func_to_io.nic_cfg.auto_neg = pause->autoneg; + nic_dev->hwdev->func_to_io.nic_cfg.rx_pause = pause->rx_pause; + nic_dev->hwdev->func_to_io.nic_cfg.tx_pause = pause->tx_pause; + mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex); + + return 0; +} + static void hinic_get_channels(struct net_device *netdev, struct ethtool_channels *channels) { @@ -970,6 +1237,11 @@ static struct hinic_stats hinic_function_stats[] = { HINIC_FUNC_STAT(rx_err_vport), }; +static char hinic_test_strings[][ETH_GSTRING_LEN] = { + "Internal lb test (on/offline)", + "External lb test (external_lb)", +}; + #define HINIC_PORT_STAT(_stat_item) { \ .name = #_stat_item, \ .size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \ @@ -1179,6 +1451,8 @@ static int hinic_get_sset_count(struct net_device *netdev, int sset) int count, q_num; switch (sset) { + case ETH_SS_TEST: + return ARRAY_LEN(hinic_test_strings); case ETH_SS_STATS: q_num = nic_dev->num_qps; count = ARRAY_LEN(hinic_function_stats) + @@ -1201,6 +1475,9 @@ static void hinic_get_strings(struct net_device *netdev, u16 i, j; switch (stringset) { + case ETH_SS_TEST: + memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings)); + return; case ETH_SS_STATS: for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) { memcpy(p, hinic_function_stats[i].name, @@ -1234,13 +1511,331 @@ static void hinic_get_strings(struct net_device *netdev, } } +static int hinic_run_lp_test(struct hinic_dev *nic_dev, u32 test_time) +{ + u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf; + struct net_device *netdev = nic_dev->netdev; + struct sk_buff *skb_tmp = NULL; + struct sk_buff *skb = NULL; + u32 cnt = test_time * 5; + u8 *test_data = NULL; + u32 i; + u8 j; + + skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC); + if (!skb_tmp) + return -ENOMEM; + + test_data = __skb_put(skb_tmp, LP_PKT_LEN); + + memset(test_data, 0xFF, 2 * ETH_ALEN); + test_data[ETH_ALEN] = 0xFE; + test_data[2 * ETH_ALEN] = 0x08; + test_data[2 * ETH_ALEN + 1] = 0x0; + + for (i = ETH_HLEN; i < LP_PKT_LEN; i++) + test_data[i] = i & 0xFF; + + skb_tmp->queue_mapping = 0; + skb_tmp->ip_summed = CHECKSUM_COMPLETE; + skb_tmp->dev = netdev; + + for (i = 0; i < cnt; i++) { + nic_dev->lb_test_rx_idx = 0; + memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN); + + for (j = 0; j < LP_PKT_CNT; j++) { + skb = pskb_copy(skb_tmp, GFP_ATOMIC); + if (!skb) { + dev_kfree_skb_any(skb_tmp); + netif_err(nic_dev, drv, netdev, + "Copy skb failed for loopback test\n"); + return -ENOMEM; + } + + /* mark index for every pkt */ + skb->data[LP_PKT_LEN - 1] = j; + + if (hinic_lb_xmit_frame(skb, netdev)) { + dev_kfree_skb_any(skb); + dev_kfree_skb_any(skb_tmp); + netif_err(nic_dev, drv, netdev, + "Xmit pkt failed for loopback test\n"); + return -EBUSY; + } + } + + /* wait till all pkts received to RX buffer */ + msleep(200); + + for (j = 0; j < LP_PKT_CNT; j++) { + if (memcmp(lb_test_rx_buf + j * LP_PKT_LEN, + skb_tmp->data, LP_PKT_LEN - 1) || + (*(lb_test_rx_buf + j * LP_PKT_LEN + + LP_PKT_LEN - 1) != j)) { + dev_kfree_skb_any(skb_tmp); + netif_err(nic_dev, drv, netdev, + "Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n", + j + i * LP_PKT_CNT, + LP_PKT_LEN - 1, + *(lb_test_rx_buf + j * LP_PKT_LEN + + LP_PKT_LEN - 1)); + return -EIO; + } + } + } + + dev_kfree_skb_any(skb_tmp); + return 0; +} + +static int do_lp_test(struct hinic_dev *nic_dev, u32 flags, u32 test_time, + enum diag_test_index *test_index) +{ + struct net_device *netdev = nic_dev->netdev; + u8 *lb_test_rx_buf = NULL; + int err = 0; + + if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) { + *test_index = INTERNAL_LP_TEST; + if (hinic_set_loopback_mode(nic_dev->hwdev, + HINIC_INTERNAL_LP_MODE, true)) { + netif_err(nic_dev, drv, netdev, + "Failed to set port loopback mode before loopback test\n"); + return -EIO; + } + } else { + *test_index = EXTERNAL_LP_TEST; + } + + lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN); + if (!lb_test_rx_buf) { + err = -ENOMEM; + } else { + nic_dev->lb_test_rx_buf = lb_test_rx_buf; + nic_dev->lb_pkt_len = LP_PKT_LEN; + nic_dev->flags |= HINIC_LP_TEST; + err = hinic_run_lp_test(nic_dev, test_time); + nic_dev->flags &= ~HINIC_LP_TEST; + msleep(100); + vfree(lb_test_rx_buf); + nic_dev->lb_test_rx_buf = NULL; + } + + if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) { + if (hinic_set_loopback_mode(nic_dev->hwdev, + HINIC_INTERNAL_LP_MODE, false)) { + netif_err(nic_dev, drv, netdev, + "Failed to cancel port loopback mode after loopback test\n"); + err = -EIO; + } + } + + return err; +} + +static void hinic_diag_test(struct net_device *netdev, + struct ethtool_test *eth_test, u64 *data) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + enum diag_test_index test_index = 0; + int err = 0; + + memset(data, 0, DIAG_TEST_MAX * sizeof(u64)); + + /* don't support loopback test when netdev is closed. */ + if (!(nic_dev->flags & HINIC_INTF_UP)) { + netif_err(nic_dev, drv, netdev, + "Do not support loopback test when netdev is closed\n"); + eth_test->flags |= ETH_TEST_FL_FAILED; + data[PORT_DOWN_ERR_IDX] = 1; + return; + } + + netif_carrier_off(netdev); + + err = do_lp_test(nic_dev, eth_test->flags, LP_DEFAULT_TIME, + &test_index); + if (err) { + eth_test->flags |= ETH_TEST_FL_FAILED; + data[test_index] = 1; + } + + err = hinic_port_link_state(nic_dev, &link_state); + if (!err && link_state == HINIC_LINK_STATE_UP) + netif_carrier_on(netdev); +} + +static int hinic_set_phys_id(struct net_device *netdev, + enum ethtool_phys_id_state state) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err = 0; + u8 port; + + port = nic_dev->hwdev->port_id; + + switch (state) { + case ETHTOOL_ID_ACTIVE: + err = hinic_set_led_status(nic_dev->hwdev, port, + HINIC_LED_TYPE_LINK, + HINIC_LED_MODE_FORCE_2HZ); + if (err) + netif_err(nic_dev, drv, netdev, + "Set LED blinking in 2HZ failed\n"); + break; + + case ETHTOOL_ID_INACTIVE: + err = hinic_reset_led_status(nic_dev->hwdev, port); + if (err) + netif_err(nic_dev, drv, netdev, + "Reset LED to original status failed\n"); + break; + + default: + return -EOPNOTSUPP; + } + + return err; +} + +static int hinic_get_module_info(struct net_device *netdev, + struct ethtool_modinfo *modinfo) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u8 sfp_type_ext; + u8 sfp_type; + int err; + + err = hinic_get_sfp_type(nic_dev->hwdev, &sfp_type, &sfp_type_ext); + if (err) + return err; + + switch (sfp_type) { + case SFF8024_ID_SFP: + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + break; + case SFF8024_ID_QSFP_8438: + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; + break; + case SFF8024_ID_QSFP_8436_8636: + if (sfp_type_ext >= 0x3) { + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; + + } else { + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; + } + break; + case SFF8024_ID_QSFP28_8636: + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; + break; + default: + netif_warn(nic_dev, drv, netdev, + "Optical module unknown: 0x%x\n", sfp_type); + return -EINVAL; + } + + return 0; +} + +static int hinic_get_module_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; + u16 len; + int err; + + if (!ee->len || ((ee->len + ee->offset) > STD_SFP_INFO_MAX_SIZE)) + return -EINVAL; + + memset(data, 0, ee->len); + + err = hinic_get_sfp_eeprom(nic_dev->hwdev, sfp_data, &len); + if (err) + return err; + + memcpy(data, sfp_data + ee->offset, ee->len); + + return 0; +} + +static int +hinic_get_link_ext_state(struct net_device *netdev, + struct ethtool_link_ext_state_info *link_ext_state_info) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + + if (netif_carrier_ok(netdev)) + return -ENODATA; + + if (nic_dev->cable_unplugged) + link_ext_state_info->link_ext_state = + ETHTOOL_LINK_EXT_STATE_NO_CABLE; + else if (nic_dev->module_unrecognized) + link_ext_state_info->link_ext_state = + ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH; + + return 0; +} + static const struct ethtool_ops hinic_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | + ETHTOOL_COALESCE_RX_MAX_FRAMES | + ETHTOOL_COALESCE_TX_USECS | + ETHTOOL_COALESCE_TX_MAX_FRAMES, + .get_link_ksettings = hinic_get_link_ksettings, .set_link_ksettings = hinic_set_link_ksettings, .get_drvinfo = hinic_get_drvinfo, .get_link = ethtool_op_get_link, + .get_link_ext_state = hinic_get_link_ext_state, + .get_ringparam = hinic_get_ringparam, + .set_ringparam = hinic_set_ringparam, + .get_coalesce = hinic_get_coalesce, + .set_coalesce = hinic_set_coalesce, + .get_per_queue_coalesce = hinic_get_per_queue_coalesce, + .set_per_queue_coalesce = hinic_set_per_queue_coalesce, + .get_pauseparam = hinic_get_pauseparam, + .set_pauseparam = hinic_set_pauseparam, + .get_channels = hinic_get_channels, + .set_channels = hinic_set_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, + .self_test = hinic_diag_test, + .set_phys_id = hinic_set_phys_id, + .get_module_info = hinic_get_module_info, + .get_module_eeprom = hinic_get_module_eeprom, +}; + +static const struct ethtool_ops hinicvf_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | + ETHTOOL_COALESCE_RX_MAX_FRAMES | + ETHTOOL_COALESCE_TX_USECS | + ETHTOOL_COALESCE_TX_MAX_FRAMES, + + .get_link_ksettings = hinic_get_link_ksettings, + .get_drvinfo = hinic_get_drvinfo, + .get_link = ethtool_op_get_link, .get_ringparam = hinic_get_ringparam, .set_ringparam = hinic_set_ringparam, + .get_coalesce = hinic_get_coalesce, + .set_coalesce = hinic_set_coalesce, + .get_per_queue_coalesce = hinic_get_per_queue_coalesce, + .set_per_queue_coalesce = hinic_set_per_queue_coalesce, .get_channels = hinic_get_channels, .set_channels = hinic_set_channels, .get_rxnfc = hinic_get_rxnfc, @@ -1256,5 +1851,10 @@ static const struct ethtool_ops hinic_ethtool_ops = { void hinic_set_ethtool_ops(struct net_device *netdev) { - netdev->ethtool_ops = &hinic_ethtool_ops; + struct hinic_dev *nic_dev = netdev_priv(netdev); + + if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) + netdev->ethtool_ops = &hinic_ethtool_ops; + else + netdev->ethtool_ops = &hinicvf_ethtool_ops; } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c index 583fd24c29cf..29e88e25a4a4 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -112,6 +112,26 @@ static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain) return HINIC_API_CMD_STATUS_GET(val, CONS_IDX); } +static void dump_api_chain_reg(struct hinic_api_cmd_chain *chain) +{ + u32 addr, val; + + addr = HINIC_CSR_API_CMD_STATUS_ADDR(chain->chain_type); + val = hinic_hwif_read_reg(chain->hwif, addr); + + dev_err(&chain->hwif->pdev->dev, "Chain type: 0x%x, cpld error: 0x%x, check error: 0x%x, current fsm: 0x%x\n", + chain->chain_type, HINIC_API_CMD_STATUS_GET(val, CPLD_ERR), + HINIC_API_CMD_STATUS_GET(val, CHKSUM_ERR), + HINIC_API_CMD_STATUS_GET(val, FSM)); + + dev_err(&chain->hwif->pdev->dev, "Chain hw current ci: 0x%x\n", + HINIC_API_CMD_STATUS_GET(val, CONS_IDX)); + + addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain->chain_type); + val = hinic_hwif_read_reg(chain->hwif, addr); + dev_err(&chain->hwif->pdev->dev, "Chain hw current pi: 0x%x\n", val); +} + /** * chain_busy - check if the chain is still processing last requests * @chain: chain to check @@ -131,8 +151,10 @@ static int chain_busy(struct hinic_api_cmd_chain *chain) /* check for a space for a new command */ if (chain->cons_idx == MASKED_IDX(chain, prod_idx + 1)) { - dev_err(&pdev->dev, "API CMD chain %d is busy\n", - chain->chain_type); + dev_err(&pdev->dev, "API CMD chain %d is busy, cons_idx: %d, prod_idx: %d\n", + chain->chain_type, chain->cons_idx, + chain->prod_idx); + dump_api_chain_reg(chain); return -EBUSY; } break; @@ -332,6 +354,7 @@ static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain) err = wait_for_status_poll(chain); if (err) { dev_err(&pdev->dev, "API CMD Poll status timeout\n"); + dump_api_chain_reg(chain); break; } break; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h index 0ba00fd828df..6d1654b050ad 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h @@ -103,10 +103,14 @@ HINIC_API_CMD_STATUS_HEADER_##member##_MASK) #define HINIC_API_CMD_STATUS_CONS_IDX_SHIFT 0 +#define HINIC_API_CMD_STATUS_FSM_SHIFT 24 #define HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT 28 +#define HINIC_API_CMD_STATUS_CPLD_ERR_SHIFT 30 #define HINIC_API_CMD_STATUS_CONS_IDX_MASK 0xFFFFFF +#define HINIC_API_CMD_STATUS_FSM_MASK 0xFU #define HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK 0x3 +#define HINIC_API_CMD_STATUS_CPLD_ERR_MASK 0x1U #define HINIC_API_CMD_STATUS_GET(val, member) \ (((val) >> HINIC_API_CMD_STATUS_##member##_SHIFT) & \ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index cb5b6e5f787f..e0eb294779ec 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -401,6 +401,7 @@ static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq, spin_unlock_bh(&cmdq->cmdq_lock); + hinic_dump_ceq_info(cmdq->hwdev); return -ETIMEDOUT; } @@ -807,6 +808,7 @@ static int init_cmdqs_ctxt(struct hinic_hwdev *hwdev, cmdq_type = HINIC_CMDQ_SYNC; for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { + cmdqs->cmdq[cmdq_type].hwdev = hwdev; err = init_cmdq(&cmdqs->cmdq[cmdq_type], &cmdqs->saved_wqs[cmdq_type], cmdq_type, db_area[cmdq_type]); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h index 3e4b0aef9fe6..9c413e963a04 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -31,6 +31,10 @@ (((u64)(val) & HINIC_CMDQ_CTXT_##member##_MASK) \ << HINIC_CMDQ_CTXT_##member##_SHIFT) +#define HINIC_CMDQ_CTXT_PAGE_INFO_GET(val, member) \ + (((u64)(val) >> HINIC_CMDQ_CTXT_##member##_SHIFT) \ + & HINIC_CMDQ_CTXT_##member##_MASK) + #define HINIC_CMDQ_CTXT_PAGE_INFO_CLEAR(val, member) \ ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ << HINIC_CMDQ_CTXT_##member##_SHIFT))) @@ -45,6 +49,10 @@ (((u64)(val) & HINIC_CMDQ_CTXT_##member##_MASK) \ << HINIC_CMDQ_CTXT_##member##_SHIFT) +#define HINIC_CMDQ_CTXT_BLOCK_INFO_GET(val, member) \ + (((u64)(val) >> HINIC_CMDQ_CTXT_##member##_SHIFT) \ + & HINIC_CMDQ_CTXT_##member##_MASK) + #define HINIC_CMDQ_CTXT_BLOCK_INFO_CLEAR(val, member) \ ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ << HINIC_CMDQ_CTXT_##member##_SHIFT))) @@ -130,6 +138,8 @@ struct hinic_cmdq_ctxt { }; struct hinic_cmdq { + struct hinic_hwdev *hwdev; + struct hinic_wq *wq; enum hinic_cmdq_type cmdq_type; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index b735bc537508..0c737765d113 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -16,8 +16,11 @@ #include <linux/log2.h> #include <linux/err.h> #include <linux/netdevice.h> +#include <net/devlink.h> +#include "hinic_devlink.h" #include "hinic_sriov.h" +#include "hinic_dev.h" #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_mgmt.h" @@ -83,6 +86,8 @@ static int parse_capability(struct hinic_hwdev *hwdev, nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1; } + hwdev->port_id = dev_cap->port_id; + return 0; } @@ -253,9 +258,9 @@ static int init_fw_ctxt(struct hinic_hwdev *hwdev) &fw_ctxt, sizeof(fw_ctxt), &fw_ctxt, &out_size); if (err || (out_size != sizeof(fw_ctxt)) || fw_ctxt.status) { - dev_err(&pdev->dev, "Failed to init FW ctxt, ret = %d\n", - fw_ctxt.status); - return -EFAULT; + dev_err(&pdev->dev, "Failed to init FW ctxt, err: %d, status: 0x%x, out size: 0x%x\n", + err, fw_ctxt.status, out_size); + return -EIO; } return 0; @@ -420,9 +425,9 @@ static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn) &cmd_base_qpn, sizeof(cmd_base_qpn), &cmd_base_qpn, &out_size); if (err || (out_size != sizeof(cmd_base_qpn)) || cmd_base_qpn.status) { - dev_err(&pdev->dev, "Failed to get base qpn, status = %d\n", - cmd_base_qpn.status); - return -EFAULT; + dev_err(&pdev->dev, "Failed to get base qpn, err: %d, status: 0x%x, out size: 0x%x\n", + err, cmd_base_qpn.status, out_size); + return -EIO; } *base_qpn = cmd_base_qpn.qpn; @@ -619,6 +624,113 @@ static void nic_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in, nic_cb->cb_state &= ~HINIC_CB_RUNNING; } +static void hinic_comm_recv_mgmt_self_cmd_reg(struct hinic_pfhwdev *pfhwdev, + u8 cmd, + comm_mgmt_self_msg_proc proc) +{ + u8 cmd_idx; + + cmd_idx = pfhwdev->proc.cmd_num; + if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) { + dev_err(&pfhwdev->hwdev.hwif->pdev->dev, + "Register recv mgmt process failed, cmd: 0x%x\n", cmd); + return; + } + + pfhwdev->proc.info[cmd_idx].cmd = cmd; + pfhwdev->proc.info[cmd_idx].proc = proc; + pfhwdev->proc.cmd_num++; +} + +static void hinic_comm_recv_mgmt_self_cmd_unreg(struct hinic_pfhwdev *pfhwdev, + u8 cmd) +{ + u8 cmd_idx; + + cmd_idx = pfhwdev->proc.cmd_num; + if (cmd_idx >= HINIC_COMM_SELF_CMD_MAX) { + dev_err(&pfhwdev->hwdev.hwif->pdev->dev, "Unregister recv mgmt process failed, cmd: 0x%x\n", + cmd); + return; + } + + for (cmd_idx = 0; cmd_idx < HINIC_COMM_SELF_CMD_MAX; cmd_idx++) { + if (cmd == pfhwdev->proc.info[cmd_idx].cmd) { + pfhwdev->proc.info[cmd_idx].cmd = 0; + pfhwdev->proc.info[cmd_idx].proc = NULL; + pfhwdev->proc.cmd_num--; + } + } +} + +static void comm_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in, + u16 in_size, void *buf_out, u16 *out_size) +{ + struct hinic_pfhwdev *pfhwdev = handle; + u8 cmd_idx; + + for (cmd_idx = 0; cmd_idx < pfhwdev->proc.cmd_num; cmd_idx++) { + if (cmd == pfhwdev->proc.info[cmd_idx].cmd) { + if (!pfhwdev->proc.info[cmd_idx].proc) { + dev_warn(&pfhwdev->hwdev.hwif->pdev->dev, + "PF recv mgmt comm msg handle null, cmd: 0x%x\n", + cmd); + } else { + pfhwdev->proc.info[cmd_idx].proc + (&pfhwdev->hwdev, buf_in, in_size, + buf_out, out_size); + } + + return; + } + } + + dev_warn(&pfhwdev->hwdev.hwif->pdev->dev, "Received unknown mgmt cpu event: 0x%x\n", + cmd); + + *out_size = 0; +} + +/* pf fault report event */ +static void pf_fault_event_handler(void *dev, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cmd_fault_event *fault_event = buf_in; + struct hinic_hwdev *hwdev = dev; + + if (in_size != sizeof(*fault_event)) { + dev_err(&hwdev->hwif->pdev->dev, "Invalid fault event report, length: %d, should be %zu\n", + in_size, sizeof(*fault_event)); + return; + } + + if (!hwdev->devlink_dev || IS_ERR_OR_NULL(hwdev->devlink_dev->hw_fault_reporter)) + return; + + devlink_health_report(hwdev->devlink_dev->hw_fault_reporter, + "HW fatal error reported", &fault_event->event); +} + +static void mgmt_watchdog_timeout_event_handler(void *dev, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_mgmt_watchdog_info *watchdog_info = buf_in; + struct hinic_hwdev *hwdev = dev; + + if (in_size != sizeof(*watchdog_info)) { + dev_err(&hwdev->hwif->pdev->dev, "Invalid mgmt watchdog report, length: %d, should be %zu\n", + in_size, sizeof(*watchdog_info)); + return; + } + + if (!hwdev->devlink_dev || IS_ERR_OR_NULL(hwdev->devlink_dev->fw_fault_reporter)) + return; + + devlink_health_report(hwdev->devlink_dev->fw_fault_reporter, + "FW fatal error reported", watchdog_info); +} + /** * init_pfhwdev - Initialize the extended components of PF * @pfhwdev: the HW device for PF @@ -638,20 +750,37 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) return err; } + err = hinic_devlink_register(hwdev->devlink_dev, &pdev->dev); + if (err) { + dev_err(&hwif->pdev->dev, "Failed to register devlink\n"); + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); + return err; + } + err = hinic_func_to_func_init(hwdev); if (err) { dev_err(&hwif->pdev->dev, "Failed to init mailbox\n"); + hinic_devlink_unregister(hwdev->devlink_dev); hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); return err; } - if (!HINIC_IS_VF(hwif)) + if (!HINIC_IS_VF(hwif)) { hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, pfhwdev, nic_mgmt_msg_handler); - else + hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + pfhwdev, comm_mgmt_msg_handler); + hinic_comm_recv_mgmt_self_cmd_reg(pfhwdev, + HINIC_COMM_CMD_FAULT_REPORT, + pf_fault_event_handler); + hinic_comm_recv_mgmt_self_cmd_reg + (pfhwdev, HINIC_COMM_CMD_WATCHDOG_INFO, + mgmt_watchdog_timeout_event_handler); + } else { hinic_register_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC, nic_mgmt_msg_handler); + } hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE); @@ -668,14 +797,23 @@ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT); - if (!HINIC_IS_VF(hwdev->hwif)) + if (!HINIC_IS_VF(hwdev->hwif)) { + hinic_comm_recv_mgmt_self_cmd_unreg(pfhwdev, + HINIC_COMM_CMD_WATCHDOG_INFO); + hinic_comm_recv_mgmt_self_cmd_unreg(pfhwdev, + HINIC_COMM_CMD_FAULT_REPORT); + hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, + HINIC_MOD_COMM); hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC); - else + } else { hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC); + } hinic_func_to_func_free(hwdev); + hinic_devlink_unregister(hwdev->devlink_dev); + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); } @@ -705,6 +843,68 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev) return 0; } +int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, + struct hinic_msix_config *interrupt_info) +{ + u16 out_size = sizeof(*interrupt_info); + struct hinic_pfhwdev *pfhwdev; + int err; + + if (!hwdev || !interrupt_info) + return -EINVAL; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP, + interrupt_info, sizeof(*interrupt_info), + interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC); + if (err || !out_size || interrupt_info->status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n", + err, interrupt_info->status, out_size); + return -EIO; + } + + return 0; +} + +int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, + struct hinic_msix_config *interrupt_info) +{ + u16 out_size = sizeof(*interrupt_info); + struct hinic_msix_config temp_info; + struct hinic_pfhwdev *pfhwdev; + int err; + + if (!hwdev) + return -EINVAL; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + + err = hinic_get_interrupt_cfg(hwdev, &temp_info); + if (err) + return -EINVAL; + + interrupt_info->lli_credit_cnt = temp_info.lli_timer_cnt; + interrupt_info->lli_timer_cnt = temp_info.lli_timer_cnt; + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP, + interrupt_info, sizeof(*interrupt_info), + interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC); + if (err || !out_size || interrupt_info->status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n", + err, interrupt_info->status, out_size); + return -EIO; + } + + return 0; +} + /** * hinic_init_hwdev - Initialize the NIC HW * @pdev: the NIC pci device @@ -713,7 +913,7 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev) * * Initialize the NIC HW device and return a pointer to it **/ -struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev, struct devlink *devlink) { struct hinic_pfhwdev *pfhwdev; struct hinic_hwdev *hwdev; @@ -738,6 +938,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) hwdev = &pfhwdev->hwdev; hwdev->hwif = hwif; + hwdev->devlink_dev = devlink_priv(devlink); + hwdev->devlink_dev->hwdev = hwdev; err = init_msix(hwdev); if (err) { @@ -777,6 +979,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) goto err_dev_cap; } + mutex_init(&hwdev->func_to_io.nic_cfg.cfg_mutex); + err = hinic_vf_func_init(hwdev); if (err) { dev_err(&pdev->dev, "Failed to init nic mbox\n"); @@ -831,6 +1035,8 @@ void hinic_free_hwdev(struct hinic_hwdev *hwdev) set_resources_state(hwdev, HINIC_RES_CLEAN); + hinic_vf_func_free(hwdev); + free_pfhwdev(pfhwdev); hinic_aeqs_free(&hwdev->aeqs); @@ -981,3 +1187,29 @@ void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index, { hinic_set_msix_state(hwdev->hwif, msix_index, flag); } + +int hinic_get_board_info(struct hinic_hwdev *hwdev, + struct hinic_comm_board_info *board_info) +{ + u16 out_size = sizeof(*board_info); + struct hinic_pfhwdev *pfhwdev; + int err; + + if (!hwdev || !board_info) + return -EINVAL; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_GET_BOARD_INFO, + board_info, sizeof(*board_info), + board_info, &out_size, HINIC_MGMT_MSG_SYNC); + if (err || board_info->status || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to get board info, err: %d, status: 0x%x, out size: 0x%x\n", + err, board_info->status, out_size); + return -EIO; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 71ea7e46dbbc..dc6e645f2689 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -10,6 +10,7 @@ #include <linux/pci.h> #include <linux/types.h> #include <linux/bitops.h> +#include <net/devlink.h> #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" @@ -27,6 +28,8 @@ #define HINIC_MGMT_STATUS_EXIST 0x6 #define HINIC_MGMT_CMD_UNSUPPORTED 0xFF +#define HINIC_CMD_VER_FUNC_ID 2 + struct hinic_cap { u16 max_qps; u16 num_qps; @@ -48,6 +51,8 @@ enum hinic_port_cmd { HINIC_PORT_CMD_ADD_VLAN = 3, HINIC_PORT_CMD_DEL_VLAN = 4, + HINIC_PORT_CMD_SET_PFC = 5, + HINIC_PORT_CMD_SET_MAC = 9, HINIC_PORT_CMD_GET_MAC = 10, HINIC_PORT_CMD_DEL_MAC = 11, @@ -95,6 +100,9 @@ enum hinic_port_cmd { HINIC_PORT_CMD_FWCTXT_INIT = 69, + HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72, + HINIC_PORT_CMD_SET_LOOPBACK_MODE, + HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78, HINIC_PORT_CMD_GET_MGMT_VERSION = 88, @@ -111,6 +119,8 @@ enum hinic_port_cmd { HINIC_PORT_CMD_SET_TSO = 112, + HINIC_PORT_CMD_UPDATE_FW = 114, + HINIC_PORT_CMD_SET_RQ_IQ_MAP = 115, HINIC_PORT_CMD_LINK_STATUS_REPORT = 160, @@ -125,9 +135,13 @@ enum hinic_port_cmd { HINIC_PORT_CMD_SET_AUTONEG = 219, + HINIC_PORT_CMD_GET_STD_SFP_INFO = 240, + HINIC_PORT_CMD_SET_LRO_TIMER = 244, HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 249, + + HINIC_PORT_CMD_GET_SFP_ABS = 251, }; /* cmd of mgmt CPU message for HILINK module */ @@ -153,9 +167,12 @@ enum hinic_ucode_cmd { #define NIC_RSS_CMD_TEMP_FREE 0x02 enum hinic_mgmt_msg_cmd { - HINIC_MGMT_MSG_CMD_BASE = 160, + HINIC_MGMT_MSG_CMD_BASE = 0xA0, - HINIC_MGMT_MSG_CMD_LINK_STATUS = 160, + HINIC_MGMT_MSG_CMD_LINK_STATUS = 0xA0, + + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT = 0xE5, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT = 0xE6, HINIC_MGMT_MSG_CMD_MAX, }; @@ -283,6 +300,61 @@ struct hinic_cmd_l2nic_reset { u16 reset_flag; }; +struct hinic_msix_config { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 msix_index; + u8 pending_cnt; + u8 coalesce_timer_cnt; + u8 lli_timer_cnt; + u8 lli_credit_cnt; + u8 resend_timer_cnt; + u8 rsvd1[3]; +}; + +struct hinic_set_random_id { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 vf_in_pf; + u8 rsvd1; + u16 func_idx; + u32 random_id; +}; + +struct hinic_board_info { + u32 board_type; + u32 port_num; + u32 port_speed; + u32 pcie_width; + u32 host_num; + u32 pf_num; + u32 vf_total_num; + u32 tile_num; + u32 qcm_num; + u32 core_num; + u32 work_mode; + u32 service_mode; + u32 pcie_mode; + u32 cfg_addr; + u32 boot_sel; + u32 board_id; +}; + +struct hinic_comm_board_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct hinic_board_info info; + + u32 rsvd1[4]; +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; @@ -292,6 +364,8 @@ struct hinic_hwdev { struct hinic_mbox_func_to_func *func_to_func; struct hinic_cap nic_cap; + u8 port_id; + struct hinic_devlink_priv *devlink_dev; }; struct hinic_nic_cb { @@ -303,12 +377,29 @@ struct hinic_nic_cb { unsigned long cb_state; }; +#define HINIC_COMM_SELF_CMD_MAX 4 + +typedef void (*comm_mgmt_self_msg_proc)(void *handle, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + +struct comm_mgmt_self_msg_sub_info { + u8 cmd; + comm_mgmt_self_msg_proc proc; +}; + +struct comm_mgmt_self_msg_info { + u8 cmd_num; + struct comm_mgmt_self_msg_sub_info info[HINIC_COMM_SELF_CMD_MAX]; +}; + struct hinic_pfhwdev { struct hinic_hwdev hwdev; struct hinic_pf_to_mgmt pf_to_mgmt; struct hinic_nic_cb nic_cb[HINIC_MGMT_NUM_MSG_CMD]; + + struct comm_mgmt_self_msg_info proc; }; struct hinic_dev_cap { @@ -330,6 +421,126 @@ struct hinic_dev_cap { u8 rsvd3[204]; }; +union hinic_fault_hw_mgmt { + u32 val[4]; + /* valid only type == FAULT_TYPE_CHIP */ + struct { + u8 node_id; + u8 err_level; + u16 err_type; + u32 err_csr_addr; + u32 err_csr_value; + /* func_id valid only if err_level == FAULT_LEVEL_SERIOUS_FLR */ + u16 func_id; + u16 rsvd2; + } chip; + + /* valid only if type == FAULT_TYPE_UCODE */ + struct { + u8 cause_id; + u8 core_id; + u8 c_id; + u8 rsvd3; + u32 epc; + u32 rsvd4; + u32 rsvd5; + } ucode; + + /* valid only if type == FAULT_TYPE_MEM_RD_TIMEOUT || + * FAULT_TYPE_MEM_WR_TIMEOUT + */ + struct { + u32 err_csr_ctrl; + u32 err_csr_data; + u32 ctrl_tab; + u32 mem_index; + } mem_timeout; + + /* valid only if type == FAULT_TYPE_REG_RD_TIMEOUT || + * FAULT_TYPE_REG_WR_TIMEOUT + */ + struct { + u32 err_csr; + u32 rsvd6; + u32 rsvd7; + u32 rsvd8; + } reg_timeout; + + struct { + /* 0: read; 1: write */ + u8 op_type; + u8 port_id; + u8 dev_ad; + u8 rsvd9; + u32 csr_addr; + u32 op_data; + u32 rsvd10; + } phy_fault; +}; + +struct hinic_fault_event { + u8 type; + u8 fault_level; + u8 rsvd0[2]; + union hinic_fault_hw_mgmt event; +}; + +struct hinic_cmd_fault_event { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct hinic_fault_event event; +}; + +enum hinic_fault_type { + FAULT_TYPE_CHIP, + FAULT_TYPE_UCODE, + FAULT_TYPE_MEM_RD_TIMEOUT, + FAULT_TYPE_MEM_WR_TIMEOUT, + FAULT_TYPE_REG_RD_TIMEOUT, + FAULT_TYPE_REG_WR_TIMEOUT, + FAULT_TYPE_PHY_FAULT, + FAULT_TYPE_MAX, +}; + +#define FAULT_SHOW_STR_LEN 16 + +enum hinic_fault_err_level { + FAULT_LEVEL_FATAL, + FAULT_LEVEL_SERIOUS_RESET, + FAULT_LEVEL_SERIOUS_FLR, + FAULT_LEVEL_GENERAL, + FAULT_LEVEL_SUGGESTION, + FAULT_LEVEL_MAX +}; + +struct hinic_mgmt_watchdog_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + u32 curr_time_h; + u32 curr_time_l; + u32 task_id; + u32 rsv; + + u32 reg[13]; + u32 pc; + u32 lr; + u32 cpsr; + + u32 stack_top; + u32 stack_bottom; + u32 sp; + u32 curr_used; + u32 peak_used; + u32 is_overflow; + + u32 stack_actlen; + u8 data[1024]; +}; + void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev, enum hinic_mgmt_msg_cmd cmd, void *handle, void (*handler)(void *handle, void *buf_in, @@ -351,7 +562,7 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev, u16 sq_depth, u16 rq_depth); void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev); -struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev, struct devlink *devlink); void hinic_free_hwdev(struct hinic_hwdev *hwdev); @@ -376,4 +587,13 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq, void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index, enum hinic_msix_state flag); +int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev, + struct hinic_msix_config *interrupt_info); + +int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev, + struct hinic_msix_config *interrupt_info); + +int hinic_get_board_info(struct hinic_hwdev *hwdev, + struct hinic_comm_board_info *board_info); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c index 397936cac304..ca8cb68a8d20 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c @@ -953,3 +953,42 @@ void hinic_ceqs_free(struct hinic_ceqs *ceqs) for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) remove_eq(&ceqs->ceq[q_id]); } + +void hinic_dump_ceq_info(struct hinic_hwdev *hwdev) +{ + struct hinic_eq *eq = NULL; + u32 addr, ci, pi; + int q_id; + + for (q_id = 0; q_id < hwdev->func_to_io.ceqs.num_ceqs; q_id++) { + eq = &hwdev->func_to_io.ceqs.ceq[q_id]; + addr = EQ_CONS_IDX_REG_ADDR(eq); + ci = hinic_hwif_read_reg(hwdev->hwif, addr); + addr = EQ_PROD_IDX_REG_ADDR(eq); + pi = hinic_hwif_read_reg(hwdev->hwif, addr); + dev_err(&hwdev->hwif->pdev->dev, "Ceq id: %d, ci: 0x%08x, sw_ci: 0x%08x, pi: 0x%x, tasklet_state: 0x%lx, wrap: %d, ceqe: 0x%x\n", + q_id, ci, eq->cons_idx, pi, + eq->ceq_tasklet.state, + eq->wrapped, be32_to_cpu(*(__be32 *)(GET_CURR_CEQ_ELEM(eq)))); + } +} + +void hinic_dump_aeq_info(struct hinic_hwdev *hwdev) +{ + struct hinic_aeq_elem *aeqe_pos = NULL; + struct hinic_eq *eq = NULL; + u32 addr, ci, pi; + int q_id; + + for (q_id = 0; q_id < hwdev->aeqs.num_aeqs; q_id++) { + eq = &hwdev->aeqs.aeq[q_id]; + addr = EQ_CONS_IDX_REG_ADDR(eq); + ci = hinic_hwif_read_reg(hwdev->hwif, addr); + addr = EQ_PROD_IDX_REG_ADDR(eq); + pi = hinic_hwif_read_reg(hwdev->hwif, addr); + aeqe_pos = GET_CURR_AEQ_ELEM(eq); + dev_err(&hwdev->hwif->pdev->dev, "Aeq id: %d, ci: 0x%08x, pi: 0x%x, work_state: 0x%x, wrap: %d, desc: 0x%x\n", + q_id, ci, pi, work_busy(&eq->aeq_work.work), + eq->wrapped, be32_to_cpu(aeqe_pos->desc)); + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h index 74b9ff90640c..43065fc70869 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h @@ -162,7 +162,7 @@ enum hinic_eqe_state { struct hinic_aeq_elem { u8 data[HINIC_AEQE_DATA_SIZE]; - u32 desc; + __be32 desc; }; struct hinic_eq_work { @@ -254,4 +254,8 @@ int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif, void hinic_ceqs_free(struct hinic_ceqs *ceqs); +void hinic_dump_ceq_info(struct hinic_hwdev *hwdev); + +void hinic_dump_aeq_info(struct hinic_hwdev *hwdev); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index cf127d896ba6..bc8925c0c982 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -21,6 +21,8 @@ #define WAIT_HWIF_READY_TIMEOUT 10000 +#define HINIC_SELFTEST_RESULT 0x883C + /** * hinic_msix_attr_set - set message attribute for msix entry * @hwif: the HW interface of a pci function device @@ -369,6 +371,26 @@ u16 hinic_pf_id_of_vf_hw(struct hinic_hwif *hwif) return HINIC_FA0_GET(attr0, PF_IDX); } +static void __print_selftest_reg(struct hinic_hwif *hwif) +{ + u32 addr, attr0, attr1; + + addr = HINIC_CSR_FUNC_ATTR1_ADDR; + attr1 = hinic_hwif_read_reg(hwif, addr); + + if (attr1 == HINIC_PCIE_LINK_DOWN) { + dev_err(&hwif->pdev->dev, "PCIE is link down\n"); + return; + } + + addr = HINIC_CSR_FUNC_ATTR0_ADDR; + attr0 = hinic_hwif_read_reg(hwif, addr); + if (HINIC_FA0_GET(attr0, FUNC_TYPE) != HINIC_VF && + !HINIC_FA0_GET(attr0, PCI_INTF_IDX)) + dev_err(&hwif->pdev->dev, "Selftest reg: 0x%08x\n", + hinic_hwif_read_reg(hwif, HINIC_SELFTEST_RESULT)); +} + /** * hinic_init_hwif - initialize the hw interface * @hwif: the HW interface of a pci function device @@ -398,6 +420,7 @@ int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev) err = wait_hwif_ready(hwif); if (err) { dev_err(&pdev->dev, "HW interface is not ready\n"); + __print_selftest_reg(hwif); goto err_hwif_ready; } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index 0872e035faa1..c06f2253151e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -12,6 +12,8 @@ #include <linux/types.h> #include <asm/byteorder.h> +#define HINIC_PCIE_LINK_DOWN 0xFFFFFFFF + #define HINIC_DMA_ATTR_ST_SHIFT 0 #define HINIC_DMA_ATTR_AT_SHIFT 8 #define HINIC_DMA_ATTR_PH_SHIFT 10 @@ -249,13 +251,17 @@ struct hinic_hwif { static inline u32 hinic_hwif_read_reg(struct hinic_hwif *hwif, u32 reg) { - return be32_to_cpu(readl(hwif->cfg_regs_bar + reg)); + u32 out = readl(hwif->cfg_regs_bar + reg); + + return be32_to_cpu(*(__be32 *)&out); } static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, u32 val) { - writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg); + __be32 in = cpu_to_be32(val); + + writel(*(u32 *)&in, hwif->cfg_regs_bar + reg); } int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index 214f162f7579..ee6d60762d84 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -47,6 +47,15 @@ struct hinic_free_db_area { struct semaphore idx_lock; }; +struct hinic_nic_cfg { + /* lock for getting nic cfg */ + struct mutex cfg_mutex; + bool pause_set; + u32 auto_neg; + u32 rx_pause; + u32 tx_pause; +}; + struct hinic_func_to_io { struct hinic_hwif *hwif; struct hinic_hwdev *hwdev; @@ -78,6 +87,7 @@ struct hinic_func_to_io { u16 max_vfs; struct vf_data_storage *vf_infos; u8 link_status; + struct hinic_nic_cfg nic_cfg; }; struct hinic_wq_page_size { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c index bc2f87e6cb5d..5078c0c73863 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c @@ -153,7 +153,6 @@ enum hinic_mbox_tx_status { (MBOX_MSG_ID(func_to_func_mbox) + 1) & MBOX_MSG_ID_MASK) #define FUNC_ID_OFF_SET_8B 8 -#define FUNC_ID_OFF_SET_10B 10 /* max message counter wait to process for one function */ #define HINIC_MAX_MSG_CNT_TO_PROCESS 10 @@ -189,6 +188,37 @@ enum mbox_aeq_trig_type { TRIGGER, }; +static bool check_func_id(struct hinic_hwdev *hwdev, u16 src_func_idx, + const void *buf_in, u16 in_size, u16 offset) +{ + u16 func_idx; + + if (in_size < offset + sizeof(func_idx)) { + dev_warn(&hwdev->hwif->pdev->dev, + "Receive mailbox msg len: %d less than %d Bytes is invalid\n", + in_size, offset); + return false; + } + + func_idx = *((u16 *)((u8 *)buf_in + offset)); + + if (src_func_idx != func_idx) { + dev_warn(&hwdev->hwif->pdev->dev, + "Receive mailbox function id: 0x%x not equal to msg function id: 0x%x\n", + src_func_idx, func_idx); + return false; + } + + return true; +} + +bool hinic_mbox_check_func_id_8B(struct hinic_hwdev *hwdev, u16 func_idx, + void *buf_in, u16 in_size) +{ + return check_func_id(hwdev, func_idx, buf_in, in_size, + FUNC_ID_OFF_SET_8B); +} + /** * hinic_register_pf_mbox_cb - register mbox callback for pf * @hwdev: the pointer to hw device @@ -486,6 +516,111 @@ err_alloc_rcv_mbox_msg: kfree(rcv_mbox_temp); } +static int set_vf_mbox_random_id(struct hinic_hwdev *hwdev, u16 func_id) +{ + struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; + struct hinic_set_random_id rand_info = {0}; + u16 out_size = sizeof(rand_info); + struct hinic_pfhwdev *pfhwdev; + int ret; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + rand_info.version = HINIC_CMD_VER_FUNC_ID; + rand_info.func_idx = func_id; + rand_info.vf_in_pf = func_id - hinic_glb_pf_vf_offset(hwdev->hwif); + rand_info.random_id = get_random_u32(); + + func_to_func->vf_mbx_rand_id[func_id] = rand_info.random_id; + + ret = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_MGMT_CMD_SET_VF_RANDOM_ID, + &rand_info, sizeof(rand_info), + &rand_info, &out_size, HINIC_MGMT_MSG_SYNC); + if ((rand_info.status != HINIC_MGMT_CMD_UNSUPPORTED && + rand_info.status) || !out_size || ret) { + dev_err(&hwdev->hwif->pdev->dev, "Set VF random id failed, err: %d, status: 0x%x, out size: 0x%x\n", + ret, rand_info.status, out_size); + return -EIO; + } + + if (rand_info.status == HINIC_MGMT_CMD_UNSUPPORTED) + return rand_info.status; + + func_to_func->vf_mbx_old_rand_id[func_id] = + func_to_func->vf_mbx_rand_id[func_id]; + + return 0; +} + +static void update_random_id_work_handler(struct work_struct *work) +{ + struct hinic_mbox_work *mbox_work = + container_of(work, struct hinic_mbox_work, work); + struct hinic_mbox_func_to_func *func_to_func; + u16 src = mbox_work->src_func_idx; + + func_to_func = mbox_work->func_to_func; + + if (set_vf_mbox_random_id(func_to_func->hwdev, src)) + dev_warn(&func_to_func->hwdev->hwif->pdev->dev, "Update VF id: 0x%x random id failed\n", + mbox_work->src_func_idx); + + kfree(mbox_work); +} + +static bool check_vf_mbox_random_id(struct hinic_mbox_func_to_func *func_to_func, + u8 *header) +{ + struct hinic_hwdev *hwdev = func_to_func->hwdev; + struct hinic_mbox_work *mbox_work = NULL; + u64 mbox_header = *((u64 *)header); + u16 offset, src; + u32 random_id; + int vf_in_pf; + + src = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); + + if (IS_PF_OR_PPF_SRC(src) || !func_to_func->support_vf_random) + return true; + + if (!HINIC_IS_PPF(hwdev->hwif)) { + offset = hinic_glb_pf_vf_offset(hwdev->hwif); + vf_in_pf = src - offset; + + if (vf_in_pf < 1 || vf_in_pf > hwdev->nic_cap.max_vf) { + dev_warn(&hwdev->hwif->pdev->dev, + "Receive vf id(0x%x) is invalid, vf id should be from 0x%x to 0x%x\n", + src, offset + 1, + hwdev->nic_cap.max_vf + offset); + return false; + } + } + + random_id = be32_to_cpu(*(u32 *)(header + MBOX_SEG_LEN + + MBOX_HEADER_SZ)); + + if (random_id == func_to_func->vf_mbx_rand_id[src] || + random_id == func_to_func->vf_mbx_old_rand_id[src]) + return true; + + dev_warn(&hwdev->hwif->pdev->dev, + "The mailbox random id(0x%x) of func_id(0x%x) doesn't match with pf reservation(0x%x)\n", + random_id, src, func_to_func->vf_mbx_rand_id[src]); + + mbox_work = kzalloc(sizeof(*mbox_work), GFP_KERNEL); + if (!mbox_work) + return false; + + mbox_work->func_to_func = func_to_func; + mbox_work->src_func_idx = src; + + INIT_WORK(&mbox_work->work, update_random_id_work_handler); + queue_work(func_to_func->workq, &mbox_work->work); + + return false; +} + void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size) { struct hinic_mbox_func_to_func *func_to_func; @@ -504,6 +639,9 @@ void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size) return; } + if (!check_vf_mbox_random_id(func_to_func, header)) + return; + recv_mbox = (dir == HINIC_HWIF_DIRECT_SEND) ? &func_to_func->mbox_send[src] : &func_to_func->mbox_resp[src]; @@ -650,6 +788,7 @@ wait_for_mbox_seg_completion(struct hinic_mbox_func_to_func *func_to_func, if (!wait_for_completion_timeout(done, jif)) { dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment timeout\n"); dump_mox_reg(hwdev); + hinic_dump_aeq_info(hwdev); return -ETIMEDOUT; } @@ -897,6 +1036,7 @@ int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, set_mbox_to_func_event(func_to_func, EVENT_TIMEOUT); dev_err(&func_to_func->hwif->pdev->dev, "Send mbox msg timeout, msg_id: %d\n", msg_info.msg_id); + hinic_dump_aeq_info(func_to_func->hwdev); err = -ETIMEDOUT; goto err_send_mbox; } @@ -1095,15 +1235,156 @@ static void free_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func) send_mbox->wb_paddr); } +bool hinic_mbox_check_cmd_valid(struct hinic_hwdev *hwdev, + struct vf_cmd_check_handle *cmd_handle, + u16 vf_id, u8 cmd, void *buf_in, + u16 in_size, u8 size) +{ + u16 src_idx = vf_id + hinic_glb_pf_vf_offset(hwdev->hwif); + int i; + + for (i = 0; i < size; i++) { + if (cmd == cmd_handle[i].cmd) { + if (cmd_handle[i].check_cmd) + return cmd_handle[i].check_cmd(hwdev, src_idx, + buf_in, in_size); + else + return true; + } + } + + dev_err(&hwdev->hwif->pdev->dev, + "PF Receive VF(%d) unsupported cmd(0x%x)\n", + vf_id + hinic_glb_pf_vf_offset(hwdev->hwif), cmd); + + return false; +} + +static bool hinic_cmdq_check_vf_ctxt(struct hinic_hwdev *hwdev, + struct hinic_cmdq_ctxt *cmdq_ctxt) +{ + struct hinic_cmdq_ctxt_info *ctxt_info = &cmdq_ctxt->ctxt_info; + u64 curr_pg_pfn, wq_block_pfn; + + if (cmdq_ctxt->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif) || + cmdq_ctxt->cmdq_type > HINIC_MAX_CMDQ_TYPES) + return false; + + curr_pg_pfn = HINIC_CMDQ_CTXT_PAGE_INFO_GET + (ctxt_info->curr_wqe_page_pfn, CURR_WQE_PAGE_PFN); + wq_block_pfn = HINIC_CMDQ_CTXT_BLOCK_INFO_GET + (ctxt_info->wq_block_pfn, WQ_BLOCK_PFN); + /* VF must use 0-level CLA */ + if (curr_pg_pfn != wq_block_pfn) + return false; + + return true; +} + +static bool check_cmdq_ctxt(struct hinic_hwdev *hwdev, u16 func_idx, + void *buf_in, u16 in_size) +{ + if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) + return false; + + return hinic_cmdq_check_vf_ctxt(hwdev, buf_in); +} + +#define HW_CTX_QPS_VALID(hw_ctxt) \ + ((hw_ctxt)->rq_depth >= HINIC_QUEUE_MIN_DEPTH && \ + (hw_ctxt)->rq_depth <= HINIC_QUEUE_MAX_DEPTH && \ + (hw_ctxt)->sq_depth >= HINIC_QUEUE_MIN_DEPTH && \ + (hw_ctxt)->sq_depth <= HINIC_QUEUE_MAX_DEPTH && \ + (hw_ctxt)->rx_buf_sz_idx <= HINIC_MAX_RX_BUFFER_SIZE) + +static bool hw_ctxt_qps_param_valid(struct hinic_cmd_hw_ioctxt *hw_ctxt) +{ + if (HW_CTX_QPS_VALID(hw_ctxt)) + return true; + + if (!hw_ctxt->rq_depth && !hw_ctxt->sq_depth && + !hw_ctxt->rx_buf_sz_idx) + return true; + + return false; +} + +static bool check_hwctxt(struct hinic_hwdev *hwdev, u16 func_idx, + void *buf_in, u16 in_size) +{ + struct hinic_cmd_hw_ioctxt *hw_ctxt = buf_in; + + if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) + return false; + + if (hw_ctxt->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif)) + return false; + + if (hw_ctxt->set_cmdq_depth) { + if (hw_ctxt->cmdq_depth >= HINIC_QUEUE_MIN_DEPTH && + hw_ctxt->cmdq_depth <= HINIC_QUEUE_MAX_DEPTH) + return true; + + return false; + } + + return hw_ctxt_qps_param_valid(hw_ctxt); +} + +static bool check_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx, + void *buf_in, u16 in_size) +{ + struct hinic_wq_page_size *page_size_info = buf_in; + + if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) + return false; + + if (page_size_info->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif)) + return false; + + if (((1U << page_size_info->page_size) * SZ_4K) != + HINIC_DEFAULT_WQ_PAGE_SIZE) + return false; + + return true; +} + +static struct vf_cmd_check_handle hw_cmd_support_vf[] = { + {HINIC_COMM_CMD_START_FLR, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_DMA_ATTR_SET, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_CMDQ_CTXT_SET, check_cmdq_ctxt}, + {HINIC_COMM_CMD_CMDQ_CTXT_GET, check_cmdq_ctxt}, + {HINIC_COMM_CMD_HWCTXT_SET, check_hwctxt}, + {HINIC_COMM_CMD_HWCTXT_GET, check_hwctxt}, + {HINIC_COMM_CMD_SQ_HI_CI_SET, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_RES_STATE_SET, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_IO_RES_CLEAR, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_L2NIC_RESET, hinic_mbox_check_func_id_8B}, + {HINIC_COMM_CMD_PAGESIZE_SET, check_set_wq_page_size}, +}; + static int comm_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { + u8 size = ARRAY_SIZE(hw_cmd_support_vf); struct hinic_hwdev *hwdev = handle; struct hinic_pfhwdev *pfhwdev; int err = 0; pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + if (!hinic_mbox_check_cmd_valid(handle, hw_cmd_support_vf, vf_id, cmd, + buf_in, in_size, size)) { + dev_err(&hwdev->hwif->pdev->dev, + "PF Receive VF: %d common cmd: 0x%x or mbox len: 0x%x is invalid\n", + vf_id + hinic_glb_pf_vf_offset(hwdev->hwif), cmd, + in_size); + return HINIC_MBOX_VF_CMD_ERROR; + } + if (cmd == HINIC_COMM_CMD_START_FLR) { *out_size = 0; } else { @@ -1208,3 +1489,32 @@ void hinic_func_to_func_free(struct hinic_hwdev *hwdev) kfree(func_to_func); } + +int hinic_vf_mbox_random_id_init(struct hinic_hwdev *hwdev) +{ + u16 vf_offset; + u8 vf_in_pf; + int err = 0; + + if (HINIC_IS_VF(hwdev->hwif)) + return 0; + + vf_offset = hinic_glb_pf_vf_offset(hwdev->hwif); + + for (vf_in_pf = 1; vf_in_pf <= hwdev->nic_cap.max_vf; vf_in_pf++) { + err = set_vf_mbox_random_id(hwdev, vf_offset + vf_in_pf); + if (err) + break; + } + + if (err == HINIC_MGMT_CMD_UNSUPPORTED) { + hwdev->func_to_func->support_vf_random = false; + err = 0; + dev_warn(&hwdev->hwif->pdev->dev, "Mgmt is unsupported to set VF%d random id\n", + vf_in_pf - 1); + } else if (!err) { + hwdev->func_to_func->support_vf_random = true; + } + + return err; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h index 7b18559bfe80..46953190d29e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h @@ -22,6 +22,14 @@ #define HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF 0x0108 #define HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF 0x010C +#define MAX_FUNCTION_NUM 512 + +struct vf_cmd_check_handle { + u8 cmd; + bool (*check_cmd)(struct hinic_hwdev *hwdev, u16 src_func_idx, + void *buf_in, u16 in_size); +}; + enum hinic_mbox_ack_type { MBOX_ACK, MBOX_NO_ACK, @@ -100,6 +108,10 @@ struct hinic_mbox_func_to_func { /* lock for mbox event flag */ spinlock_t mbox_lock; + + u32 vf_mbx_old_rand_id[MAX_FUNCTION_NUM]; + u32 vf_mbx_rand_id[MAX_FUNCTION_NUM]; + bool support_vf_random; }; struct hinic_mbox_work { @@ -116,6 +128,14 @@ struct vf_cmd_msg_handle { void *buf_out, u16 *out_size); }; +bool hinic_mbox_check_func_id_8B(struct hinic_hwdev *hwdev, u16 func_idx, + void *buf_in, u16 in_size); + +bool hinic_mbox_check_cmd_valid(struct hinic_hwdev *hwdev, + struct vf_cmd_check_handle *cmd_handle, + u16 vf_id, u8 cmd, void *buf_in, + u16 in_size, u8 size); + int hinic_register_pf_mbox_cb(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, hinic_pf_mbox_cb callback); @@ -151,4 +171,6 @@ int hinic_mbox_to_vf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size, u32 timeout); +int hinic_vf_mbox_random_id_init(struct hinic_hwdev *hwdev); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c index e0f5a81d8620..c6ce5966284c 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -12,8 +12,10 @@ #include <linux/semaphore.h> #include <linux/completion.h> #include <linux/slab.h> +#include <net/devlink.h> #include <asm/barrier.h> +#include "hinic_devlink.h" #include "hinic_hw_if.h" #include "hinic_hw_eqs.h" #include "hinic_hw_api_cmd.h" @@ -274,6 +276,7 @@ static int msg_to_mgmt_sync(struct hinic_pf_to_mgmt *pf_to_mgmt, if (!wait_for_completion_timeout(recv_done, timeo)) { dev_err(&pdev->dev, "MGMT timeout, MSG id = %d\n", msg_id); + hinic_dump_aeq_info(pf_to_mgmt->hwdev); err = -ETIMEDOUT; goto unlock_sync_msg; } @@ -617,10 +620,15 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, if (HINIC_IS_VF(hwif)) return 0; + err = hinic_health_reporters_create(hwdev->devlink_dev); + if (err) + return err; + sema_init(&pf_to_mgmt->sync_msg_lock, 1); pf_to_mgmt->workq = create_singlethread_workqueue("hinic_mgmt"); if (!pf_to_mgmt->workq) { dev_err(&pdev->dev, "Failed to initialize MGMT workqueue\n"); + hinic_health_reporters_destroy(hwdev->devlink_dev); return -ENOMEM; } pf_to_mgmt->sync_msg_id = 0; @@ -628,12 +636,14 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, err = alloc_msg_buf(pf_to_mgmt); if (err) { dev_err(&pdev->dev, "Failed to allocate msg buffers\n"); + hinic_health_reporters_destroy(hwdev->devlink_dev); return err; } err = hinic_api_cmd_init(pf_to_mgmt->cmd_chain, hwif); if (err) { dev_err(&pdev->dev, "Failed to initialize cmd chains\n"); + hinic_health_reporters_destroy(hwdev->devlink_dev); return err; } @@ -658,4 +668,5 @@ void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt) hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU); hinic_api_cmd_free(pf_to_mgmt->cmd_chain); destroy_workqueue(pf_to_mgmt->workq); + hinic_health_reporters_destroy(hwdev->devlink_dev); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index a824fbda59db..4ca81cc838db 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -78,11 +78,24 @@ enum hinic_comm_cmd { HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33, - HINIC_COMM_CMD_L2NIC_RESET = 0x4b, + HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP, + HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP, + + HINIC_COMM_CMD_FAULT_REPORT = 0x37, + + HINIC_COMM_CMD_SET_LED_STATUS = 0x4a, + + HINIC_COMM_CMD_L2NIC_RESET = 0x4b, HINIC_COMM_CMD_PAGESIZE_SET = 0x50, - HINIC_COMM_CMD_MAX = 0x51, + HINIC_COMM_CMD_GET_BOARD_INFO = 0x52, + + HINIC_COMM_CMD_WATCHDOG_INFO = 0x56, + + HINIC_MGMT_CMD_SET_VF_RANDOM_ID = 0x61, + + HINIC_COMM_CMD_MAX, }; enum hinic_mgmt_cb_state { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index e9e6f4c9309a..501056fd32ee 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -18,6 +18,7 @@ #include <linux/semaphore.h> #include <linux/workqueue.h> #include <net/ip.h> +#include <net/devlink.h> #include <linux/bitops.h> #include <linux/bitmap.h> #include <linux/delay.h> @@ -25,6 +26,7 @@ #include "hinic_hw_qp.h" #include "hinic_hw_dev.h" +#include "hinic_devlink.h" #include "hinic_port.h" #include "hinic_tx.h" #include "hinic_rx.h" @@ -69,6 +71,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); #define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000 +#define HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT 2 +#define HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG 32 +#define HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG 7 + static int change_mac_addr(struct net_device *netdev, const u8 *addr); static int set_features(struct hinic_dev *nic_dev, @@ -438,8 +444,11 @@ int hinic_open(struct net_device *netdev) if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) hinic_notify_all_vfs_link_changed(nic_dev->hwdev, link_state); - if (link_state == HINIC_LINK_STATE_UP) + if (link_state == HINIC_LINK_STATE_UP) { nic_dev->flags |= HINIC_LINK_UP; + nic_dev->cable_unplugged = false; + nic_dev->module_unrecognized = false; + } nic_dev->flags |= HINIC_INTF_UP; @@ -887,6 +896,26 @@ static void netdev_features_init(struct net_device *netdev) netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; } +static void hinic_refresh_nic_cfg(struct hinic_dev *nic_dev) +{ + struct hinic_nic_cfg *nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg; + struct hinic_pause_config pause_info = {0}; + struct hinic_port_cap port_cap = {0}; + + if (hinic_port_get_cap(nic_dev, &port_cap)) + return; + + mutex_lock(&nic_cfg->cfg_mutex); + if (nic_cfg->pause_set || !port_cap.autoneg_state) { + nic_cfg->auto_neg = port_cap.autoneg_state; + pause_info.auto_neg = nic_cfg->auto_neg; + pause_info.rx_pause = nic_cfg->rx_pause; + pause_info.tx_pause = nic_cfg->tx_pause; + hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info); + } + mutex_unlock(&nic_cfg->cfg_mutex); +} + /** * link_status_event_handler - link event handler * @handle: nic device for the handler @@ -909,6 +938,8 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, down(&nic_dev->mgmt_lock); nic_dev->flags |= HINIC_LINK_UP; + nic_dev->cable_unplugged = false; + nic_dev->module_unrecognized = false; if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) == (HINIC_LINK_UP | HINIC_INTF_UP)) { @@ -918,6 +949,9 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, up(&nic_dev->mgmt_lock); + if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) + hinic_refresh_nic_cfg(nic_dev); + netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n"); } else { down(&nic_dev->mgmt_lock); @@ -942,34 +976,132 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, *out_size = sizeof(*ret_link_status); } +static void cable_plug_event(void *handle, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_cable_plug_event *plug_event = buf_in; + struct hinic_dev *nic_dev = handle; + + nic_dev->cable_unplugged = plug_event->plugged ? false : true; + + *out_size = sizeof(*plug_event); + plug_event = buf_out; + plug_event->status = 0; +} + +static void link_err_event(void *handle, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_link_err_event *link_err = buf_in; + struct hinic_dev *nic_dev = handle; + + if (link_err->err_type >= LINK_ERR_NUM) + netif_info(nic_dev, link, nic_dev->netdev, + "Link failed, Unknown error type: 0x%x\n", + link_err->err_type); + else + nic_dev->module_unrecognized = true; + + *out_size = sizeof(*link_err); + link_err = buf_out; + link_err->status = 0; +} + static int set_features(struct hinic_dev *nic_dev, netdev_features_t pre_features, netdev_features_t features, bool force_change) { netdev_features_t changed = force_change ? ~0 : pre_features ^ features; u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN; + netdev_features_t failed_features = 0; + int ret = 0; int err = 0; - if (changed & NETIF_F_TSO) - err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ? + if (changed & NETIF_F_TSO) { + ret = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ? HINIC_TSO_ENABLE : HINIC_TSO_DISABLE); + if (ret) { + err = ret; + failed_features |= NETIF_F_TSO; + } + } - if (changed & NETIF_F_RXCSUM) - err = hinic_set_rx_csum_offload(nic_dev, csum_en); + if (changed & NETIF_F_RXCSUM) { + ret = hinic_set_rx_csum_offload(nic_dev, csum_en); + if (ret) { + err = ret; + failed_features |= NETIF_F_RXCSUM; + } + } if (changed & NETIF_F_LRO) { - err = hinic_set_rx_lro_state(nic_dev, + ret = hinic_set_rx_lro_state(nic_dev, !!(features & NETIF_F_LRO), HINIC_LRO_RX_TIMER_DEFAULT, HINIC_LRO_MAX_WQE_NUM_DEFAULT); + if (ret) { + err = ret; + failed_features |= NETIF_F_LRO; + } } - if (changed & NETIF_F_HW_VLAN_CTAG_RX) - err = hinic_set_rx_vlan_offload(nic_dev, + if (changed & NETIF_F_HW_VLAN_CTAG_RX) { + ret = hinic_set_rx_vlan_offload(nic_dev, !!(features & NETIF_F_HW_VLAN_CTAG_RX)); + if (ret) { + err = ret; + failed_features |= NETIF_F_HW_VLAN_CTAG_RX; + } + } - return err; + if (err) { + nic_dev->netdev->features = features ^ failed_features; + return -EIO; + } + + return 0; +} + +static int hinic_init_intr_coalesce(struct hinic_dev *nic_dev) +{ + u64 size; + u16 i; + + size = sizeof(struct hinic_intr_coal_info) * nic_dev->max_qps; + nic_dev->rx_intr_coalesce = kzalloc(size, GFP_KERNEL); + if (!nic_dev->rx_intr_coalesce) + return -ENOMEM; + nic_dev->tx_intr_coalesce = kzalloc(size, GFP_KERNEL); + if (!nic_dev->tx_intr_coalesce) { + kfree(nic_dev->rx_intr_coalesce); + return -ENOMEM; + } + + for (i = 0; i < nic_dev->max_qps; i++) { + nic_dev->rx_intr_coalesce[i].pending_limt = + HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT; + nic_dev->rx_intr_coalesce[i].coalesce_timer_cfg = + HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG; + nic_dev->rx_intr_coalesce[i].resend_timer_cfg = + HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG; + nic_dev->tx_intr_coalesce[i].pending_limt = + HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT; + nic_dev->tx_intr_coalesce[i].coalesce_timer_cfg = + HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG; + nic_dev->tx_intr_coalesce[i].resend_timer_cfg = + HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG; + } + + return 0; +} + +static void hinic_free_intr_coalesce(struct hinic_dev *nic_dev) +{ + kfree(nic_dev->tx_intr_coalesce); + kfree(nic_dev->rx_intr_coalesce); } /** @@ -986,11 +1118,19 @@ static int nic_dev_init(struct pci_dev *pdev) struct hinic_dev *nic_dev; struct net_device *netdev; struct hinic_hwdev *hwdev; + struct devlink *devlink; int err, num_qps; - hwdev = hinic_init_hwdev(pdev); + devlink = hinic_devlink_alloc(); + if (!devlink) { + dev_err(&pdev->dev, "Hinic devlink alloc failed\n"); + return -ENOMEM; + } + + hwdev = hinic_init_hwdev(pdev, devlink); if (IS_ERR(hwdev)) { dev_err(&pdev->dev, "Failed to initialize HW device\n"); + hinic_devlink_free(devlink); return PTR_ERR(hwdev); } @@ -1008,8 +1148,6 @@ static int nic_dev_init(struct pci_dev *pdev) goto err_alloc_etherdev; } - hinic_set_ethtool_ops(netdev); - if (!HINIC_IS_VF(hwdev->hwif)) netdev->netdev_ops = &hinic_netdev_ops; else @@ -1031,6 +1169,9 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->sriov_info.hwdev = hwdev; nic_dev->sriov_info.pdev = pdev; nic_dev->max_qps = num_qps; + nic_dev->devlink = devlink; + + hinic_set_ethtool_ops(netdev); sema_init(&nic_dev->mgmt_lock, 1); @@ -1095,13 +1236,30 @@ static int nic_dev_init(struct pci_dev *pdev) hinic_hwdev_cb_register(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS, nic_dev, link_status_event_handler); + hinic_hwdev_cb_register(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT, + nic_dev, cable_plug_event); + hinic_hwdev_cb_register(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT, + nic_dev, link_err_event); err = set_features(nic_dev, 0, nic_dev->netdev->features, true); if (err) goto err_set_features; + /* enable pause and disable pfc by default */ + err = hinic_dcb_set_pfc(nic_dev->hwdev, 0, 0); + if (err) + goto err_set_pfc; + SET_NETDEV_DEV(netdev, &pdev->dev); + err = hinic_init_intr_coalesce(nic_dev); + if (err) { + dev_err(&pdev->dev, "Failed to init_intr_coalesce\n"); + goto err_init_intr; + } + err = register_netdev(netdev); if (err) { dev_err(&pdev->dev, "Failed to register netdev\n"); @@ -1111,17 +1269,24 @@ static int nic_dev_init(struct pci_dev *pdev) return 0; err_reg_netdev: + hinic_free_intr_coalesce(nic_dev); +err_init_intr: +err_set_pfc: err_set_features: hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT); + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT); + hinic_hwdev_cb_unregister(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS); cancel_work_sync(&rx_mode_work->work); err_set_mtu: -err_get_mac: + hinic_port_del_mac(nic_dev, netdev->dev_addr, 0); err_add_mac: +err_get_mac: pci_set_drvdata(pdev, NULL); destroy_workqueue(nic_dev->workq); - err_workq: err_vlan_bitmap: free_netdev(netdev); @@ -1129,6 +1294,7 @@ err_vlan_bitmap: err_alloc_etherdev: err_num_qps: hinic_free_hwdev(hwdev); + hinic_devlink_free(devlink); return err; } @@ -1215,6 +1381,7 @@ static void hinic_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct hinic_dev *nic_dev = netdev_priv(netdev); + struct devlink *devlink = nic_dev->devlink; struct hinic_rx_mode_work *rx_mode_work; if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) { @@ -1224,9 +1391,15 @@ static void hinic_remove(struct pci_dev *pdev) unregister_netdev(netdev); + hinic_free_intr_coalesce(nic_dev); + hinic_port_del_mac(nic_dev, netdev->dev_addr, 0); hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_ERR_EVENT); + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_CABLE_PLUG_EVENT); + hinic_hwdev_cb_unregister(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS); rx_mode_work = &nic_dev->rx_mode_work; @@ -1236,12 +1409,12 @@ static void hinic_remove(struct pci_dev *pdev) destroy_workqueue(nic_dev->workq); - hinic_vf_func_free(nic_dev->hwdev); - hinic_free_hwdev(nic_dev->hwdev); free_netdev(netdev); + hinic_devlink_free(devlink); + pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 175c0ee00038..02cd635d6914 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -61,8 +61,8 @@ static int change_mac(struct hinic_dev *nic_dev, const u8 *addr, (port_mac_cmd.status && port_mac_cmd.status != HINIC_PF_SET_VF_ALREADY && port_mac_cmd.status != HINIC_MGMT_STATUS_EXIST)) { - dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n", - port_mac_cmd.status); + dev_err(&pdev->dev, "Failed to change MAC, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_mac_cmd.status, out_size); return -EFAULT; } @@ -129,8 +129,8 @@ int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr) &port_mac_cmd, sizeof(port_mac_cmd), &port_mac_cmd, &out_size); if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) { - dev_err(&pdev->dev, "Failed to get mac, ret = %d\n", - port_mac_cmd.status); + dev_err(&pdev->dev, "Failed to get mac, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_mac_cmd.status, out_size); return -EFAULT; } @@ -172,9 +172,9 @@ int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu) err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU, &port_mtu_cmd, sizeof(port_mtu_cmd), &port_mtu_cmd, &out_size); - if (err || (out_size != sizeof(port_mtu_cmd)) || port_mtu_cmd.status) { - dev_err(&pdev->dev, "Failed to set mtu, ret = %d\n", - port_mtu_cmd.status); + if (err || out_size != sizeof(port_mtu_cmd) || port_mtu_cmd.status) { + dev_err(&pdev->dev, "Failed to set mtu, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_mtu_cmd.status, out_size); return -EFAULT; } @@ -264,8 +264,8 @@ int hinic_port_link_state(struct hinic_dev *nic_dev, &link_cmd, sizeof(link_cmd), &link_cmd, &out_size); if (err || (out_size != sizeof(link_cmd)) || link_cmd.status) { - dev_err(&pdev->dev, "Failed to get link state, ret = %d\n", - link_cmd.status); + dev_err(&pdev->dev, "Failed to get link state, err: %d, status: 0x%x, out size: 0x%x\n", + err, link_cmd.status, out_size); return -EINVAL; } @@ -298,8 +298,8 @@ int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state) &port_state, sizeof(port_state), &port_state, &out_size); if (err || (out_size != sizeof(port_state)) || port_state.status) { - dev_err(&pdev->dev, "Failed to set port state, ret = %d\n", - port_state.status); + dev_err(&pdev->dev, "Failed to set port state, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_state.status, out_size); return -EFAULT; } @@ -330,8 +330,8 @@ int hinic_port_set_func_state(struct hinic_dev *nic_dev, &func_state, sizeof(func_state), &func_state, &out_size); if (err || (out_size != sizeof(func_state)) || func_state.status) { - dev_err(&pdev->dev, "Failed to set port func state, ret = %d\n", - func_state.status); + dev_err(&pdev->dev, "Failed to set port func state, err: %d, status: 0x%x, out size: 0x%x\n", + err, func_state.status, out_size); return -EFAULT; } @@ -361,9 +361,9 @@ int hinic_port_get_cap(struct hinic_dev *nic_dev, port_cap, &out_size); if (err || (out_size != sizeof(*port_cap)) || port_cap->status) { dev_err(&pdev->dev, - "Failed to get port capabilities, ret = %d\n", - port_cap->status); - return -EINVAL; + "Failed to get port capabilities, err: %d, status: 0x%x, out size: 0x%x\n", + err, port_cap->status, out_size); + return -EIO; } return 0; @@ -393,9 +393,9 @@ int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state) &tso_cfg, &out_size); if (err || out_size != sizeof(tso_cfg) || tso_cfg.status) { dev_err(&pdev->dev, - "Failed to set port tso, ret = %d\n", - tso_cfg.status); - return -EINVAL; + "Failed to set port tso, err: %d, status: 0x%x, out size: 0x%x\n", + err, tso_cfg.status, out_size); + return -EIO; } return 0; @@ -423,9 +423,9 @@ int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en) &rx_csum_cfg, &out_size); if (err || !out_size || rx_csum_cfg.status) { dev_err(&pdev->dev, - "Failed to set rx csum offload, ret = %d\n", - rx_csum_cfg.status); - return -EINVAL; + "Failed to set rx csum offload, err: %d, status: 0x%x, out size: 0x%x\n", + err, rx_csum_cfg.status, out_size); + return -EIO; } return 0; @@ -480,9 +480,9 @@ int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs) &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; + "Failed to set rxq number, err: %d, status: 0x%x, out size: 0x%x\n", + err, rq_num.status, out_size); + return -EIO; } return 0; @@ -508,9 +508,9 @@ static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en, &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; + "Failed to set lro offload, err: %d, status: 0x%x, out size: 0x%x\n", + err, lro_cfg.status, out_size); + return -EIO; } return 0; @@ -542,10 +542,10 @@ static int hinic_set_rx_lro_timer(struct hinic_dev *nic_dev, u32 timer_value) if (err || !out_size || lro_timer.status) { dev_err(&pdev->dev, - "Failed to set lro timer, ret = %d\n", - lro_timer.status); + "Failed to set lro timer, err: %d, status: 0x%x, out size: 0x%x\n", + err, lro_timer.status, out_size); - return -EINVAL; + return -EIO; } return 0; @@ -1082,6 +1082,7 @@ int hinic_get_link_mode(struct hinic_hwdev *hwdev, if (!hwdev || !link_mode) return -EINVAL; + link_mode->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); out_size = sizeof(*link_mode); err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_MODE, @@ -1172,6 +1173,8 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev, u16 out_size = sizeof(*pause_info); int err; + pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO, pause_info, sizeof(*pause_info), pause_info, &out_size); @@ -1190,6 +1193,8 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, u16 out_size = sizeof(*pause_info); int err; + pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO, pause_info, sizeof(*pause_info), pause_info, &out_size); @@ -1201,3 +1206,192 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, return 0; } + +int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap) +{ + struct hinic_nic_cfg *nic_cfg = &hwdev->func_to_io.nic_cfg; + struct hinic_set_pfc pfc = {0}; + u16 out_size = sizeof(pfc); + int err; + + if (HINIC_IS_VF(hwdev->hwif)) + return 0; + + mutex_lock(&nic_cfg->cfg_mutex); + + pfc.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + pfc.pfc_bitmap = pfc_bitmap; + pfc.pfc_en = pfc_en; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PFC, + &pfc, sizeof(pfc), &pfc, &out_size); + if (err || pfc.status || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to %s pfc, err: %d, status: 0x%x, out size: 0x%x\n", + pfc_en ? "enable" : "disable", err, pfc.status, + out_size); + mutex_unlock(&nic_cfg->cfg_mutex); + return -EIO; + } + + /* pause settings is opposite from pfc */ + nic_cfg->rx_pause = pfc_en ? 0 : 1; + nic_cfg->tx_pause = pfc_en ? 0 : 1; + + mutex_unlock(&nic_cfg->cfg_mutex); + + return 0; +} + +int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable) +{ + struct hinic_port_loopback lb = {0}; + u16 out_size = sizeof(lb); + int err; + + lb.mode = mode; + lb.en = enable; + + if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) { + dev_err(&hwdev->hwif->pdev->dev, + "Invalid loopback mode %d to set\n", mode); + return -EINVAL; + } + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE, + &lb, sizeof(lb), &lb, &out_size); + if (err || !out_size || lb.status) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n", + mode, enable, err, lb.status, out_size); + return -EIO; + } + + return 0; +} + +static int _set_led_status(struct hinic_hwdev *hwdev, u8 port, + enum hinic_led_type type, + enum hinic_led_mode mode, u8 reset) +{ + struct hinic_led_info led_info = {0}; + u16 out_size = sizeof(led_info); + struct hinic_pfhwdev *pfhwdev; + int err; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + led_info.port = port; + led_info.reset = reset; + + led_info.type = type; + led_info.mode = mode; + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_SET_LED_STATUS, + &led_info, sizeof(led_info), + &led_info, &out_size, HINIC_MGMT_MSG_SYNC); + if (err || led_info.status || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to set led status, err: %d, status: 0x%x, out size: 0x%x\n", + err, led_info.status, out_size); + return -EIO; + } + + return 0; +} + +int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port, + enum hinic_led_type type, enum hinic_led_mode mode) +{ + if (!hwdev) + return -EINVAL; + + return _set_led_status(hwdev, port, type, mode, 0); +} + +int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port) +{ + int err; + + if (!hwdev) + return -EINVAL; + + err = _set_led_status(hwdev, port, HINIC_LED_TYPE_INVALID, + HINIC_LED_MODE_INVALID, 1); + if (err) + dev_err(&hwdev->hwif->pdev->dev, + "Failed to reset led status\n"); + + return err; +} + +static bool hinic_if_sfp_absent(struct hinic_hwdev *hwdev) +{ + struct hinic_cmd_get_light_module_abs sfp_abs = {0}; + u16 out_size = sizeof(sfp_abs); + u8 port_id = hwdev->port_id; + int err; + + sfp_abs.port_id = port_id; + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_SFP_ABS, + &sfp_abs, sizeof(sfp_abs), &sfp_abs, + &out_size); + if (sfp_abs.status || err || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to get port%d sfp absent status, err: %d, status: 0x%x, out size: 0x%x\n", + port_id, err, sfp_abs.status, out_size); + return true; + } + + return ((sfp_abs.abs_status == 0) ? false : true); +} + +int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len) +{ + struct hinic_cmd_get_std_sfp_info sfp_info = {0}; + u16 out_size = sizeof(sfp_info); + u8 port_id; + int err; + + if (!hwdev || !data || !len) + return -EINVAL; + + port_id = hwdev->port_id; + + if (hinic_if_sfp_absent(hwdev)) + return -ENXIO; + + sfp_info.port_id = port_id; + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_STD_SFP_INFO, + &sfp_info, sizeof(sfp_info), &sfp_info, + &out_size); + if (sfp_info.status || err || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to get port%d sfp eeprom information, err: %d, status: 0x%x, out size: 0x%x\n", + port_id, err, sfp_info.status, out_size); + return -EIO; + } + + *len = min_t(u16, sfp_info.eeprom_len, STD_SFP_INFO_MAX_SIZE); + memcpy(data, sfp_info.sfp_info, STD_SFP_INFO_MAX_SIZE); + + return 0; +} + +int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1) +{ + u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; + u16 len; + int err; + + if (hinic_if_sfp_absent(hwdev)) + return -ENXIO; + + err = hinic_get_sfp_eeprom(hwdev, sfp_data, &len); + if (err) + return err; + + *data0 = sfp_data[0]; + *data1 = sfp_data[1]; + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 661c6322dc15..9c3cbe45c9ec 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -189,6 +189,31 @@ struct hinic_port_link_status { u8 port_id; }; +struct hinic_cable_plug_event { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 plugged; /* 0: unplugged, 1: plugged */ + u8 port_id; +}; + +enum link_err_type { + LINK_ERR_MODULE_UNRECOGENIZED, + LINK_ERR_NUM, +}; + +struct hinic_link_err_event { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 err_type; + u8 port_id; +}; + struct hinic_port_func_state_cmd { u8 status; u8 version; @@ -641,6 +666,93 @@ struct hinic_pause_config { u32 tx_pause; }; +struct hinic_set_pfc { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u8 pfc_en; + u8 pfc_bitmap; + u8 rsvd1[4]; +}; + +/* get or set loopback mode, need to modify by base API */ +#define HINIC_INTERNAL_LP_MODE 5 +#define LOOP_MODE_MIN 1 +#define LOOP_MODE_MAX 6 + +struct hinic_port_loopback { + u8 status; + u8 version; + u8 rsvd[6]; + + u32 mode; + u32 en; +}; + +struct hinic_led_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 port; + u8 type; + u8 mode; + u8 reset; +}; + +#define STD_SFP_INFO_MAX_SIZE 640 + +struct hinic_cmd_get_light_module_abs { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 port_id; + u8 abs_status; /* 0:present, 1:absent */ + u8 rsv[2]; +}; + +#define STD_SFP_INFO_MAX_SIZE 640 + +struct hinic_cmd_get_std_sfp_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 port_id; + u8 wire_type; + u16 eeprom_len; + u32 rsvd; + u8 sfp_info[STD_SFP_INFO_MAX_SIZE]; +}; + +struct hinic_cmd_update_fw { + u8 status; + u8 version; + u8 rsvd0[6]; + + struct { + u32 SL:1; + u32 SF:1; + u32 flag:1; + u32 reserved:13; + u32 fragment_len:16; + } ctl_info; + + struct { + u32 FW_section_CRC; + u32 FW_section_type; + } section_info; + + u32 total_len; + u32 setion_total_len; + u32 fw_section_version; + u32 section_offset; + u32 data[384]; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -736,6 +848,38 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev, int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, struct hinic_pause_config *pause_info); +int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap); + +int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable); + +enum hinic_led_mode { + HINIC_LED_MODE_ON, + HINIC_LED_MODE_OFF, + HINIC_LED_MODE_FORCE_1HZ, + HINIC_LED_MODE_FORCE_2HZ, + HINIC_LED_MODE_FORCE_4HZ, + HINIC_LED_MODE_1HZ, + HINIC_LED_MODE_2HZ, + HINIC_LED_MODE_4HZ, + HINIC_LED_MODE_INVALID, +}; + +enum hinic_led_type { + HINIC_LED_TYPE_LINK, + HINIC_LED_TYPE_LOW_SPEED, + HINIC_LED_TYPE_HIGH_SPEED, + HINIC_LED_TYPE_INVALID, +}; + +int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port); + +int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port, + enum hinic_led_type type, enum hinic_led_mode mode); + +int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1); + +int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len); + int hinic_open(struct net_device *netdev); int hinic_close(struct net_device *netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c index af20d0dd6de7..5bee951fe9d4 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_rx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -316,6 +316,39 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb, return num_wqes; } +static void hinic_copy_lp_data(struct hinic_dev *nic_dev, + struct sk_buff *skb) +{ + struct net_device *netdev = nic_dev->netdev; + u8 *lb_buf = nic_dev->lb_test_rx_buf; + int lb_len = nic_dev->lb_pkt_len; + int pkt_offset, frag_len, i; + void *frag_data = NULL; + + if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) { + nic_dev->lb_test_rx_idx = 0; + netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n"); + } + + if (skb->len != nic_dev->lb_pkt_len) { + netif_warn(nic_dev, drv, netdev, "Wrong packet length\n"); + nic_dev->lb_test_rx_idx++; + return; + } + + pkt_offset = nic_dev->lb_test_rx_idx * lb_len; + frag_len = (int)skb_headlen(skb); + memcpy(lb_buf + pkt_offset, skb->data, frag_len); + pkt_offset += frag_len; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]); + frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]); + memcpy((lb_buf + pkt_offset), frag_data, frag_len); + pkt_offset += frag_len; + } + nic_dev->lb_test_rx_idx++; +} + /** * rxq_recv - Rx handler * @rxq: rx queue @@ -330,6 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) u64 pkt_len = 0, rx_bytes = 0; struct hinic_rq *rq = rxq->rq; struct hinic_rq_wqe *rq_wqe; + struct hinic_dev *nic_dev; unsigned int free_wqebbs; struct hinic_rq_cqe *cqe; int num_wqes, pkts = 0; @@ -342,6 +376,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) u32 vlan_len; u16 vid; + nic_dev = netdev_priv(netdev); + while (pkts < budget) { num_wqes = 0; @@ -384,6 +420,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); } + if (unlikely(nic_dev->flags & HINIC_LP_TEST)) + hinic_copy_lp_data(nic_dev, skb); + skb_record_rx_queue(skb, qp->q_id); skb->protocol = eth_type_trans(skb, rxq->netdev); @@ -478,11 +517,15 @@ static irqreturn_t rx_irq(int irq, void *data) static int rx_request_irq(struct hinic_rxq *rxq) { struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_msix_config interrupt_info = {0}; + struct hinic_intr_coal_info *intr_coal = NULL; struct hinic_hwdev *hwdev = nic_dev->hwdev; struct hinic_rq *rq = rxq->rq; struct hinic_qp *qp; int err; + qp = container_of(rq, struct hinic_qp, rq); + rx_add_napi(rxq); hinic_hwdev_msix_set(hwdev, rq->msix_entry, @@ -490,13 +533,26 @@ static int rx_request_irq(struct hinic_rxq *rxq) RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT, RX_IRQ_NO_RESEND_TIMER); + intr_coal = &nic_dev->rx_intr_coalesce[qp->q_id]; + interrupt_info.msix_index = rq->msix_entry; + interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; + interrupt_info.pending_cnt = intr_coal->pending_limt; + interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; + + err = hinic_set_interrupt_cfg(hwdev, &interrupt_info); + if (err) { + netif_err(nic_dev, drv, rxq->netdev, + "Failed to set RX interrupt coalescing attribute\n"); + rx_del_napi(rxq); + return err; + } + err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq); if (err) { rx_del_napi(rxq); return err; } - qp = container_of(rq, struct hinic_qp, rq); cpumask_set_cpu(qp->q_id % num_online_cpus(), &rq->affinity_mask); return irq_set_affinity_hint(rq->irq, &rq->affinity_mask); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c index efab2dd2c889..4d63680f2143 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c @@ -40,9 +40,9 @@ static int hinic_set_mac(struct hinic_hwdev *hwdev, const u8 *mac_addr, if (err || out_size != sizeof(mac_info) || (mac_info.status && mac_info.status != HINIC_PF_SET_VF_ALREADY && mac_info.status != HINIC_MGMT_STATUS_EXIST)) { - dev_err(&hwdev->func_to_io.hwif->pdev->dev, "Failed to change MAC, ret = %d\n", - mac_info.status); - return -EFAULT; + dev_err(&hwdev->func_to_io.hwif->pdev->dev, "Failed to set MAC, err: %d, status: 0x%x, out size: 0x%x\n", + err, mac_info.status, out_size); + return -EIO; } return 0; @@ -383,7 +383,7 @@ static int hinic_del_vf_mac_msg_handler(void *hwdev, u16 vf_id, nic_io = &hw_dev->func_to_io; vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id); - if (vf_info->pf_set_mac && is_valid_ether_addr(mac_in->mac) && + if (vf_info->pf_set_mac && is_valid_ether_addr(mac_in->mac) && !memcmp(vf_info->vf_mac_addr, mac_in->mac, ETH_ALEN)) { dev_warn(&hw_dev->hwif->pdev->dev, "PF has already set VF mac.\n"); mac_out->status = HINIC_PF_SET_VF_ALREADY; @@ -429,6 +429,18 @@ static int hinic_get_vf_link_status_msg_handler(void *hwdev, u16 vf_id, return 0; } +static bool check_func_table(struct hinic_hwdev *hwdev, u16 func_idx, + void *buf_in, u16 in_size) +{ + struct hinic_cmd_fw_ctxt *function_table = buf_in; + + if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size) || + !function_table->rx_buf_sz) + return false; + + return true; +} + static struct vf_cmd_msg_handle nic_vf_cmd_msg_handler[] = { {HINIC_PORT_CMD_VF_REGISTER, hinic_register_vf_msg_handler}, {HINIC_PORT_CMD_VF_UNREGISTER, hinic_unregister_vf_msg_handler}, @@ -439,6 +451,45 @@ static struct vf_cmd_msg_handle nic_vf_cmd_msg_handler[] = { {HINIC_PORT_CMD_GET_LINK_STATE, hinic_get_vf_link_status_msg_handler}, }; +static struct vf_cmd_check_handle nic_cmd_support_vf[] = { + {HINIC_PORT_CMD_VF_REGISTER, NULL}, + {HINIC_PORT_CMD_VF_UNREGISTER, NULL}, + {HINIC_PORT_CMD_CHANGE_MTU, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_ADD_VLAN, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_DEL_VLAN, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_MAC, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_MAC, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_DEL_MAC, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RX_MODE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_PAUSE_INFO, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_LINK_STATE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_LRO, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RX_CSUM, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_VPORT_STAT, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_CLEAN_VPORT_STAT, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL, + hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_RSS_CTX_TBL, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RSS_CTX_TBL, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_RSS_TEMP_MGR, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_RSS_CFG, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_FWCTXT_INIT, check_func_table}, + {HINIC_PORT_CMD_GET_MGMT_VERSION, NULL}, + {HINIC_PORT_CMD_SET_FUNC_STATE, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_GLOBAL_QPN, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_TSO, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_SET_RQ_IQ_MAP, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_LINK_STATUS_REPORT, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_UPDATE_MAC, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_CAP, hinic_mbox_check_func_id_8B}, + {HINIC_PORT_CMD_GET_LINK_MODE, hinic_mbox_check_func_id_8B}, +}; + #define CHECK_IPSU_15BIT 0X8000 static @@ -905,7 +956,6 @@ int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting) err = hinic_set_vf_spoofchk(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), setting); - if (!err) { netif_info(nic_dev, drv, netdev, "Set VF %d spoofchk %s successfully\n", vf, setting ? "on" : "off"); @@ -973,6 +1023,7 @@ int hinic_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) static int nic_pf_mbox_handler(void *hwdev, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) { + u8 size = ARRAY_SIZE(nic_cmd_support_vf); struct vf_cmd_msg_handle *vf_msg_handle; struct hinic_hwdev *dev = hwdev; struct hinic_func_to_io *nic_io; @@ -981,7 +1032,15 @@ static int nic_pf_mbox_handler(void *hwdev, u16 vf_id, u8 cmd, void *buf_in, u32 i; if (!hwdev) - return -EFAULT; + return -EINVAL; + + if (!hinic_mbox_check_cmd_valid(hwdev, nic_cmd_support_vf, vf_id, cmd, + buf_in, in_size, size)) { + dev_err(&dev->hwif->pdev->dev, + "PF Receive VF nic cmd: 0x%x, mbox len: 0x%x is invalid\n", + cmd, in_size); + return HINIC_MBOX_VF_CMD_ERROR; + } pfhwdev = container_of(dev, struct hinic_pfhwdev, hwdev); nic_io = &dev->func_to_io; @@ -1020,6 +1079,7 @@ static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in, dev_cap->max_vf = cap->max_vf; dev_cap->max_sqs = cap->max_vf_qps; dev_cap->max_rqs = cap->max_vf_qps; + dev_cap->port_id = dev->port_id; *out_size = sizeof(*dev_cap); @@ -1060,9 +1120,7 @@ static int hinic_init_vf_infos(struct hinic_func_to_io *nic_io, u16 vf_id) static void hinic_clear_vf_infos(struct hinic_dev *nic_dev, u16 vf_id) { struct vf_data_storage *vf_infos; - u16 func_id; - func_id = hinic_glb_pf_vf_offset(nic_dev->hwdev->hwif) + vf_id; vf_infos = nic_dev->hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id); if (vf_infos->pf_set_mac) hinic_port_del_mac(nic_dev, vf_infos->vf_mac_addr, 0); @@ -1110,6 +1168,13 @@ int hinic_vf_func_init(struct hinic_hwdev *hwdev) int err = 0; u32 size, i; + err = hinic_vf_mbox_random_id_init(hwdev); + if (err) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to init vf mbox random id, err: %d\n", + err); + return err; + } + nic_io = &hwdev->func_to_io; if (HINIC_IS_VF(hwdev->hwif)) { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c index 4c66a0bc1b28..a97498ee6914 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -459,6 +459,67 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task, return 0; } +netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 prod_idx, q_id = skb->queue_mapping; + struct netdev_queue *netdev_txq; + int nr_sges, err = NETDEV_TX_OK; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + struct hinic_txq *txq; + struct hinic_qp *qp; + + txq = &nic_dev->txqs[q_id]; + qp = container_of(txq->sq, struct hinic_qp, sq); + nr_sges = skb_shinfo(skb)->nr_frags + 1; + + err = tx_map_skb(nic_dev, skb, txq->sges); + if (err) + goto skb_error; + + wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); + + sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); + if (!sq_wqe) { + netif_stop_subqueue(netdev, qp->q_id); + + sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); + if (sq_wqe) { + netif_wake_subqueue(nic_dev->netdev, qp->q_id); + goto process_sq_wqe; + } + + tx_unmap_skb(nic_dev, skb, txq->sges); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_busy++; + u64_stats_update_end(&txq->txq_stats.syncp); + err = NETDEV_TX_BUSY; + wqe_size = 0; + goto flush_skbs; + } + +process_sq_wqe: + hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); + hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); + +flush_skbs: + netdev_txq = netdev_get_tx_queue(netdev, q_id); + if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq))) + hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); + + return err; + +skb_error: + dev_kfree_skb_any(skb); + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_dropped++; + u64_stats_update_end(&txq->txq_stats.syncp); + + return NETDEV_TX_OK; +} + netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct hinic_dev *nic_dev = netdev_priv(netdev); @@ -718,12 +779,17 @@ static irqreturn_t tx_irq(int irq, void *data) static int tx_request_irq(struct hinic_txq *txq) { struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct hinic_msix_config interrupt_info = {0}; + struct hinic_intr_coal_info *intr_coal = NULL; struct hinic_hwdev *hwdev = nic_dev->hwdev; struct hinic_hwif *hwif = hwdev->hwif; struct pci_dev *pdev = hwif->pdev; struct hinic_sq *sq = txq->sq; + struct hinic_qp *qp; int err; + qp = container_of(sq, struct hinic_qp, sq); + tx_napi_add(txq, nic_dev->tx_weight); hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry, @@ -731,6 +797,20 @@ static int tx_request_irq(struct hinic_txq *txq) TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT, TX_IRQ_NO_RESEND_TIMER); + intr_coal = &nic_dev->tx_intr_coalesce[qp->q_id]; + interrupt_info.msix_index = sq->msix_entry; + interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; + interrupt_info.pending_cnt = intr_coal->pending_limt; + interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; + + err = hinic_set_interrupt_cfg(hwdev, &interrupt_info); + if (err) { + netif_err(nic_dev, drv, txq->netdev, + "Failed to set TX interrupt coalescing attribute\n"); + tx_napi_del(txq); + return err; + } + err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq); if (err) { dev_err(&pdev->dev, "Failed to request Tx irq\n"); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h index f158b7db7fb8..b3c8657774a7 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_tx.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -44,6 +44,8 @@ void hinic_txq_clean_stats(struct hinic_txq *txq); void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats); +netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev); + netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, |