diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/dpdk/drivers/net/hns3 | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/dpdk/drivers/net/hns3')
28 files changed, 22639 insertions, 0 deletions
diff --git a/src/spdk/dpdk/drivers/net/hns3/Makefile b/src/spdk/dpdk/drivers/net/hns3/Makefile new file mode 100644 index 000000000..d7798a470 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/Makefile @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018-2019 Hisilicon Limited. + +include $(RTE_SDK)/mk/rte.vars.mk + +# +# library name +# +LIB = librte_pmd_hns3.a + +CFLAGS += -O3 +CFLAGS += $(WERROR_FLAGS) +# Experimantal APIs: +# - rte_mp_action_register +# - rte_mp_action_unregister +# - rte_mp_reply +# - rte_mp_request_sync + +LDLIBS += -lrte_eal -lrte_mbuf -lrte_mempool -lrte_ring +LDLIBS += -lrte_ethdev -lrte_net -lrte_hash +LDLIBS += -lrte_bus_pci + +EXPORT_MAP := rte_pmd_hns3_version.map + +# +# all source are stored in SRCS-y +# +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_ethdev.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_ethdev_vf.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_cmd.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_mbx.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_rxtx.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_rss.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_flow.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_fdir.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_intr.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_stats.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_regs.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_dcb.c +SRCS-$(CONFIG_RTE_LIBRTE_HNS3_PMD) += hns3_mp.c + +include $(RTE_SDK)/mk/rte.lib.mk diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_cmd.c b/src/spdk/dpdk/drivers/net/hns3/hns3_cmd.c new file mode 100644 index 000000000..cbb09887c --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_cmd.c @@ -0,0 +1,572 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/queue.h> +#include <inttypes.h> +#include <unistd.h> +#include <rte_bus_pci.h> +#include <rte_common.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_eal.h> +#include <rte_ether.h> +#include <rte_ethdev_driver.h> +#include <rte_ethdev_pci.h> +#include <rte_io.h> + +#include "hns3_ethdev.h" +#include "hns3_regs.h" +#include "hns3_intr.h" +#include "hns3_logs.h" + +#define hns3_is_csq(ring) ((ring)->flag & HNS3_TYPE_CSQ) + +#define cmq_ring_to_dev(ring) (&(ring)->dev->pdev->dev) + +static int +hns3_ring_space(struct hns3_cmq_ring *ring) +{ + int ntu = ring->next_to_use; + int ntc = ring->next_to_clean; + int used = (ntu - ntc + ring->desc_num) % ring->desc_num; + + return ring->desc_num - used - 1; +} + +static bool +is_valid_csq_clean_head(struct hns3_cmq_ring *ring, int head) +{ + int ntu = ring->next_to_use; + int ntc = ring->next_to_clean; + + if (ntu > ntc) + return head >= ntc && head <= ntu; + + return head >= ntc || head <= ntu; +} + +/* + * hns3_allocate_dma_mem - Specific memory alloc for command function. + * Malloc a memzone, which is a contiguous portion of physical memory identified + * by a name. + * @ring: pointer to the ring structure + * @size: size of memory requested + * @alignment: what to align the allocation to + */ +static int +hns3_allocate_dma_mem(struct hns3_hw *hw, struct hns3_cmq_ring *ring, + uint64_t size, uint32_t alignment) +{ + const struct rte_memzone *mz = NULL; + char z_name[RTE_MEMZONE_NAMESIZE]; + + snprintf(z_name, sizeof(z_name), "hns3_dma_%" PRIu64, rte_rand()); + mz = rte_memzone_reserve_bounded(z_name, size, SOCKET_ID_ANY, + RTE_MEMZONE_IOVA_CONTIG, alignment, + RTE_PGSIZE_2M); + if (mz == NULL) + return -ENOMEM; + + ring->buf_size = size; + ring->desc = mz->addr; + ring->desc_dma_addr = mz->iova; + ring->zone = (const void *)mz; + hns3_dbg(hw, "memzone %s allocated with physical address: %" PRIu64, + mz->name, ring->desc_dma_addr); + + return 0; +} + +static void +hns3_free_dma_mem(struct hns3_hw *hw, struct hns3_cmq_ring *ring) +{ + hns3_dbg(hw, "memzone %s to be freed with physical address: %" PRIu64, + ((const struct rte_memzone *)ring->zone)->name, + ring->desc_dma_addr); + rte_memzone_free((const struct rte_memzone *)ring->zone); + ring->buf_size = 0; + ring->desc = NULL; + ring->desc_dma_addr = 0; + ring->zone = NULL; +} + +static int +hns3_alloc_cmd_desc(struct hns3_hw *hw, struct hns3_cmq_ring *ring) +{ + int size = ring->desc_num * sizeof(struct hns3_cmd_desc); + + if (hns3_allocate_dma_mem(hw, ring, size, HNS3_CMD_DESC_ALIGNMENT)) { + hns3_err(hw, "allocate dma mem failed"); + return -ENOMEM; + } + + return 0; +} + +static void +hns3_free_cmd_desc(struct hns3_hw *hw, struct hns3_cmq_ring *ring) +{ + if (ring->desc) + hns3_free_dma_mem(hw, ring); +} + +static int +hns3_alloc_cmd_queue(struct hns3_hw *hw, int ring_type) +{ + struct hns3_cmq_ring *ring = + (ring_type == HNS3_TYPE_CSQ) ? &hw->cmq.csq : &hw->cmq.crq; + int ret; + + ring->ring_type = ring_type; + ring->hw = hw; + + ret = hns3_alloc_cmd_desc(hw, ring); + if (ret) + hns3_err(hw, "descriptor %s alloc error %d", + (ring_type == HNS3_TYPE_CSQ) ? "CSQ" : "CRQ", ret); + + return ret; +} + +void +hns3_cmd_reuse_desc(struct hns3_cmd_desc *desc, bool is_read) +{ + desc->flag = rte_cpu_to_le_16(HNS3_CMD_FLAG_NO_INTR | HNS3_CMD_FLAG_IN); + if (is_read) + desc->flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_WR); + else + desc->flag &= rte_cpu_to_le_16(~HNS3_CMD_FLAG_WR); +} + +void +hns3_cmd_setup_basic_desc(struct hns3_cmd_desc *desc, + enum hns3_opcode_type opcode, bool is_read) +{ + memset((void *)desc, 0, sizeof(struct hns3_cmd_desc)); + desc->opcode = rte_cpu_to_le_16(opcode); + desc->flag = rte_cpu_to_le_16(HNS3_CMD_FLAG_NO_INTR | HNS3_CMD_FLAG_IN); + + if (is_read) + desc->flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_WR); +} + +static void +hns3_cmd_clear_regs(struct hns3_hw *hw) +{ + hns3_write_dev(hw, HNS3_CMDQ_TX_ADDR_L_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_TX_ADDR_H_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_TX_DEPTH_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_TX_HEAD_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_TX_TAIL_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_RX_ADDR_L_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_RX_ADDR_H_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_RX_DEPTH_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_RX_HEAD_REG, 0); + hns3_write_dev(hw, HNS3_CMDQ_RX_TAIL_REG, 0); +} + +static void +hns3_cmd_config_regs(struct hns3_cmq_ring *ring) +{ + uint64_t dma = ring->desc_dma_addr; + + if (ring->ring_type == HNS3_TYPE_CSQ) { + hns3_write_dev(ring->hw, HNS3_CMDQ_TX_ADDR_L_REG, + lower_32_bits(dma)); + hns3_write_dev(ring->hw, HNS3_CMDQ_TX_ADDR_H_REG, + upper_32_bits(dma)); + hns3_write_dev(ring->hw, HNS3_CMDQ_TX_DEPTH_REG, + ring->desc_num >> HNS3_NIC_CMQ_DESC_NUM_S | + HNS3_NIC_SW_RST_RDY); + hns3_write_dev(ring->hw, HNS3_CMDQ_TX_HEAD_REG, 0); + hns3_write_dev(ring->hw, HNS3_CMDQ_TX_TAIL_REG, 0); + } else { + hns3_write_dev(ring->hw, HNS3_CMDQ_RX_ADDR_L_REG, + lower_32_bits(dma)); + hns3_write_dev(ring->hw, HNS3_CMDQ_RX_ADDR_H_REG, + upper_32_bits(dma)); + hns3_write_dev(ring->hw, HNS3_CMDQ_RX_DEPTH_REG, + ring->desc_num >> HNS3_NIC_CMQ_DESC_NUM_S); + hns3_write_dev(ring->hw, HNS3_CMDQ_RX_HEAD_REG, 0); + hns3_write_dev(ring->hw, HNS3_CMDQ_RX_TAIL_REG, 0); + } +} + +static void +hns3_cmd_init_regs(struct hns3_hw *hw) +{ + hns3_cmd_config_regs(&hw->cmq.csq); + hns3_cmd_config_regs(&hw->cmq.crq); +} + +static int +hns3_cmd_csq_clean(struct hns3_hw *hw) +{ + struct hns3_cmq_ring *csq = &hw->cmq.csq; + uint32_t head; + int clean; + + head = hns3_read_dev(hw, HNS3_CMDQ_TX_HEAD_REG); + + if (!is_valid_csq_clean_head(csq, head)) { + hns3_err(hw, "wrong cmd head (%u, %u-%u)", head, + csq->next_to_use, csq->next_to_clean); + if (rte_eal_process_type() == RTE_PROC_PRIMARY) { + rte_atomic16_set(&hw->reset.disable_cmd, 1); + hns3_schedule_delayed_reset(HNS3_DEV_HW_TO_ADAPTER(hw)); + } + + return -EIO; + } + + clean = (head - csq->next_to_clean + csq->desc_num) % csq->desc_num; + csq->next_to_clean = head; + return clean; +} + +static int +hns3_cmd_csq_done(struct hns3_hw *hw) +{ + uint32_t head = hns3_read_dev(hw, HNS3_CMDQ_TX_HEAD_REG); + + return head == hw->cmq.csq.next_to_use; +} + +static bool +hns3_is_special_opcode(uint16_t opcode) +{ + /* + * These commands have several descriptors, + * and use the first one to save opcode and return value. + */ + uint16_t spec_opcode[] = {HNS3_OPC_STATS_64_BIT, + HNS3_OPC_STATS_32_BIT, + HNS3_OPC_STATS_MAC, + HNS3_OPC_STATS_MAC_ALL, + HNS3_OPC_QUERY_32_BIT_REG, + HNS3_OPC_QUERY_64_BIT_REG}; + uint32_t i; + + for (i = 0; i < ARRAY_SIZE(spec_opcode); i++) + if (spec_opcode[i] == opcode) + return true; + + return false; +} + +static int +hns3_cmd_convert_err_code(uint16_t desc_ret) +{ + switch (desc_ret) { + case HNS3_CMD_EXEC_SUCCESS: + return 0; + case HNS3_CMD_NO_AUTH: + return -EPERM; + case HNS3_CMD_NOT_SUPPORTED: + return -EOPNOTSUPP; + case HNS3_CMD_QUEUE_FULL: + return -EXFULL; + case HNS3_CMD_NEXT_ERR: + return -ENOSR; + case HNS3_CMD_UNEXE_ERR: + return -ENOTBLK; + case HNS3_CMD_PARA_ERR: + return -EINVAL; + case HNS3_CMD_RESULT_ERR: + return -ERANGE; + case HNS3_CMD_TIMEOUT: + return -ETIME; + case HNS3_CMD_HILINK_ERR: + return -ENOLINK; + case HNS3_CMD_QUEUE_ILLEGAL: + return -ENXIO; + case HNS3_CMD_INVALID: + return -EBADR; + default: + return -EREMOTEIO; + } +} + +static int +hns3_cmd_get_hardware_reply(struct hns3_hw *hw, + struct hns3_cmd_desc *desc, int num, int ntc) +{ + uint16_t opcode, desc_ret; + int current_ntc = ntc; + int handle; + + opcode = rte_le_to_cpu_16(desc[0].opcode); + for (handle = 0; handle < num; handle++) { + /* Get the result of hardware write back */ + desc[handle] = hw->cmq.csq.desc[current_ntc]; + + current_ntc++; + if (current_ntc == hw->cmq.csq.desc_num) + current_ntc = 0; + } + + if (likely(!hns3_is_special_opcode(opcode))) + desc_ret = rte_le_to_cpu_16(desc[num - 1].retval); + else + desc_ret = rte_le_to_cpu_16(desc[0].retval); + + hw->cmq.last_status = desc_ret; + return hns3_cmd_convert_err_code(desc_ret); +} + +static int hns3_cmd_poll_reply(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + uint32_t timeout = 0; + + do { + if (hns3_cmd_csq_done(hw)) + return 0; + + if (rte_atomic16_read(&hw->reset.disable_cmd)) { + hns3_err(hw, + "Don't wait for reply because of disable_cmd"); + return -EBUSY; + } + + if (is_reset_pending(hns)) { + hns3_err(hw, "Don't wait for reply because of reset pending"); + return -EIO; + } + + rte_delay_us(1); + timeout++; + } while (timeout < hw->cmq.tx_timeout); + hns3_err(hw, "Wait for reply timeout"); + return -ETIME; +} + +/* + * hns3_cmd_send - send command to command queue + * + * @param hw + * pointer to the hw struct + * @param desc + * prefilled descriptor for describing the command + * @param num + * the number of descriptors to be sent + * @return + * - -EBUSY if detect device is in resetting + * - -EIO if detect cmd csq corrupted (due to reset) or + * there is reset pending + * - -ENOMEM/-ETIME/...(Non-Zero) if other error case + * - Zero if operation completed successfully + * + * Note -BUSY/-EIO only used in reset case + * + * Note this is the main send command for command queue, it + * sends the queue, cleans the queue, etc + */ +int +hns3_cmd_send(struct hns3_hw *hw, struct hns3_cmd_desc *desc, int num) +{ + struct hns3_cmd_desc *desc_to_use; + int handle = 0; + int retval; + uint32_t ntc; + + if (rte_atomic16_read(&hw->reset.disable_cmd)) + return -EBUSY; + + rte_spinlock_lock(&hw->cmq.csq.lock); + + /* Clean the command send queue */ + retval = hns3_cmd_csq_clean(hw); + if (retval < 0) { + rte_spinlock_unlock(&hw->cmq.csq.lock); + return retval; + } + + if (num > hns3_ring_space(&hw->cmq.csq)) { + rte_spinlock_unlock(&hw->cmq.csq.lock); + return -ENOMEM; + } + + /* + * Record the location of desc in the ring for this time + * which will be use for hardware to write back + */ + ntc = hw->cmq.csq.next_to_use; + + while (handle < num) { + desc_to_use = &hw->cmq.csq.desc[hw->cmq.csq.next_to_use]; + *desc_to_use = desc[handle]; + (hw->cmq.csq.next_to_use)++; + if (hw->cmq.csq.next_to_use == hw->cmq.csq.desc_num) + hw->cmq.csq.next_to_use = 0; + handle++; + } + + /* Write to hardware */ + hns3_write_dev(hw, HNS3_CMDQ_TX_TAIL_REG, hw->cmq.csq.next_to_use); + + /* + * If the command is sync, wait for the firmware to write back, + * if multi descriptors to be sent, use the first one to check. + */ + if (HNS3_CMD_SEND_SYNC(rte_le_to_cpu_16(desc->flag))) { + retval = hns3_cmd_poll_reply(hw); + if (!retval) + retval = hns3_cmd_get_hardware_reply(hw, desc, num, + ntc); + } + + rte_spinlock_unlock(&hw->cmq.csq.lock); + return retval; +} + +static enum hns3_cmd_status +hns3_cmd_query_firmware_version(struct hns3_hw *hw, uint32_t *version) +{ + struct hns3_query_version_cmd *resp; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_FW_VER, 1); + resp = (struct hns3_query_version_cmd *)desc.data; + + /* Initialize the cmd function */ + ret = hns3_cmd_send(hw, &desc, 1); + if (ret == 0) + *version = rte_le_to_cpu_32(resp->firmware); + + return ret; +} + +int +hns3_cmd_init_queue(struct hns3_hw *hw) +{ + int ret; + + /* Setup the lock for command queue */ + rte_spinlock_init(&hw->cmq.csq.lock); + rte_spinlock_init(&hw->cmq.crq.lock); + + /* + * Clear up all command register, + * in case there are some residual values + */ + hns3_cmd_clear_regs(hw); + + /* Setup the queue entries for use cmd queue */ + hw->cmq.csq.desc_num = HNS3_NIC_CMQ_DESC_NUM; + hw->cmq.crq.desc_num = HNS3_NIC_CMQ_DESC_NUM; + + /* Setup Tx write back timeout */ + hw->cmq.tx_timeout = HNS3_CMDQ_TX_TIMEOUT; + + /* Setup queue rings */ + ret = hns3_alloc_cmd_queue(hw, HNS3_TYPE_CSQ); + if (ret) { + PMD_INIT_LOG(ERR, "CSQ ring setup error %d", ret); + return ret; + } + + ret = hns3_alloc_cmd_queue(hw, HNS3_TYPE_CRQ); + if (ret) { + PMD_INIT_LOG(ERR, "CRQ ring setup error %d", ret); + goto err_crq; + } + + return 0; + +err_crq: + hns3_free_cmd_desc(hw, &hw->cmq.csq); + + return ret; +} + +int +hns3_cmd_init(struct hns3_hw *hw) +{ + uint32_t version; + int ret; + + rte_spinlock_lock(&hw->cmq.csq.lock); + rte_spinlock_lock(&hw->cmq.crq.lock); + + hw->cmq.csq.next_to_clean = 0; + hw->cmq.csq.next_to_use = 0; + hw->cmq.crq.next_to_clean = 0; + hw->cmq.crq.next_to_use = 0; + hw->mbx_resp.head = 0; + hw->mbx_resp.tail = 0; + hw->mbx_resp.lost = 0; + hns3_cmd_init_regs(hw); + + rte_spinlock_unlock(&hw->cmq.crq.lock); + rte_spinlock_unlock(&hw->cmq.csq.lock); + + /* + * Check if there is new reset pending, because the higher level + * reset may happen when lower level reset is being processed. + */ + if (is_reset_pending(HNS3_DEV_HW_TO_ADAPTER(hw))) { + PMD_INIT_LOG(ERR, "New reset pending, keep disable cmd"); + ret = -EBUSY; + goto err_cmd_init; + } + rte_atomic16_clear(&hw->reset.disable_cmd); + + ret = hns3_cmd_query_firmware_version(hw, &version); + if (ret) { + PMD_INIT_LOG(ERR, "firmware version query failed %d", ret); + goto err_cmd_init; + } + + hw->fw_version = version; + PMD_INIT_LOG(INFO, "The firmware version is %lu.%lu.%lu.%lu", + hns3_get_field(version, HNS3_FW_VERSION_BYTE3_M, + HNS3_FW_VERSION_BYTE3_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE2_M, + HNS3_FW_VERSION_BYTE2_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE1_M, + HNS3_FW_VERSION_BYTE1_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE0_M, + HNS3_FW_VERSION_BYTE0_S)); + + return 0; + +err_cmd_init: + rte_atomic16_set(&hw->reset.disable_cmd, 1); + return ret; +} + +static void +hns3_destroy_queue(struct hns3_hw *hw, struct hns3_cmq_ring *ring) +{ + rte_spinlock_lock(&ring->lock); + + hns3_free_cmd_desc(hw, ring); + + rte_spinlock_unlock(&ring->lock); +} + +void +hns3_cmd_destroy_queue(struct hns3_hw *hw) +{ + hns3_destroy_queue(hw, &hw->cmq.csq); + hns3_destroy_queue(hw, &hw->cmq.crq); +} + +void +hns3_cmd_uninit(struct hns3_hw *hw) +{ + rte_spinlock_lock(&hw->cmq.csq.lock); + rte_spinlock_lock(&hw->cmq.crq.lock); + rte_atomic16_set(&hw->reset.disable_cmd, 1); + hns3_cmd_clear_regs(hw); + rte_spinlock_unlock(&hw->cmq.crq.lock); + rte_spinlock_unlock(&hw->cmq.csq.lock); +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_cmd.h b/src/spdk/dpdk/drivers/net/hns3/hns3_cmd.h new file mode 100644 index 000000000..da770ac95 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_cmd.h @@ -0,0 +1,814 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_CMD_H_ +#define _HNS3_CMD_H_ + +#define HNS3_CMDQ_TX_TIMEOUT 30000 +#define HNS3_CMDQ_RX_INVLD_B 0 +#define HNS3_CMDQ_RX_OUTVLD_B 1 +#define HNS3_CMD_DESC_ALIGNMENT 4096 +#define HNS3_QUEUE_ID_MASK 0x1ff +#define HNS3_CMD_FLAG_NEXT BIT(2) + +struct hns3_hw; + +#define HNS3_CMD_DESC_DATA_NUM 6 +struct hns3_cmd_desc { + uint16_t opcode; + uint16_t flag; + uint16_t retval; + uint16_t rsv; + uint32_t data[HNS3_CMD_DESC_DATA_NUM]; +}; + +struct hns3_cmq_ring { + uint64_t desc_dma_addr; + struct hns3_cmd_desc *desc; + struct hns3_hw *hw; + + uint16_t buf_size; + uint16_t desc_num; /* max number of cmq descriptor */ + uint32_t next_to_use; + uint32_t next_to_clean; + uint8_t ring_type; /* cmq ring type */ + rte_spinlock_t lock; /* Command queue lock */ + + const void *zone; /* memory zone */ +}; + +enum hns3_cmd_return_status { + HNS3_CMD_EXEC_SUCCESS = 0, + HNS3_CMD_NO_AUTH = 1, + HNS3_CMD_NOT_SUPPORTED = 2, + HNS3_CMD_QUEUE_FULL = 3, + HNS3_CMD_NEXT_ERR = 4, + HNS3_CMD_UNEXE_ERR = 5, + HNS3_CMD_PARA_ERR = 6, + HNS3_CMD_RESULT_ERR = 7, + HNS3_CMD_TIMEOUT = 8, + HNS3_CMD_HILINK_ERR = 9, + HNS3_CMD_QUEUE_ILLEGAL = 10, + HNS3_CMD_INVALID = 11, +}; + +enum hns3_cmd_status { + HNS3_STATUS_SUCCESS = 0, + HNS3_ERR_CSQ_FULL = -1, + HNS3_ERR_CSQ_TIMEOUT = -2, + HNS3_ERR_CSQ_ERROR = -3, +}; + +struct hns3_misc_vector { + uint8_t *addr; + int vector_irq; +}; + +struct hns3_cmq { + struct hns3_cmq_ring csq; + struct hns3_cmq_ring crq; + uint16_t tx_timeout; + enum hns3_cmd_status last_status; +}; + +enum hns3_opcode_type { + /* Generic commands */ + HNS3_OPC_QUERY_FW_VER = 0x0001, + HNS3_OPC_CFG_RST_TRIGGER = 0x0020, + HNS3_OPC_GBL_RST_STATUS = 0x0021, + HNS3_OPC_QUERY_FUNC_STATUS = 0x0022, + HNS3_OPC_QUERY_PF_RSRC = 0x0023, + HNS3_OPC_QUERY_VF_RSRC = 0x0024, + HNS3_OPC_GET_CFG_PARAM = 0x0025, + HNS3_OPC_PF_RST_DONE = 0x0026, + + HNS3_OPC_STATS_64_BIT = 0x0030, + HNS3_OPC_STATS_32_BIT = 0x0031, + HNS3_OPC_STATS_MAC = 0x0032, + HNS3_OPC_QUERY_MAC_REG_NUM = 0x0033, + HNS3_OPC_STATS_MAC_ALL = 0x0034, + + HNS3_OPC_QUERY_REG_NUM = 0x0040, + HNS3_OPC_QUERY_32_BIT_REG = 0x0041, + HNS3_OPC_QUERY_64_BIT_REG = 0x0042, + + /* MAC command */ + HNS3_OPC_CONFIG_MAC_MODE = 0x0301, + HNS3_OPC_QUERY_LINK_STATUS = 0x0307, + HNS3_OPC_CONFIG_MAX_FRM_SIZE = 0x0308, + HNS3_OPC_CONFIG_SPEED_DUP = 0x0309, + HNS3_MAC_COMMON_INT_EN = 0x030E, + + /* PFC/Pause commands */ + HNS3_OPC_CFG_MAC_PAUSE_EN = 0x0701, + HNS3_OPC_CFG_PFC_PAUSE_EN = 0x0702, + HNS3_OPC_CFG_MAC_PARA = 0x0703, + HNS3_OPC_CFG_PFC_PARA = 0x0704, + HNS3_OPC_QUERY_MAC_TX_PKT_CNT = 0x0705, + HNS3_OPC_QUERY_MAC_RX_PKT_CNT = 0x0706, + HNS3_OPC_QUERY_PFC_TX_PKT_CNT = 0x0707, + HNS3_OPC_QUERY_PFC_RX_PKT_CNT = 0x0708, + HNS3_OPC_PRI_TO_TC_MAPPING = 0x0709, + HNS3_OPC_QOS_MAP = 0x070A, + + /* ETS/scheduler commands */ + HNS3_OPC_TM_PG_TO_PRI_LINK = 0x0804, + HNS3_OPC_TM_QS_TO_PRI_LINK = 0x0805, + HNS3_OPC_TM_NQ_TO_QS_LINK = 0x0806, + HNS3_OPC_TM_RQ_TO_QS_LINK = 0x0807, + HNS3_OPC_TM_PORT_WEIGHT = 0x0808, + HNS3_OPC_TM_PG_WEIGHT = 0x0809, + HNS3_OPC_TM_QS_WEIGHT = 0x080A, + HNS3_OPC_TM_PRI_WEIGHT = 0x080B, + HNS3_OPC_TM_PRI_C_SHAPPING = 0x080C, + HNS3_OPC_TM_PRI_P_SHAPPING = 0x080D, + HNS3_OPC_TM_PG_C_SHAPPING = 0x080E, + HNS3_OPC_TM_PG_P_SHAPPING = 0x080F, + HNS3_OPC_TM_PORT_SHAPPING = 0x0810, + HNS3_OPC_TM_PG_SCH_MODE_CFG = 0x0812, + HNS3_OPC_TM_PRI_SCH_MODE_CFG = 0x0813, + HNS3_OPC_TM_QS_SCH_MODE_CFG = 0x0814, + HNS3_OPC_TM_BP_TO_QSET_MAPPING = 0x0815, + HNS3_OPC_ETS_TC_WEIGHT = 0x0843, + HNS3_OPC_QSET_DFX_STS = 0x0844, + HNS3_OPC_PRI_DFX_STS = 0x0845, + HNS3_OPC_PG_DFX_STS = 0x0846, + HNS3_OPC_PORT_DFX_STS = 0x0847, + HNS3_OPC_SCH_NQ_CNT = 0x0848, + HNS3_OPC_SCH_RQ_CNT = 0x0849, + HNS3_OPC_TM_INTERNAL_STS = 0x0850, + HNS3_OPC_TM_INTERNAL_CNT = 0x0851, + HNS3_OPC_TM_INTERNAL_STS_1 = 0x0852, + + /* Mailbox cmd */ + HNS3_OPC_MBX_VF_TO_PF = 0x2001, + + /* Packet buffer allocate commands */ + HNS3_OPC_TX_BUFF_ALLOC = 0x0901, + HNS3_OPC_RX_PRIV_BUFF_ALLOC = 0x0902, + HNS3_OPC_RX_PRIV_WL_ALLOC = 0x0903, + HNS3_OPC_RX_COM_THRD_ALLOC = 0x0904, + HNS3_OPC_RX_COM_WL_ALLOC = 0x0905, + + /* SSU module INT commands */ + HNS3_SSU_ECC_INT_CMD = 0x0989, + HNS3_SSU_COMMON_INT_CMD = 0x098C, + + /* TQP management command */ + HNS3_OPC_SET_TQP_MAP = 0x0A01, + + /* TQP commands */ + HNS3_OPC_QUERY_TX_STATUS = 0x0B03, + HNS3_OPC_QUERY_RX_STATUS = 0x0B13, + HNS3_OPC_CFG_COM_TQP_QUEUE = 0x0B20, + HNS3_OPC_RESET_TQP_QUEUE = 0x0B22, + + /* PPU module intr commands */ + HNS3_PPU_MPF_ECC_INT_CMD = 0x0B40, + HNS3_PPU_MPF_OTHER_INT_CMD = 0x0B41, + HNS3_PPU_PF_OTHER_INT_CMD = 0x0B42, + + /* TSO command */ + HNS3_OPC_TSO_GENERIC_CONFIG = 0x0C01, + HNS3_OPC_GRO_GENERIC_CONFIG = 0x0C10, + + /* RSS commands */ + HNS3_OPC_RSS_GENERIC_CONFIG = 0x0D01, + HNS3_OPC_RSS_INPUT_TUPLE = 0x0D02, + HNS3_OPC_RSS_INDIR_TABLE = 0x0D07, + HNS3_OPC_RSS_TC_MODE = 0x0D08, + + /* Promisuous mode command */ + HNS3_OPC_CFG_PROMISC_MODE = 0x0E01, + + /* Vlan offload commands */ + HNS3_OPC_VLAN_PORT_TX_CFG = 0x0F01, + HNS3_OPC_VLAN_PORT_RX_CFG = 0x0F02, + + /* MAC commands */ + HNS3_OPC_MAC_VLAN_ADD = 0x1000, + HNS3_OPC_MAC_VLAN_REMOVE = 0x1001, + HNS3_OPC_MAC_VLAN_TYPE_ID = 0x1002, + HNS3_OPC_MAC_VLAN_INSERT = 0x1003, + HNS3_OPC_MAC_VLAN_ALLOCATE = 0x1004, + HNS3_OPC_MAC_ETHTYPE_ADD = 0x1010, + + /* VLAN commands */ + HNS3_OPC_VLAN_FILTER_CTRL = 0x1100, + HNS3_OPC_VLAN_FILTER_PF_CFG = 0x1101, + HNS3_OPC_VLAN_FILTER_VF_CFG = 0x1102, + + /* Flow Director command */ + HNS3_OPC_FD_MODE_CTRL = 0x1200, + HNS3_OPC_FD_GET_ALLOCATION = 0x1201, + HNS3_OPC_FD_KEY_CONFIG = 0x1202, + HNS3_OPC_FD_TCAM_OP = 0x1203, + HNS3_OPC_FD_AD_OP = 0x1204, + HNS3_OPC_FD_COUNTER_OP = 0x1205, + + /* SFP command */ + HNS3_OPC_SFP_GET_SPEED = 0x7104, + + /* Interrupts commands */ + HNS3_OPC_ADD_RING_TO_VECTOR = 0x1503, + HNS3_OPC_DEL_RING_TO_VECTOR = 0x1504, + + /* Error INT commands */ + HNS3_QUERY_MSIX_INT_STS_BD_NUM = 0x1513, + HNS3_QUERY_CLEAR_ALL_MPF_MSIX_INT = 0x1514, + HNS3_QUERY_CLEAR_ALL_PF_MSIX_INT = 0x1515, + + /* PPP module intr commands */ + HNS3_PPP_CMD0_INT_CMD = 0x2100, + HNS3_PPP_CMD1_INT_CMD = 0x2101, +}; + +#define HNS3_CMD_FLAG_IN BIT(0) +#define HNS3_CMD_FLAG_OUT BIT(1) +#define HNS3_CMD_FLAG_NEXT BIT(2) +#define HNS3_CMD_FLAG_WR BIT(3) +#define HNS3_CMD_FLAG_NO_INTR BIT(4) +#define HNS3_CMD_FLAG_ERR_INTR BIT(5) + +#define HNS3_BUF_SIZE_UNIT 256 +#define HNS3_BUF_MUL_BY 2 +#define HNS3_BUF_DIV_BY 2 +#define NEED_RESERVE_TC_NUM 2 +#define BUF_MAX_PERCENT 100 +#define BUF_RESERVE_PERCENT 90 + +#define HNS3_MAX_TC_NUM 8 +#define HNS3_TC0_PRI_BUF_EN_B 15 /* Bit 15 indicate enable or not */ +#define HNS3_BUF_UNIT_S 7 /* Buf size is united by 128 bytes */ +#define HNS3_TX_BUFF_RSV_NUM 8 +struct hns3_tx_buff_alloc_cmd { + uint16_t tx_pkt_buff[HNS3_MAX_TC_NUM]; + uint8_t tx_buff_rsv[HNS3_TX_BUFF_RSV_NUM]; +}; + +struct hns3_rx_priv_buff_cmd { + uint16_t buf_num[HNS3_MAX_TC_NUM]; + uint16_t shared_buf; + uint8_t rsv[6]; +}; + +#define HNS3_FW_VERSION_BYTE3_S 24 +#define HNS3_FW_VERSION_BYTE3_M GENMASK(31, 24) +#define HNS3_FW_VERSION_BYTE2_S 16 +#define HNS3_FW_VERSION_BYTE2_M GENMASK(23, 16) +#define HNS3_FW_VERSION_BYTE1_S 8 +#define HNS3_FW_VERSION_BYTE1_M GENMASK(15, 8) +#define HNS3_FW_VERSION_BYTE0_S 0 +#define HNS3_FW_VERSION_BYTE0_M GENMASK(7, 0) +struct hns3_query_version_cmd { + uint32_t firmware; + uint32_t firmware_rsv[5]; +}; + +#define HNS3_RX_PRIV_EN_B 15 +#define HNS3_TC_NUM_ONE_DESC 4 +struct hns3_priv_wl { + uint16_t high; + uint16_t low; +}; + +struct hns3_rx_priv_wl_buf { + struct hns3_priv_wl tc_wl[HNS3_TC_NUM_ONE_DESC]; +}; + +struct hns3_rx_com_thrd { + struct hns3_priv_wl com_thrd[HNS3_TC_NUM_ONE_DESC]; +}; + +struct hns3_rx_com_wl { + struct hns3_priv_wl com_wl; +}; + +struct hns3_waterline { + uint32_t low; + uint32_t high; +}; + +struct hns3_tc_thrd { + uint32_t low; + uint32_t high; +}; + +struct hns3_priv_buf { + struct hns3_waterline wl; /* Waterline for low and high */ + uint32_t buf_size; /* TC private buffer size */ + uint32_t tx_buf_size; + uint32_t enable; /* Enable TC private buffer or not */ +}; + +struct hns3_shared_buf { + struct hns3_waterline self; + struct hns3_tc_thrd tc_thrd[HNS3_MAX_TC_NUM]; + uint32_t buf_size; +}; + +struct hns3_pkt_buf_alloc { + struct hns3_priv_buf priv_buf[HNS3_MAX_TC_NUM]; + struct hns3_shared_buf s_buf; +}; + +#define HNS3_RX_COM_WL_EN_B 15 +struct hns3_rx_com_wl_buf_cmd { + uint16_t high_wl; + uint16_t low_wl; + uint8_t rsv[20]; +}; + +#define HNS3_RX_PKT_EN_B 15 +struct hns3_rx_pkt_buf_cmd { + uint16_t high_pkt; + uint16_t low_pkt; + uint8_t rsv[20]; +}; + +#define HNS3_PF_STATE_DONE_B 0 +#define HNS3_PF_STATE_MAIN_B 1 +#define HNS3_PF_STATE_BOND_B 2 +#define HNS3_PF_STATE_MAC_N_B 6 +#define HNS3_PF_MAC_NUM_MASK 0x3 +#define HNS3_PF_STATE_MAIN BIT(HNS3_PF_STATE_MAIN_B) +#define HNS3_PF_STATE_DONE BIT(HNS3_PF_STATE_DONE_B) +#define HNS3_VF_RST_STATE_NUM 4 +struct hns3_func_status_cmd { + uint32_t vf_rst_state[HNS3_VF_RST_STATE_NUM]; + uint8_t pf_state; + uint8_t mac_id; + uint8_t rsv1; + uint8_t pf_cnt_in_mac; + uint8_t pf_num; + uint8_t vf_num; + uint8_t rsv[2]; +}; + +#define HNS3_VEC_NUM_S 0 +#define HNS3_VEC_NUM_M GENMASK(7, 0) +#define HNS3_MIN_VECTOR_NUM 2 /* one for msi-x, another for IO */ +struct hns3_pf_res_cmd { + uint16_t tqp_num; + uint16_t buf_size; + uint16_t msixcap_localid_ba_nic; + uint16_t msixcap_localid_ba_rocee; + uint16_t pf_intr_vector_number; + uint16_t pf_own_fun_number; + uint16_t tx_buf_size; + uint16_t dv_buf_size; + uint32_t rsv[2]; +}; + +struct hns3_vf_res_cmd { + uint16_t tqp_num; + uint16_t reserved; + uint16_t msixcap_localid_ba_nic; + uint16_t msixcap_localid_ba_rocee; + uint16_t vf_intr_vector_number; + uint16_t rsv[7]; +}; + +#define HNS3_UMV_SPC_ALC_B 0 +struct hns3_umv_spc_alc_cmd { + uint8_t allocate; + uint8_t rsv1[3]; + uint32_t space_size; + uint8_t rsv2[16]; +}; + +#define HNS3_CFG_OFFSET_S 0 +#define HNS3_CFG_OFFSET_M GENMASK(19, 0) +#define HNS3_CFG_RD_LEN_S 24 +#define HNS3_CFG_RD_LEN_M GENMASK(27, 24) +#define HNS3_CFG_RD_LEN_BYTES 16 +#define HNS3_CFG_RD_LEN_UNIT 4 + +#define HNS3_CFG_VMDQ_S 0 +#define HNS3_CFG_VMDQ_M GENMASK(7, 0) +#define HNS3_CFG_TC_NUM_S 8 +#define HNS3_CFG_TC_NUM_M GENMASK(15, 8) +#define HNS3_CFG_TQP_DESC_N_S 16 +#define HNS3_CFG_TQP_DESC_N_M GENMASK(31, 16) +#define HNS3_CFG_PHY_ADDR_S 0 +#define HNS3_CFG_PHY_ADDR_M GENMASK(7, 0) +#define HNS3_CFG_MEDIA_TP_S 8 +#define HNS3_CFG_MEDIA_TP_M GENMASK(15, 8) +#define HNS3_CFG_RX_BUF_LEN_S 16 +#define HNS3_CFG_RX_BUF_LEN_M GENMASK(31, 16) +#define HNS3_CFG_MAC_ADDR_H_S 0 +#define HNS3_CFG_MAC_ADDR_H_M GENMASK(15, 0) +#define HNS3_CFG_DEFAULT_SPEED_S 16 +#define HNS3_CFG_DEFAULT_SPEED_M GENMASK(23, 16) +#define HNS3_CFG_RSS_SIZE_S 24 +#define HNS3_CFG_RSS_SIZE_M GENMASK(31, 24) +#define HNS3_CFG_SPEED_ABILITY_S 0 +#define HNS3_CFG_SPEED_ABILITY_M GENMASK(7, 0) +#define HNS3_CFG_UMV_TBL_SPACE_S 16 +#define HNS3_CFG_UMV_TBL_SPACE_M GENMASK(31, 16) + +#define HNS3_ACCEPT_TAG1_B 0 +#define HNS3_ACCEPT_UNTAG1_B 1 +#define HNS3_PORT_INS_TAG1_EN_B 2 +#define HNS3_PORT_INS_TAG2_EN_B 3 +#define HNS3_CFG_NIC_ROCE_SEL_B 4 +#define HNS3_ACCEPT_TAG2_B 5 +#define HNS3_ACCEPT_UNTAG2_B 6 + +#define HNS3_REM_TAG1_EN_B 0 +#define HNS3_REM_TAG2_EN_B 1 +#define HNS3_SHOW_TAG1_EN_B 2 +#define HNS3_SHOW_TAG2_EN_B 3 + +/* Factor used to calculate offset and bitmap of VF num */ +#define HNS3_VF_NUM_PER_CMD 64 +#define HNS3_VF_NUM_PER_BYTE 8 + +struct hns3_cfg_param_cmd { + uint32_t offset; + uint32_t rsv; + uint32_t param[4]; +}; + +#define HNS3_VPORT_VTAG_RX_CFG_CMD_VF_BITMAP_NUM 8 +struct hns3_vport_vtag_rx_cfg_cmd { + uint8_t vport_vlan_cfg; + uint8_t vf_offset; + uint8_t rsv1[6]; + uint8_t vf_bitmap[HNS3_VPORT_VTAG_RX_CFG_CMD_VF_BITMAP_NUM]; + uint8_t rsv2[8]; +}; + +struct hns3_vport_vtag_tx_cfg_cmd { + uint8_t vport_vlan_cfg; + uint8_t vf_offset; + uint8_t rsv1[2]; + uint16_t def_vlan_tag1; + uint16_t def_vlan_tag2; + uint8_t vf_bitmap[8]; + uint8_t rsv2[8]; +}; + + +struct hns3_vlan_filter_ctrl_cmd { + uint8_t vlan_type; + uint8_t vlan_fe; + uint8_t rsv1[2]; + uint8_t vf_id; + uint8_t rsv2[19]; +}; + +#define HNS3_VLAN_OFFSET_BITMAP_NUM 20 +struct hns3_vlan_filter_pf_cfg_cmd { + uint8_t vlan_offset; + uint8_t vlan_cfg; + uint8_t rsv[2]; + uint8_t vlan_offset_bitmap[HNS3_VLAN_OFFSET_BITMAP_NUM]; +}; + +#define HNS3_VLAN_FILTER_VF_CFG_CMD_VF_BITMAP_NUM 16 +struct hns3_vlan_filter_vf_cfg_cmd { + uint16_t vlan_id; + uint8_t resp_code; + uint8_t rsv; + uint8_t vlan_cfg; + uint8_t rsv1[3]; + uint8_t vf_bitmap[HNS3_VLAN_FILTER_VF_CFG_CMD_VF_BITMAP_NUM]; +}; + +struct hns3_tx_vlan_type_cfg_cmd { + uint16_t ot_vlan_type; + uint16_t in_vlan_type; + uint8_t rsv[20]; +}; + +struct hns3_rx_vlan_type_cfg_cmd { + uint16_t ot_fst_vlan_type; + uint16_t ot_sec_vlan_type; + uint16_t in_fst_vlan_type; + uint16_t in_sec_vlan_type; + uint8_t rsv[16]; +}; + +#define HNS3_TSO_MSS_MIN_S 0 +#define HNS3_TSO_MSS_MIN_M GENMASK(13, 0) + +#define HNS3_TSO_MSS_MAX_S 16 +#define HNS3_TSO_MSS_MAX_M GENMASK(29, 16) + +struct hns3_cfg_tso_status_cmd { + rte_le16_t tso_mss_min; + rte_le16_t tso_mss_max; + uint8_t rsv[20]; +}; + +#define HNS3_GRO_EN_B 0 +struct hns3_cfg_gro_status_cmd { + rte_le16_t gro_en; + uint8_t rsv[22]; +}; + +#define HNS3_TSO_MSS_MIN 256 +#define HNS3_TSO_MSS_MAX 9668 + +#define HNS3_RSS_HASH_KEY_OFFSET_B 4 + +#define HNS3_RSS_CFG_TBL_SIZE 16 +#define HNS3_RSS_HASH_KEY_NUM 16 +/* Configure the algorithm mode and Hash Key, opcode:0x0D01 */ +struct hns3_rss_generic_config_cmd { + /* Hash_algorithm(8.0~8.3), hash_key_offset(8.4~8.7) */ + uint8_t hash_config; + uint8_t rsv[7]; + uint8_t hash_key[HNS3_RSS_HASH_KEY_NUM]; +}; + +/* Configure the tuple selection for RSS hash input, opcode:0x0D02 */ +struct hns3_rss_input_tuple_cmd { + uint8_t ipv4_tcp_en; + uint8_t ipv4_udp_en; + uint8_t ipv4_sctp_en; + uint8_t ipv4_fragment_en; + uint8_t ipv6_tcp_en; + uint8_t ipv6_udp_en; + uint8_t ipv6_sctp_en; + uint8_t ipv6_fragment_en; + uint8_t rsv[16]; +}; + +#define HNS3_RSS_CFG_TBL_SIZE 16 + +/* Configure the indirection table, opcode:0x0D07 */ +struct hns3_rss_indirection_table_cmd { + uint16_t start_table_index; /* Bit3~0 must be 0x0. */ + uint16_t rss_set_bitmap; + uint8_t rsv[4]; + uint8_t rss_result[HNS3_RSS_CFG_TBL_SIZE]; +}; + +#define HNS3_RSS_TC_OFFSET_S 0 +#define HNS3_RSS_TC_OFFSET_M (0x3ff << HNS3_RSS_TC_OFFSET_S) +#define HNS3_RSS_TC_SIZE_S 12 +#define HNS3_RSS_TC_SIZE_M (0x7 << HNS3_RSS_TC_SIZE_S) +#define HNS3_RSS_TC_VALID_B 15 + +/* Configure the tc_size and tc_offset, opcode:0x0D08 */ +struct hns3_rss_tc_mode_cmd { + uint16_t rss_tc_mode[HNS3_MAX_TC_NUM]; + uint8_t rsv[8]; +}; + +#define HNS3_LINK_STATUS_UP_B 0 +#define HNS3_LINK_STATUS_UP_M BIT(HNS3_LINK_STATUS_UP_B) +struct hns3_link_status_cmd { + uint8_t status; + uint8_t rsv[23]; +}; + +struct hns3_promisc_param { + uint8_t vf_id; + uint8_t enable; +}; + +#define HNS3_PROMISC_TX_EN_B BIT(4) +#define HNS3_PROMISC_RX_EN_B BIT(5) +#define HNS3_PROMISC_EN_B 1 +#define HNS3_PROMISC_EN_ALL 0x7 +#define HNS3_PROMISC_EN_UC 0x1 +#define HNS3_PROMISC_EN_MC 0x2 +#define HNS3_PROMISC_EN_BC 0x4 +struct hns3_promisc_cfg_cmd { + uint8_t flag; + uint8_t vf_id; + uint16_t rsv0; + uint8_t rsv1[20]; +}; + +enum hns3_promisc_type { + HNS3_UNICAST = 1, + HNS3_MULTICAST = 2, + HNS3_BROADCAST = 3, +}; + +#define HNS3_MAC_TX_EN_B 6 +#define HNS3_MAC_RX_EN_B 7 +#define HNS3_MAC_PAD_TX_B 11 +#define HNS3_MAC_PAD_RX_B 12 +#define HNS3_MAC_1588_TX_B 13 +#define HNS3_MAC_1588_RX_B 14 +#define HNS3_MAC_APP_LP_B 15 +#define HNS3_MAC_LINE_LP_B 16 +#define HNS3_MAC_FCS_TX_B 17 +#define HNS3_MAC_RX_OVERSIZE_TRUNCATE_B 18 +#define HNS3_MAC_RX_FCS_STRIP_B 19 +#define HNS3_MAC_RX_FCS_B 20 +#define HNS3_MAC_TX_UNDER_MIN_ERR_B 21 +#define HNS3_MAC_TX_OVERSIZE_TRUNCATE_B 22 + +struct hns3_config_mac_mode_cmd { + uint32_t txrx_pad_fcs_loop_en; + uint8_t rsv[20]; +}; + +#define HNS3_CFG_SPEED_10M 6 +#define HNS3_CFG_SPEED_100M 7 +#define HNS3_CFG_SPEED_1G 0 +#define HNS3_CFG_SPEED_10G 1 +#define HNS3_CFG_SPEED_25G 2 +#define HNS3_CFG_SPEED_40G 3 +#define HNS3_CFG_SPEED_50G 4 +#define HNS3_CFG_SPEED_100G 5 + +#define HNS3_CFG_SPEED_S 0 +#define HNS3_CFG_SPEED_M GENMASK(5, 0) +#define HNS3_CFG_DUPLEX_B 7 +#define HNS3_CFG_DUPLEX_M BIT(HNS3_CFG_DUPLEX_B) + +#define HNS3_CFG_MAC_SPEED_CHANGE_EN_B 0 + +struct hns3_config_mac_speed_dup_cmd { + uint8_t speed_dup; + uint8_t mac_change_fec_en; + uint8_t rsv[22]; +}; + +#define HNS3_RING_ID_MASK GENMASK(9, 0) +#define HNS3_TQP_ENABLE_B 0 + +#define HNS3_MAC_CFG_AN_EN_B 0 +#define HNS3_MAC_CFG_AN_INT_EN_B 1 +#define HNS3_MAC_CFG_AN_INT_MSK_B 2 +#define HNS3_MAC_CFG_AN_INT_CLR_B 3 +#define HNS3_MAC_CFG_AN_RST_B 4 + +#define HNS3_MAC_CFG_AN_EN BIT(HNS3_MAC_CFG_AN_EN_B) + +struct hns3_config_auto_neg_cmd { + uint32_t cfg_an_cmd_flag; + uint8_t rsv[20]; +}; + +struct hns3_sfp_speed_cmd { + uint32_t sfp_speed; + uint32_t rsv[5]; +}; + +#define HNS3_MAC_MGR_MASK_VLAN_B BIT(0) +#define HNS3_MAC_MGR_MASK_MAC_B BIT(1) +#define HNS3_MAC_MGR_MASK_ETHERTYPE_B BIT(2) +#define HNS3_MAC_ETHERTYPE_LLDP 0x88cc + +struct hns3_mac_mgr_tbl_entry_cmd { + uint8_t flags; + uint8_t resp_code; + uint16_t vlan_tag; + uint32_t mac_addr_hi32; + uint16_t mac_addr_lo16; + uint16_t rsv1; + uint16_t ethter_type; + uint16_t egress_port; + uint16_t egress_queue; + uint8_t sw_port_id_aware; + uint8_t rsv2; + uint8_t i_port_bitmap; + uint8_t i_port_direction; + uint8_t rsv3[2]; +}; + +struct hns3_cfg_com_tqp_queue_cmd { + uint16_t tqp_id; + uint16_t stream_id; + uint8_t enable; + uint8_t rsv[19]; +}; + +#define HNS3_TQP_MAP_TYPE_PF 0 +#define HNS3_TQP_MAP_TYPE_VF 1 +#define HNS3_TQP_MAP_TYPE_B 0 +#define HNS3_TQP_MAP_EN_B 1 + +struct hns3_tqp_map_cmd { + uint16_t tqp_id; /* Absolute tqp id for in this pf */ + uint8_t tqp_vf; /* VF id */ + uint8_t tqp_flag; /* Indicate it's pf or vf tqp */ + uint16_t tqp_vid; /* Virtual id in this pf/vf */ + uint8_t rsv[18]; +}; + +enum hns3_ring_type { + HNS3_RING_TYPE_TX, + HNS3_RING_TYPE_RX +}; + +enum hns3_int_gl_idx { + HNS3_RING_GL_RX, + HNS3_RING_GL_TX, + HNS3_RING_GL_IMMEDIATE = 3 +}; + +#define HNS3_RING_GL_IDX_S 0 +#define HNS3_RING_GL_IDX_M GENMASK(1, 0) + +#define HNS3_VECTOR_ELEMENTS_PER_CMD 10 + +#define HNS3_INT_TYPE_S 0 +#define HNS3_INT_TYPE_M GENMASK(1, 0) +#define HNS3_TQP_ID_S 2 +#define HNS3_TQP_ID_M GENMASK(12, 2) +#define HNS3_INT_GL_IDX_S 13 +#define HNS3_INT_GL_IDX_M GENMASK(14, 13) +struct hns3_ctrl_vector_chain_cmd { + uint8_t int_vector_id; + uint8_t int_cause_num; + uint16_t tqp_type_and_id[HNS3_VECTOR_ELEMENTS_PER_CMD]; + uint8_t vfid; + uint8_t rsv; +}; + +struct hns3_config_max_frm_size_cmd { + uint16_t max_frm_size; + uint8_t min_frm_size; + uint8_t rsv[21]; +}; + +enum hns3_mac_vlan_tbl_opcode { + HNS3_MAC_VLAN_ADD, /* Add new or modify mac_vlan */ + HNS3_MAC_VLAN_UPDATE, /* Modify other fields of this table */ + HNS3_MAC_VLAN_REMOVE, /* Remove a entry through mac_vlan key */ + HNS3_MAC_VLAN_LKUP, /* Lookup a entry through mac_vlan key */ +}; + +enum hns3_mac_vlan_add_resp_code { + HNS3_ADD_UC_OVERFLOW = 2, /* ADD failed for UC overflow */ + HNS3_ADD_MC_OVERFLOW, /* ADD failed for MC overflow */ +}; + +#define HNS3_MC_MAC_VLAN_ADD_DESC_NUM 3 + +#define HNS3_MAC_VLAN_BIT0_EN_B 0 +#define HNS3_MAC_VLAN_BIT1_EN_B 1 +#define HNS3_MAC_EPORT_SW_EN_B 12 +#define HNS3_MAC_EPORT_TYPE_B 11 +#define HNS3_MAC_EPORT_VFID_S 3 +#define HNS3_MAC_EPORT_VFID_M GENMASK(10, 3) +#define HNS3_MAC_EPORT_PFID_S 0 +#define HNS3_MAC_EPORT_PFID_M GENMASK(2, 0) +struct hns3_mac_vlan_tbl_entry_cmd { + uint8_t flags; + uint8_t resp_code; + uint16_t vlan_tag; + uint32_t mac_addr_hi32; + uint16_t mac_addr_lo16; + uint16_t rsv1; + uint8_t entry_type; + uint8_t mc_mac_en; + uint16_t egress_port; + uint16_t egress_queue; + uint8_t rsv2[6]; +}; + +#define HNS3_TQP_RESET_B 0 +struct hns3_reset_tqp_queue_cmd { + uint16_t tqp_id; + uint8_t reset_req; + uint8_t ready_to_reset; + uint8_t rsv[20]; +}; + +#define HNS3_CFG_RESET_MAC_B 3 +#define HNS3_CFG_RESET_FUNC_B 7 +struct hns3_reset_cmd { + uint8_t mac_func_reset; + uint8_t fun_reset_vfid; + uint8_t rsv[22]; +}; + +#define HNS3_MAX_TQP_NUM_PER_FUNC 64 +#define HNS3_DEFAULT_TX_BUF 0x4000 /* 16k bytes */ +#define HNS3_TOTAL_PKT_BUF 0x108000 /* 1.03125M bytes */ +#define HNS3_DEFAULT_DV 0xA000 /* 40k byte */ +#define HNS3_DEFAULT_NON_DCB_DV 0x7800 /* 30K byte */ +#define HNS3_NON_DCB_ADDITIONAL_BUF 0x1400 /* 5120 byte */ + +#define HNS3_TYPE_CRQ 0 +#define HNS3_TYPE_CSQ 1 + +#define HNS3_NIC_SW_RST_RDY_B 16 +#define HNS3_NIC_SW_RST_RDY BIT(HNS3_NIC_SW_RST_RDY_B) +#define HNS3_NIC_CMQ_DESC_NUM 1024 +#define HNS3_NIC_CMQ_DESC_NUM_S 3 + +#define HNS3_CMD_SEND_SYNC(flag) \ + ((flag) & HNS3_CMD_FLAG_NO_INTR) + +void hns3_cmd_reuse_desc(struct hns3_cmd_desc *desc, bool is_read); +void hns3_cmd_setup_basic_desc(struct hns3_cmd_desc *desc, + enum hns3_opcode_type opcode, bool is_read); +int hns3_cmd_send(struct hns3_hw *hw, struct hns3_cmd_desc *desc, int num); +int hns3_cmd_init_queue(struct hns3_hw *hw); +int hns3_cmd_init(struct hns3_hw *hw); +void hns3_cmd_destroy_queue(struct hns3_hw *hw); +void hns3_cmd_uninit(struct hns3_hw *hw); + +#endif /* _HNS3_CMD_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_dcb.c b/src/spdk/dpdk/drivers/net/hns3/hns3_dcb.c new file mode 100644 index 000000000..02628b6b6 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_dcb.c @@ -0,0 +1,1690 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <rte_io.h> +#include <rte_common.h> +#include <rte_ethdev.h> + +#include "hns3_logs.h" +#include "hns3_regs.h" +#include "hns3_ethdev.h" +#include "hns3_dcb.h" + +#define HNS3_SHAPER_BS_U_DEF 5 +#define HNS3_SHAPER_BS_S_DEF 20 +#define BW_MAX_PERCENT 100 +#define HNS3_ETHER_MAX_RATE 100000 + +/* + * hns3_shaper_para_calc: calculate ir parameter for the shaper + * @ir: Rate to be config, its unit is Mbps + * @shaper_level: the shaper level. eg: port, pg, priority, queueset + * @shaper_para: shaper parameter of IR shaper + * + * the formula: + * + * IR_b * (2 ^ IR_u) * 8 + * IR(Mbps) = ------------------------- * CLOCK(1000Mbps) + * Tick * (2 ^ IR_s) + * + * @return: 0: calculate sucessful, negative: fail + */ +static int +hns3_shaper_para_calc(struct hns3_hw *hw, uint32_t ir, uint8_t shaper_level, + struct hns3_shaper_parameter *shaper_para) +{ +#define SHAPER_DEFAULT_IR_B 126 +#define DIVISOR_CLK (1000 * 8) +#define DIVISOR_IR_B_126 (126 * DIVISOR_CLK) + + const uint16_t tick_array[HNS3_SHAPER_LVL_CNT] = { + 6 * 256, /* Prioriy level */ + 6 * 32, /* Prioriy group level */ + 6 * 8, /* Port level */ + 6 * 256 /* Qset level */ + }; + uint8_t ir_u_calc = 0; + uint8_t ir_s_calc = 0; + uint32_t denominator; + uint32_t ir_calc; + uint32_t tick; + + /* Calc tick */ + if (shaper_level >= HNS3_SHAPER_LVL_CNT) { + hns3_err(hw, + "shaper_level(%d) is greater than HNS3_SHAPER_LVL_CNT(%d)", + shaper_level, HNS3_SHAPER_LVL_CNT); + return -EINVAL; + } + + if (ir > HNS3_ETHER_MAX_RATE) { + hns3_err(hw, "rate(%d) exceeds the rate driver supported " + "HNS3_ETHER_MAX_RATE(%d)", ir, HNS3_ETHER_MAX_RATE); + return -EINVAL; + } + + tick = tick_array[shaper_level]; + + /* + * Calc the speed if ir_b = 126, ir_u = 0 and ir_s = 0 + * the formula is changed to: + * 126 * 1 * 8 + * ir_calc = ---------------- * 1000 + * tick * 1 + */ + ir_calc = (DIVISOR_IR_B_126 + (tick >> 1) - 1) / tick; + + if (ir_calc == ir) { + shaper_para->ir_b = SHAPER_DEFAULT_IR_B; + } else if (ir_calc > ir) { + /* Increasing the denominator to select ir_s value */ + do { + ir_s_calc++; + ir_calc = DIVISOR_IR_B_126 / (tick * (1 << ir_s_calc)); + } while (ir_calc > ir); + + if (ir_calc == ir) + shaper_para->ir_b = SHAPER_DEFAULT_IR_B; + else + shaper_para->ir_b = (ir * tick * (1 << ir_s_calc) + + (DIVISOR_CLK >> 1)) / DIVISOR_CLK; + } else { + /* + * Increasing the numerator to select ir_u value. ir_u_calc will + * get maximum value when ir_calc is minimum and ir is maximum. + * ir_calc gets minimum value when tick is the maximum value. + * At the same time, value of ir_u_calc can only be increased up + * to eight after the while loop if the value of ir is equal + * to HNS3_ETHER_MAX_RATE. + */ + uint32_t numerator; + do { + ir_u_calc++; + numerator = DIVISOR_IR_B_126 * (1 << ir_u_calc); + ir_calc = (numerator + (tick >> 1)) / tick; + } while (ir_calc < ir); + + if (ir_calc == ir) { + shaper_para->ir_b = SHAPER_DEFAULT_IR_B; + } else { + --ir_u_calc; + + /* + * The maximum value of ir_u_calc in this branch is + * seven in all cases. Thus, value of denominator can + * not be zero here. + */ + denominator = DIVISOR_CLK * (1 << ir_u_calc); + shaper_para->ir_b = + (ir * tick + (denominator >> 1)) / denominator; + } + } + + shaper_para->ir_u = ir_u_calc; + shaper_para->ir_s = ir_s_calc; + + return 0; +} + +static int +hns3_fill_pri_array(struct hns3_hw *hw, uint8_t *pri, uint8_t pri_id) +{ +#define HNS3_HALF_BYTE_BIT_OFFSET 4 + uint8_t tc = hw->dcb_info.prio_tc[pri_id]; + + if (tc >= hw->dcb_info.num_tc) + return -EINVAL; + + /* + * The register for priority has four bytes, the first bytes includes + * priority0 and priority1, the higher 4bit stands for priority1 + * while the lower 4bit stands for priority0, as below: + * first byte: | pri_1 | pri_0 | + * second byte: | pri_3 | pri_2 | + * third byte: | pri_5 | pri_4 | + * fourth byte: | pri_7 | pri_6 | + */ + pri[pri_id >> 1] |= tc << ((pri_id & 1) * HNS3_HALF_BYTE_BIT_OFFSET); + + return 0; +} + +static int +hns3_up_to_tc_map(struct hns3_hw *hw) +{ + struct hns3_cmd_desc desc; + uint8_t *pri = (uint8_t *)desc.data; + uint8_t pri_id; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_PRI_TO_TC_MAPPING, false); + + for (pri_id = 0; pri_id < HNS3_MAX_USER_PRIO; pri_id++) { + ret = hns3_fill_pri_array(hw, pri, pri_id); + if (ret) + return ret; + } + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_pg_to_pri_map_cfg(struct hns3_hw *hw, uint8_t pg_id, uint8_t pri_bit_map) +{ + struct hns3_pg_to_pri_link_cmd *map; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_PG_TO_PRI_LINK, false); + + map = (struct hns3_pg_to_pri_link_cmd *)desc.data; + + map->pg_id = pg_id; + map->pri_bit_map = pri_bit_map; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_pg_to_pri_map(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_pg_info *pg_info; + int ret, i; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE) + return -EINVAL; + + for (i = 0; i < hw->dcb_info.num_pg; i++) { + /* Cfg pg to priority mapping */ + pg_info = &hw->dcb_info.pg_info[i]; + ret = hns3_pg_to_pri_map_cfg(hw, i, pg_info->tc_bit_map); + if (ret) + return ret; + } + + return 0; +} + +static int +hns3_qs_to_pri_map_cfg(struct hns3_hw *hw, uint16_t qs_id, uint8_t pri) +{ + struct hns3_qs_to_pri_link_cmd *map; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_QS_TO_PRI_LINK, false); + + map = (struct hns3_qs_to_pri_link_cmd *)desc.data; + + map->qs_id = rte_cpu_to_le_16(qs_id); + map->priority = pri; + map->link_vld = HNS3_DCB_QS_PRI_LINK_VLD_MSK; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_qs_weight_cfg(struct hns3_hw *hw, uint16_t qs_id, uint8_t dwrr) +{ + struct hns3_qs_weight_cmd *weight; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_QS_WEIGHT, false); + + weight = (struct hns3_qs_weight_cmd *)desc.data; + + weight->qs_id = rte_cpu_to_le_16(qs_id); + weight->dwrr = dwrr; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_ets_tc_dwrr_cfg(struct hns3_hw *hw) +{ +#define DEFAULT_TC_WEIGHT 1 +#define DEFAULT_TC_OFFSET 14 + struct hns3_ets_tc_weight_cmd *ets_weight; + struct hns3_cmd_desc desc; + uint8_t i; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_ETS_TC_WEIGHT, false); + ets_weight = (struct hns3_ets_tc_weight_cmd *)desc.data; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + struct hns3_pg_info *pg_info; + + ets_weight->tc_weight[i] = DEFAULT_TC_WEIGHT; + + if (!(hw->hw_tc_map & BIT(i))) + continue; + + pg_info = &hw->dcb_info.pg_info[hw->dcb_info.tc_info[i].pgid]; + ets_weight->tc_weight[i] = pg_info->tc_dwrr[i]; + } + + ets_weight->weight_offset = DEFAULT_TC_OFFSET; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pri_weight_cfg(struct hns3_hw *hw, uint8_t pri_id, uint8_t dwrr) +{ + struct hns3_priority_weight_cmd *weight; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_PRI_WEIGHT, false); + + weight = (struct hns3_priority_weight_cmd *)desc.data; + + weight->pri_id = pri_id; + weight->dwrr = dwrr; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pg_weight_cfg(struct hns3_hw *hw, uint8_t pg_id, uint8_t dwrr) +{ + struct hns3_pg_weight_cmd *weight; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_PG_WEIGHT, false); + + weight = (struct hns3_pg_weight_cmd *)desc.data; + + weight->pg_id = pg_id; + weight->dwrr = dwrr; + + return hns3_cmd_send(hw, &desc, 1); +} +static int +hns3_dcb_pg_schd_mode_cfg(struct hns3_hw *hw, uint8_t pg_id) +{ + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_PG_SCH_MODE_CFG, false); + + if (hw->dcb_info.pg_info[pg_id].pg_sch_mode == HNS3_SCH_MODE_DWRR) + desc.data[1] = rte_cpu_to_le_32(HNS3_DCB_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = rte_cpu_to_le_32(pg_id); + + return hns3_cmd_send(hw, &desc, 1); +} + +static uint32_t +hns3_dcb_get_shapping_para(uint8_t ir_b, uint8_t ir_u, uint8_t ir_s, + uint8_t bs_b, uint8_t bs_s) +{ + uint32_t shapping_para = 0; + + hns3_dcb_set_field(shapping_para, IR_B, ir_b); + hns3_dcb_set_field(shapping_para, IR_U, ir_u); + hns3_dcb_set_field(shapping_para, IR_S, ir_s); + hns3_dcb_set_field(shapping_para, BS_B, bs_b); + hns3_dcb_set_field(shapping_para, BS_S, bs_s); + + return shapping_para; +} + +static int +hns3_dcb_port_shaper_cfg(struct hns3_hw *hw) +{ + struct hns3_port_shapping_cmd *shap_cfg_cmd; + struct hns3_shaper_parameter shaper_parameter; + uint32_t shapping_para; + uint32_t ir_u, ir_b, ir_s; + struct hns3_cmd_desc desc; + int ret; + + ret = hns3_shaper_para_calc(hw, hw->mac.link_speed, + HNS3_SHAPER_LVL_PORT, &shaper_parameter); + if (ret) { + hns3_err(hw, "calculate shaper parameter failed: %d", ret); + return ret; + } + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_PORT_SHAPPING, false); + shap_cfg_cmd = (struct hns3_port_shapping_cmd *)desc.data; + + ir_b = shaper_parameter.ir_b; + ir_u = shaper_parameter.ir_u; + ir_s = shaper_parameter.ir_s; + shapping_para = hns3_dcb_get_shapping_para(ir_b, ir_u, ir_s, + HNS3_SHAPER_BS_U_DEF, + HNS3_SHAPER_BS_S_DEF); + + shap_cfg_cmd->port_shapping_para = rte_cpu_to_le_32(shapping_para); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pg_shapping_cfg(struct hns3_hw *hw, enum hns3_shap_bucket bucket, + uint8_t pg_id, uint32_t shapping_para) +{ + struct hns3_pg_shapping_cmd *shap_cfg_cmd; + enum hns3_opcode_type opcode; + struct hns3_cmd_desc desc; + + opcode = bucket ? HNS3_OPC_TM_PG_P_SHAPPING : + HNS3_OPC_TM_PG_C_SHAPPING; + hns3_cmd_setup_basic_desc(&desc, opcode, false); + + shap_cfg_cmd = (struct hns3_pg_shapping_cmd *)desc.data; + + shap_cfg_cmd->pg_id = pg_id; + + shap_cfg_cmd->pg_shapping_para = rte_cpu_to_le_32(shapping_para); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pg_shaper_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_shaper_parameter shaper_parameter; + struct hns3_pf *pf = &hns->pf; + uint32_t ir_u, ir_b, ir_s; + uint32_t shaper_para; + uint8_t i; + int ret; + + /* Cfg pg schd */ + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE) + return -EINVAL; + + /* Pg to pri */ + for (i = 0; i < hw->dcb_info.num_pg; i++) { + /* Calc shaper para */ + ret = hns3_shaper_para_calc(hw, + hw->dcb_info.pg_info[i].bw_limit, + HNS3_SHAPER_LVL_PG, + &shaper_parameter); + if (ret) { + hns3_err(hw, "calculate shaper parameter failed: %d", + ret); + return ret; + } + + shaper_para = hns3_dcb_get_shapping_para(0, 0, 0, + HNS3_SHAPER_BS_U_DEF, + HNS3_SHAPER_BS_S_DEF); + + ret = hns3_dcb_pg_shapping_cfg(hw, HNS3_DCB_SHAP_C_BUCKET, i, + shaper_para); + if (ret) { + hns3_err(hw, + "config PG CIR shaper parameter failed: %d", + ret); + return ret; + } + + ir_b = shaper_parameter.ir_b; + ir_u = shaper_parameter.ir_u; + ir_s = shaper_parameter.ir_s; + shaper_para = hns3_dcb_get_shapping_para(ir_b, ir_u, ir_s, + HNS3_SHAPER_BS_U_DEF, + HNS3_SHAPER_BS_S_DEF); + + ret = hns3_dcb_pg_shapping_cfg(hw, HNS3_DCB_SHAP_P_BUCKET, i, + shaper_para); + if (ret) { + hns3_err(hw, + "config PG PIR shaper parameter failed: %d", + ret); + return ret; + } + } + + return 0; +} + +static int +hns3_dcb_qs_schd_mode_cfg(struct hns3_hw *hw, uint16_t qs_id, uint8_t mode) +{ + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_QS_SCH_MODE_CFG, false); + + if (mode == HNS3_SCH_MODE_DWRR) + desc.data[1] = rte_cpu_to_le_32(HNS3_DCB_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = rte_cpu_to_le_32(qs_id); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pri_schd_mode_cfg(struct hns3_hw *hw, uint8_t pri_id) +{ + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_PRI_SCH_MODE_CFG, false); + + if (hw->dcb_info.tc_info[pri_id].tc_sch_mode == HNS3_SCH_MODE_DWRR) + desc.data[1] = rte_cpu_to_le_32(HNS3_DCB_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = rte_cpu_to_le_32(pri_id); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pri_shapping_cfg(struct hns3_hw *hw, enum hns3_shap_bucket bucket, + uint8_t pri_id, uint32_t shapping_para) +{ + struct hns3_pri_shapping_cmd *shap_cfg_cmd; + enum hns3_opcode_type opcode; + struct hns3_cmd_desc desc; + + opcode = bucket ? HNS3_OPC_TM_PRI_P_SHAPPING : + HNS3_OPC_TM_PRI_C_SHAPPING; + + hns3_cmd_setup_basic_desc(&desc, opcode, false); + + shap_cfg_cmd = (struct hns3_pri_shapping_cmd *)desc.data; + + shap_cfg_cmd->pri_id = pri_id; + + shap_cfg_cmd->pri_shapping_para = rte_cpu_to_le_32(shapping_para); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_dcb_pri_tc_base_shaper_cfg(struct hns3_hw *hw) +{ + struct hns3_shaper_parameter shaper_parameter; + uint32_t ir_u, ir_b, ir_s; + uint32_t shaper_para; + int ret, i; + + for (i = 0; i < hw->dcb_info.num_tc; i++) { + ret = hns3_shaper_para_calc(hw, + hw->dcb_info.tc_info[i].bw_limit, + HNS3_SHAPER_LVL_PRI, + &shaper_parameter); + if (ret) { + hns3_err(hw, "calculate shaper parameter failed: %d", + ret); + return ret; + } + + shaper_para = hns3_dcb_get_shapping_para(0, 0, 0, + HNS3_SHAPER_BS_U_DEF, + HNS3_SHAPER_BS_S_DEF); + + ret = hns3_dcb_pri_shapping_cfg(hw, HNS3_DCB_SHAP_C_BUCKET, i, + shaper_para); + if (ret) { + hns3_err(hw, + "config priority CIR shaper parameter failed: %d", + ret); + return ret; + } + + ir_b = shaper_parameter.ir_b; + ir_u = shaper_parameter.ir_u; + ir_s = shaper_parameter.ir_s; + shaper_para = hns3_dcb_get_shapping_para(ir_b, ir_u, ir_s, + HNS3_SHAPER_BS_U_DEF, + HNS3_SHAPER_BS_S_DEF); + + ret = hns3_dcb_pri_shapping_cfg(hw, HNS3_DCB_SHAP_P_BUCKET, i, + shaper_para); + if (ret) { + hns3_err(hw, + "config priority PIR shaper parameter failed: %d", + ret); + return ret; + } + } + + return 0; +} + + +static int +hns3_dcb_pri_shaper_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int ret; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE) + return -EINVAL; + + ret = hns3_dcb_pri_tc_base_shaper_cfg(hw); + if (ret) + hns3_err(hw, "config port shaper failed: %d", ret); + + return ret; +} + +void +hns3_set_rss_size(struct hns3_hw *hw, uint16_t nb_rx_q) +{ + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint16_t rx_qnum_per_tc; + int i; + + rx_qnum_per_tc = nb_rx_q / hw->num_tc; + rx_qnum_per_tc = RTE_MIN(hw->rss_size_max, rx_qnum_per_tc); + if (hw->alloc_rss_size != rx_qnum_per_tc) { + hns3_info(hw, "rss size changes from %u to %u", + hw->alloc_rss_size, rx_qnum_per_tc); + hw->alloc_rss_size = rx_qnum_per_tc; + } + hw->used_rx_queues = hw->num_tc * hw->alloc_rss_size; + + /* + * When rss size is changed, we need to update rss redirection table + * maintained by driver. Besides, during the entire reset process, we + * need to ensure that the rss table information are not overwritten + * and configured directly to the hardware in the RESET_STAGE_RESTORE + * stage of the reset process. + */ + if (rte_atomic16_read(&hw->reset.resetting) == 0) { + for (i = 0; i < HNS3_RSS_IND_TBL_SIZE; i++) + rss_cfg->rss_indirection_tbl[i] = + i % hw->alloc_rss_size; + } +} + +void +hns3_tc_queue_mapping_cfg(struct hns3_hw *hw, uint16_t nb_queue) +{ + struct hns3_tc_queue_info *tc_queue; + uint8_t i; + + hw->tx_qnum_per_tc = nb_queue / hw->num_tc; + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + tc_queue = &hw->tc_queue[i]; + if (hw->hw_tc_map & BIT(i) && i < hw->num_tc) { + tc_queue->enable = true; + tc_queue->tqp_offset = i * hw->tx_qnum_per_tc; + tc_queue->tqp_count = hw->tx_qnum_per_tc; + tc_queue->tc = i; + } else { + /* Set to default queue if TC is disable */ + tc_queue->enable = false; + tc_queue->tqp_offset = 0; + tc_queue->tqp_count = 0; + tc_queue->tc = 0; + } + } + hw->used_tx_queues = hw->num_tc * hw->tx_qnum_per_tc; +} + +static void +hns3_dcb_update_tc_queue_mapping(struct hns3_hw *hw, uint16_t nb_rx_q, + uint16_t nb_tx_q) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + + hw->num_tc = hw->dcb_info.num_tc; + hns3_set_rss_size(hw, nb_rx_q); + hns3_tc_queue_mapping_cfg(hw, nb_tx_q); + + if (!hns->is_vf) + memcpy(pf->prio_tc, hw->dcb_info.prio_tc, HNS3_MAX_USER_PRIO); +} + +int +hns3_dcb_info_init(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int i, k; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE && + hw->dcb_info.num_pg != 1) + return -EINVAL; + + /* Initializing PG information */ + memset(hw->dcb_info.pg_info, 0, + sizeof(struct hns3_pg_info) * HNS3_PG_NUM); + for (i = 0; i < hw->dcb_info.num_pg; i++) { + hw->dcb_info.pg_dwrr[i] = i ? 0 : BW_MAX_PERCENT; + hw->dcb_info.pg_info[i].pg_id = i; + hw->dcb_info.pg_info[i].pg_sch_mode = HNS3_SCH_MODE_DWRR; + hw->dcb_info.pg_info[i].bw_limit = HNS3_ETHER_MAX_RATE; + + if (i != 0) + continue; + + hw->dcb_info.pg_info[i].tc_bit_map = hw->hw_tc_map; + for (k = 0; k < hw->dcb_info.num_tc; k++) + hw->dcb_info.pg_info[i].tc_dwrr[k] = BW_MAX_PERCENT; + } + + /* All UPs mapping to TC0 */ + for (i = 0; i < HNS3_MAX_USER_PRIO; i++) + hw->dcb_info.prio_tc[i] = 0; + + /* Initializing tc information */ + memset(hw->dcb_info.tc_info, 0, + sizeof(struct hns3_tc_info) * HNS3_MAX_TC_NUM); + for (i = 0; i < hw->dcb_info.num_tc; i++) { + hw->dcb_info.tc_info[i].tc_id = i; + hw->dcb_info.tc_info[i].tc_sch_mode = HNS3_SCH_MODE_DWRR; + hw->dcb_info.tc_info[i].pgid = 0; + hw->dcb_info.tc_info[i].bw_limit = + hw->dcb_info.pg_info[0].bw_limit; + } + + return 0; +} + +static int +hns3_dcb_lvl2_schd_mode_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int ret, i; + + /* Only being config on TC-Based scheduler mode */ + if (pf->tx_sch_mode == HNS3_FLAG_VNET_BASE_SCH_MODE) + return -EINVAL; + + for (i = 0; i < hw->dcb_info.num_pg; i++) { + ret = hns3_dcb_pg_schd_mode_cfg(hw, i); + if (ret) + return ret; + } + + return 0; +} + +static int +hns3_dcb_lvl34_schd_mode_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint8_t i; + int ret; + + if (pf->tx_sch_mode == HNS3_FLAG_TC_BASE_SCH_MODE) { + for (i = 0; i < hw->dcb_info.num_tc; i++) { + ret = hns3_dcb_pri_schd_mode_cfg(hw, i); + if (ret) + return ret; + + ret = hns3_dcb_qs_schd_mode_cfg(hw, i, + HNS3_SCH_MODE_DWRR); + if (ret) + return ret; + } + } + + return 0; +} + +static int +hns3_dcb_schd_mode_cfg(struct hns3_hw *hw) +{ + int ret; + + ret = hns3_dcb_lvl2_schd_mode_cfg(hw); + if (ret) { + hns3_err(hw, "config lvl2_schd_mode failed: %d", ret); + return ret; + } + + ret = hns3_dcb_lvl34_schd_mode_cfg(hw); + if (ret) + hns3_err(hw, "config lvl34_schd_mode failed: %d", ret); + + return ret; +} + +static int +hns3_dcb_pri_tc_base_dwrr_cfg(struct hns3_hw *hw) +{ + struct hns3_pg_info *pg_info; + uint8_t dwrr; + int ret, i; + + for (i = 0; i < hw->dcb_info.num_tc; i++) { + pg_info = &hw->dcb_info.pg_info[hw->dcb_info.tc_info[i].pgid]; + dwrr = pg_info->tc_dwrr[i]; + + ret = hns3_dcb_pri_weight_cfg(hw, i, dwrr); + if (ret) { + hns3_err(hw, + "fail to send priority weight cmd: %d, ret = %d", + i, ret); + return ret; + } + + ret = hns3_dcb_qs_weight_cfg(hw, i, BW_MAX_PERCENT); + if (ret) { + hns3_err(hw, "fail to send qs_weight cmd: %d, ret = %d", + i, ret); + return ret; + } + } + + return 0; +} + +static int +hns3_dcb_pri_dwrr_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint32_t version; + int ret; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE) + return -EINVAL; + + ret = hns3_dcb_pri_tc_base_dwrr_cfg(hw); + if (ret) + return ret; + + if (!hns3_dev_dcb_supported(hw)) + return 0; + + ret = hns3_dcb_ets_tc_dwrr_cfg(hw); + if (ret == -EOPNOTSUPP) { + version = hw->fw_version; + hns3_warn(hw, + "fw %lu.%lu.%lu.%lu doesn't support ets tc weight cmd", + hns3_get_field(version, HNS3_FW_VERSION_BYTE3_M, + HNS3_FW_VERSION_BYTE3_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE2_M, + HNS3_FW_VERSION_BYTE2_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE1_M, + HNS3_FW_VERSION_BYTE1_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE0_M, + HNS3_FW_VERSION_BYTE0_S)); + ret = 0; + } + + return ret; +} + +static int +hns3_dcb_pg_dwrr_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int ret, i; + + /* Cfg pg schd */ + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE) + return -EINVAL; + + /* Cfg pg to prio */ + for (i = 0; i < hw->dcb_info.num_pg; i++) { + /* Cfg dwrr */ + ret = hns3_dcb_pg_weight_cfg(hw, i, hw->dcb_info.pg_dwrr[i]); + if (ret) + return ret; + } + + return 0; +} + +static int +hns3_dcb_dwrr_cfg(struct hns3_hw *hw) +{ + int ret; + + ret = hns3_dcb_pg_dwrr_cfg(hw); + if (ret) { + hns3_err(hw, "config pg_dwrr failed: %d", ret); + return ret; + } + + ret = hns3_dcb_pri_dwrr_cfg(hw); + if (ret) + hns3_err(hw, "config pri_dwrr failed: %d", ret); + + return ret; +} + +static int +hns3_dcb_shaper_cfg(struct hns3_hw *hw) +{ + int ret; + + ret = hns3_dcb_port_shaper_cfg(hw); + if (ret) { + hns3_err(hw, "config port shaper failed: %d", ret); + return ret; + } + + ret = hns3_dcb_pg_shaper_cfg(hw); + if (ret) { + hns3_err(hw, "config pg shaper failed: %d", ret); + return ret; + } + + return hns3_dcb_pri_shaper_cfg(hw); +} + +static int +hns3_q_to_qs_map_cfg(struct hns3_hw *hw, uint16_t q_id, uint16_t qs_id) +{ + struct hns3_nq_to_qs_link_cmd *map; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_NQ_TO_QS_LINK, false); + + map = (struct hns3_nq_to_qs_link_cmd *)desc.data; + + map->nq_id = rte_cpu_to_le_16(q_id); + map->qset_id = rte_cpu_to_le_16(qs_id | HNS3_DCB_Q_QS_LINK_VLD_MSK); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_q_to_qs_map(struct hns3_hw *hw) +{ + struct hns3_tc_queue_info *tc_queue; + uint16_t q_id; + uint32_t i, j; + int ret; + + for (i = 0; i < hw->num_tc; i++) { + tc_queue = &hw->tc_queue[i]; + for (j = 0; j < tc_queue->tqp_count; j++) { + q_id = tc_queue->tqp_offset + j; + ret = hns3_q_to_qs_map_cfg(hw, q_id, i); + if (ret) + return ret; + } + } + + return 0; +} + +static int +hns3_pri_q_qs_cfg(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint32_t i; + int ret; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE) + return -EINVAL; + + /* Cfg qs -> pri mapping */ + for (i = 0; i < hw->num_tc; i++) { + ret = hns3_qs_to_pri_map_cfg(hw, i, i); + if (ret) { + hns3_err(hw, "qs_to_pri mapping fail: %d", ret); + return ret; + } + } + + /* Cfg q -> qs mapping */ + ret = hns3_q_to_qs_map(hw); + if (ret) + hns3_err(hw, "nq_to_qs mapping fail: %d", ret); + + return ret; +} + +static int +hns3_dcb_map_cfg(struct hns3_hw *hw) +{ + int ret; + + ret = hns3_up_to_tc_map(hw); + if (ret) { + hns3_err(hw, "up_to_tc mapping fail: %d", ret); + return ret; + } + + ret = hns3_pg_to_pri_map(hw); + if (ret) { + hns3_err(hw, "pri_to_pg mapping fail: %d", ret); + return ret; + } + + return hns3_pri_q_qs_cfg(hw); +} + +static int +hns3_dcb_schd_setup_hw(struct hns3_hw *hw) +{ + int ret; + + /* Cfg dcb mapping */ + ret = hns3_dcb_map_cfg(hw); + if (ret) + return ret; + + /* Cfg dcb shaper */ + ret = hns3_dcb_shaper_cfg(hw); + if (ret) + return ret; + + /* Cfg dwrr */ + ret = hns3_dcb_dwrr_cfg(hw); + if (ret) + return ret; + + /* Cfg schd mode for each level schd */ + return hns3_dcb_schd_mode_cfg(hw); +} + +static int +hns3_pause_param_cfg(struct hns3_hw *hw, const uint8_t *addr, + uint8_t pause_trans_gap, uint16_t pause_trans_time) +{ + struct hns3_cfg_pause_param_cmd *pause_param; + struct hns3_cmd_desc desc; + + pause_param = (struct hns3_cfg_pause_param_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_MAC_PARA, false); + + memcpy(pause_param->mac_addr, addr, RTE_ETHER_ADDR_LEN); + memcpy(pause_param->mac_addr_extra, addr, RTE_ETHER_ADDR_LEN); + pause_param->pause_trans_gap = pause_trans_gap; + pause_param->pause_trans_time = rte_cpu_to_le_16(pause_trans_time); + + return hns3_cmd_send(hw, &desc, 1); +} + +int +hns3_pause_addr_cfg(struct hns3_hw *hw, const uint8_t *mac_addr) +{ + struct hns3_cfg_pause_param_cmd *pause_param; + struct hns3_cmd_desc desc; + uint16_t trans_time; + uint8_t trans_gap; + int ret; + + pause_param = (struct hns3_cfg_pause_param_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_MAC_PARA, true); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + return ret; + + trans_gap = pause_param->pause_trans_gap; + trans_time = rte_le_to_cpu_16(pause_param->pause_trans_time); + + return hns3_pause_param_cfg(hw, mac_addr, trans_gap, trans_time); +} + +static int +hns3_pause_param_setup_hw(struct hns3_hw *hw, uint16_t pause_time) +{ +#define PAUSE_TIME_DIV_BY 2 +#define PAUSE_TIME_MIN_VALUE 0x4 + + struct hns3_mac *mac = &hw->mac; + uint8_t pause_trans_gap; + + /* + * Pause transmit gap must be less than "pause_time / 2", otherwise + * the behavior of MAC is undefined. + */ + if (pause_time > PAUSE_TIME_DIV_BY * HNS3_DEFAULT_PAUSE_TRANS_GAP) + pause_trans_gap = HNS3_DEFAULT_PAUSE_TRANS_GAP; + else if (pause_time >= PAUSE_TIME_MIN_VALUE && + pause_time <= PAUSE_TIME_DIV_BY * HNS3_DEFAULT_PAUSE_TRANS_GAP) + pause_trans_gap = pause_time / PAUSE_TIME_DIV_BY - 1; + else { + hns3_warn(hw, "pause_time(%d) is adjusted to 4", pause_time); + pause_time = PAUSE_TIME_MIN_VALUE; + pause_trans_gap = pause_time / PAUSE_TIME_DIV_BY - 1; + } + + return hns3_pause_param_cfg(hw, mac->mac_addr, + pause_trans_gap, pause_time); +} + +static int +hns3_mac_pause_en_cfg(struct hns3_hw *hw, bool tx, bool rx) +{ + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_MAC_PAUSE_EN, false); + + desc.data[0] = rte_cpu_to_le_32((tx ? HNS3_TX_MAC_PAUSE_EN_MSK : 0) | + (rx ? HNS3_RX_MAC_PAUSE_EN_MSK : 0)); + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_pfc_pause_en_cfg(struct hns3_hw *hw, uint8_t pfc_bitmap, bool tx, bool rx) +{ + struct hns3_cmd_desc desc; + struct hns3_pfc_en_cmd *pfc = (struct hns3_pfc_en_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_PFC_PAUSE_EN, false); + + pfc->tx_rx_en_bitmap = (uint8_t)((tx ? HNS3_TX_MAC_PAUSE_EN_MSK : 0) | + (rx ? HNS3_RX_MAC_PAUSE_EN_MSK : 0)); + + pfc->pri_en_bitmap = pfc_bitmap; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_qs_bp_cfg(struct hns3_hw *hw, uint8_t tc, uint8_t grp_id, uint32_t bit_map) +{ + struct hns3_bp_to_qs_map_cmd *bp_to_qs_map_cmd; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TM_BP_TO_QSET_MAPPING, false); + + bp_to_qs_map_cmd = (struct hns3_bp_to_qs_map_cmd *)desc.data; + + bp_to_qs_map_cmd->tc_id = tc; + bp_to_qs_map_cmd->qs_group_id = grp_id; + bp_to_qs_map_cmd->qs_bit_map = rte_cpu_to_le_32(bit_map); + + return hns3_cmd_send(hw, &desc, 1); +} + +static void +hns3_get_rx_tx_en_status(struct hns3_hw *hw, bool *tx_en, bool *rx_en) +{ + switch (hw->current_mode) { + case HNS3_FC_NONE: + *tx_en = false; + *rx_en = false; + break; + case HNS3_FC_RX_PAUSE: + *tx_en = false; + *rx_en = true; + break; + case HNS3_FC_TX_PAUSE: + *tx_en = true; + *rx_en = false; + break; + case HNS3_FC_FULL: + *tx_en = true; + *rx_en = true; + break; + default: + *tx_en = false; + *rx_en = false; + break; + } +} + +static int +hns3_mac_pause_setup_hw(struct hns3_hw *hw) +{ + bool tx_en, rx_en; + + if (hw->current_fc_status == HNS3_FC_STATUS_MAC_PAUSE) + hns3_get_rx_tx_en_status(hw, &tx_en, &rx_en); + else { + tx_en = false; + rx_en = false; + } + + return hns3_mac_pause_en_cfg(hw, tx_en, rx_en); +} + +static int +hns3_pfc_setup_hw(struct hns3_hw *hw) +{ + bool tx_en, rx_en; + + if (hw->current_fc_status == HNS3_FC_STATUS_PFC) + hns3_get_rx_tx_en_status(hw, &tx_en, &rx_en); + else { + tx_en = false; + rx_en = false; + } + + return hns3_pfc_pause_en_cfg(hw, hw->dcb_info.pfc_en, tx_en, rx_en); +} + +/* + * Each Tc has a 1024 queue sets to backpress, it divides to + * 32 group, each group contains 32 queue sets, which can be + * represented by uint32_t bitmap. + */ +static int +hns3_bp_setup_hw(struct hns3_hw *hw, uint8_t tc) +{ + uint32_t qs_bitmap; + int ret; + int i; + + for (i = 0; i < HNS3_BP_GRP_NUM; i++) { + uint8_t grp, sub_grp; + qs_bitmap = 0; + + grp = hns3_get_field(tc, HNS3_BP_GRP_ID_M, HNS3_BP_GRP_ID_S); + sub_grp = hns3_get_field(tc, HNS3_BP_SUB_GRP_ID_M, + HNS3_BP_SUB_GRP_ID_S); + if (i == grp) + qs_bitmap |= (1 << sub_grp); + + ret = hns3_qs_bp_cfg(hw, tc, i, qs_bitmap); + if (ret) + return ret; + } + + return 0; +} + +static int +hns3_dcb_bp_setup(struct hns3_hw *hw) +{ + int ret, i; + + for (i = 0; i < hw->dcb_info.num_tc; i++) { + ret = hns3_bp_setup_hw(hw, i); + if (ret) + return ret; + } + + return 0; +} + +static int +hns3_dcb_pause_setup_hw(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int ret; + + ret = hns3_pause_param_setup_hw(hw, pf->pause_time); + if (ret) { + hns3_err(hw, "Fail to set pause parameter. ret = %d", ret); + return ret; + } + + ret = hns3_mac_pause_setup_hw(hw); + if (ret) { + hns3_err(hw, "Fail to setup MAC pause. ret = %d", ret); + return ret; + } + + /* Only DCB-supported dev supports qset back pressure and pfc cmd */ + if (!hns3_dev_dcb_supported(hw)) + return 0; + + ret = hns3_pfc_setup_hw(hw); + if (ret) { + hns3_err(hw, "config pfc failed! ret = %d", ret); + return ret; + } + + return hns3_dcb_bp_setup(hw); +} + +static uint8_t +hns3_dcb_undrop_tc_map(struct hns3_hw *hw, uint8_t pfc_en) +{ + uint8_t pfc_map = 0; + uint8_t *prio_tc; + uint8_t i, j; + + prio_tc = hw->dcb_info.prio_tc; + for (i = 0; i < hw->dcb_info.num_tc; i++) { + for (j = 0; j < HNS3_MAX_USER_PRIO; j++) { + if (prio_tc[j] == i && pfc_en & BIT(j)) { + pfc_map |= BIT(i); + break; + } + } + } + + return pfc_map; +} + +static void +hns3_dcb_cfg_validate(struct hns3_adapter *hns, uint8_t *tc, bool *changed) +{ + struct rte_eth_dcb_rx_conf *dcb_rx_conf; + struct hns3_hw *hw = &hns->hw; + uint8_t max_tc = 0; + uint8_t pfc_en; + int i; + + dcb_rx_conf = &hw->data->dev_conf.rx_adv_conf.dcb_rx_conf; + for (i = 0; i < HNS3_MAX_USER_PRIO; i++) { + if (dcb_rx_conf->dcb_tc[i] != hw->dcb_info.prio_tc[i]) + *changed = true; + + if (dcb_rx_conf->dcb_tc[i] > max_tc) + max_tc = dcb_rx_conf->dcb_tc[i]; + } + *tc = max_tc + 1; + if (*tc != hw->dcb_info.num_tc) + *changed = true; + + /* + * We ensure that dcb information can be reconfigured + * after the hns3_priority_flow_ctrl_set function called. + */ + if (hw->current_mode != HNS3_FC_FULL) + *changed = true; + pfc_en = RTE_LEN2MASK((uint8_t)dcb_rx_conf->nb_tcs, uint8_t); + if (hw->dcb_info.pfc_en != pfc_en) + *changed = true; +} + +static void +hns3_dcb_info_cfg(struct hns3_adapter *hns) +{ + struct rte_eth_dcb_rx_conf *dcb_rx_conf; + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + uint8_t tc_bw, bw_rest; + uint8_t i, j; + + dcb_rx_conf = &hw->data->dev_conf.rx_adv_conf.dcb_rx_conf; + pf->local_max_tc = (uint8_t)dcb_rx_conf->nb_tcs; + pf->pfc_max = (uint8_t)dcb_rx_conf->nb_tcs; + + /* Config pg0 */ + memset(hw->dcb_info.pg_info, 0, + sizeof(struct hns3_pg_info) * HNS3_PG_NUM); + hw->dcb_info.pg_dwrr[0] = BW_MAX_PERCENT; + hw->dcb_info.pg_info[0].pg_id = 0; + hw->dcb_info.pg_info[0].pg_sch_mode = HNS3_SCH_MODE_DWRR; + hw->dcb_info.pg_info[0].bw_limit = HNS3_ETHER_MAX_RATE; + hw->dcb_info.pg_info[0].tc_bit_map = hw->hw_tc_map; + + /* Each tc has same bw for valid tc by default */ + tc_bw = BW_MAX_PERCENT / hw->dcb_info.num_tc; + for (i = 0; i < hw->dcb_info.num_tc; i++) + hw->dcb_info.pg_info[0].tc_dwrr[i] = tc_bw; + /* To ensure the sum of tc_dwrr is equal to 100 */ + bw_rest = BW_MAX_PERCENT % hw->dcb_info.num_tc; + for (j = 0; j < bw_rest; j++) + hw->dcb_info.pg_info[0].tc_dwrr[j]++; + for (; i < dcb_rx_conf->nb_tcs; i++) + hw->dcb_info.pg_info[0].tc_dwrr[i] = 0; + + /* All tcs map to pg0 */ + memset(hw->dcb_info.tc_info, 0, + sizeof(struct hns3_tc_info) * HNS3_MAX_TC_NUM); + for (i = 0; i < hw->dcb_info.num_tc; i++) { + hw->dcb_info.tc_info[i].tc_id = i; + hw->dcb_info.tc_info[i].tc_sch_mode = HNS3_SCH_MODE_DWRR; + hw->dcb_info.tc_info[i].pgid = 0; + hw->dcb_info.tc_info[i].bw_limit = + hw->dcb_info.pg_info[0].bw_limit; + } + + for (i = 0; i < HNS3_MAX_USER_PRIO; i++) + hw->dcb_info.prio_tc[i] = dcb_rx_conf->dcb_tc[i]; + + hns3_dcb_update_tc_queue_mapping(hw, hw->data->nb_rx_queues, + hw->data->nb_tx_queues); +} + +static int +hns3_dcb_info_update(struct hns3_adapter *hns, uint8_t num_tc) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + uint16_t nb_rx_q = hw->data->nb_rx_queues; + uint16_t nb_tx_q = hw->data->nb_tx_queues; + uint8_t bit_map = 0; + uint8_t i; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE && + hw->dcb_info.num_pg != 1) + return -EINVAL; + + if (nb_rx_q < num_tc) { + hns3_err(hw, "number of Rx queues(%d) is less than tcs(%d).", + nb_rx_q, num_tc); + return -EINVAL; + } + + if (nb_tx_q < num_tc) { + hns3_err(hw, "number of Tx queues(%d) is less than tcs(%d).", + nb_tx_q, num_tc); + return -EINVAL; + } + + /* Currently not support uncontinuous tc */ + hw->dcb_info.num_tc = num_tc; + for (i = 0; i < hw->dcb_info.num_tc; i++) + bit_map |= BIT(i); + + if (!bit_map) { + bit_map = 1; + hw->dcb_info.num_tc = 1; + } + hw->hw_tc_map = bit_map; + hns3_dcb_info_cfg(hns); + + return 0; +} + +static int +hns3_dcb_hw_configure(struct hns3_adapter *hns) +{ + struct rte_eth_dcb_rx_conf *dcb_rx_conf; + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + enum hns3_fc_status fc_status = hw->current_fc_status; + enum hns3_fc_mode current_mode = hw->current_mode; + uint8_t hw_pfc_map = hw->dcb_info.hw_pfc_map; + int ret, status; + + if (pf->tx_sch_mode != HNS3_FLAG_TC_BASE_SCH_MODE && + pf->tx_sch_mode != HNS3_FLAG_VNET_BASE_SCH_MODE) + return -ENOTSUP; + + ret = hns3_dcb_schd_setup_hw(hw); + if (ret) { + hns3_err(hw, "dcb schdule configure failed! ret = %d", ret); + return ret; + } + + if (hw->data->dev_conf.dcb_capability_en & ETH_DCB_PFC_SUPPORT) { + dcb_rx_conf = &hw->data->dev_conf.rx_adv_conf.dcb_rx_conf; + if (dcb_rx_conf->nb_tcs == 0) + hw->dcb_info.pfc_en = 1; /* tc0 only */ + else + hw->dcb_info.pfc_en = + RTE_LEN2MASK((uint8_t)dcb_rx_conf->nb_tcs, uint8_t); + + hw->dcb_info.hw_pfc_map = + hns3_dcb_undrop_tc_map(hw, hw->dcb_info.pfc_en); + + ret = hns3_buffer_alloc(hw); + if (ret) + return ret; + + hw->current_fc_status = HNS3_FC_STATUS_PFC; + hw->current_mode = HNS3_FC_FULL; + ret = hns3_dcb_pause_setup_hw(hw); + if (ret) { + hns3_err(hw, "setup pfc failed! ret = %d", ret); + goto pfc_setup_fail; + } + } else { + /* + * Although dcb_capability_en is lack of ETH_DCB_PFC_SUPPORT + * flag, the DCB information is configured, such as tc numbers. + * Therefore, refreshing the allocation of packet buffer is + * necessary. + */ + ret = hns3_buffer_alloc(hw); + if (ret) + return ret; + } + + return 0; + +pfc_setup_fail: + hw->current_mode = current_mode; + hw->current_fc_status = fc_status; + hw->dcb_info.hw_pfc_map = hw_pfc_map; + status = hns3_buffer_alloc(hw); + if (status) + hns3_err(hw, "recover packet buffer fail! status = %d", status); + + return ret; +} + +/* + * hns3_dcb_configure - setup dcb related config + * @hns: pointer to hns3 adapter + * Returns 0 on success, negative value on failure. + */ +int +hns3_dcb_configure(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + bool map_changed = false; + uint8_t num_tc = 0; + int ret; + + hns3_dcb_cfg_validate(hns, &num_tc, &map_changed); + if (map_changed || rte_atomic16_read(&hw->reset.resetting)) { + ret = hns3_dcb_info_update(hns, num_tc); + if (ret) { + hns3_err(hw, "dcb info update failed: %d", ret); + return ret; + } + + ret = hns3_dcb_hw_configure(hns); + if (ret) { + hns3_err(hw, "dcb sw configure failed: %d", ret); + return ret; + } + } + + return 0; +} + +int +hns3_dcb_init_hw(struct hns3_hw *hw) +{ + int ret; + + ret = hns3_dcb_schd_setup_hw(hw); + if (ret) { + hns3_err(hw, "dcb schedule setup failed: %d", ret); + return ret; + } + + ret = hns3_dcb_pause_setup_hw(hw); + if (ret) + hns3_err(hw, "PAUSE setup failed: %d", ret); + + return ret; +} + +int +hns3_dcb_init(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int ret; + + PMD_INIT_FUNC_TRACE(); + + /* + * According to the 'adapter_state' identifier, the following branch + * is only executed to initialize default configurations of dcb during + * the initializing driver process. Due to driver saving dcb-related + * information before reset triggered, the reinit dev stage of the + * reset process can not access to the branch, or those information + * will be changed. + */ + if (hw->adapter_state == HNS3_NIC_UNINITIALIZED) { + hw->requested_mode = HNS3_FC_NONE; + hw->current_mode = hw->requested_mode; + pf->pause_time = HNS3_DEFAULT_PAUSE_TRANS_TIME; + hw->current_fc_status = HNS3_FC_STATUS_NONE; + + ret = hns3_dcb_info_init(hw); + if (ret) { + hns3_err(hw, "dcb info init failed: %d", ret); + return ret; + } + hns3_dcb_update_tc_queue_mapping(hw, hw->tqps_num, + hw->tqps_num); + } + + /* + * DCB hardware will be configured by following the function during + * the initializing driver process and the reset process. However, + * driver will restore directly configurations of dcb hardware based + * on dcb-related information soft maintained when driver + * initialization has finished and reset is coming. + */ + ret = hns3_dcb_init_hw(hw); + if (ret) { + hns3_err(hw, "dcb init hardware failed: %d", ret); + return ret; + } + + return 0; +} + +static int +hns3_update_queue_map_configure(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + uint16_t nb_rx_q = hw->data->nb_rx_queues; + uint16_t nb_tx_q = hw->data->nb_tx_queues; + int ret; + + hns3_dcb_update_tc_queue_mapping(hw, nb_rx_q, nb_tx_q); + ret = hns3_q_to_qs_map(hw); + if (ret) + hns3_err(hw, "failed to map nq to qs! ret = %d", ret); + + return ret; +} + +int +hns3_dcb_cfg_update(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + enum rte_eth_rx_mq_mode mq_mode = hw->data->dev_conf.rxmode.mq_mode; + int ret; + + if ((uint32_t)mq_mode & ETH_MQ_RX_DCB_FLAG) { + ret = hns3_dcb_configure(hns); + if (ret) + hns3_err(hw, "Failed to config dcb: %d", ret); + } else { + /* + * Update queue map without PFC configuration, + * due to queues reconfigured by user. + */ + ret = hns3_update_queue_map_configure(hns); + if (ret) + hns3_err(hw, + "Failed to update queue mapping configure: %d", + ret); + } + + return ret; +} + +/* + * hns3_dcb_pfc_enable - Enable priority flow control + * @dev: pointer to ethernet device + * + * Configures the pfc settings for one porority. + */ +int +hns3_dcb_pfc_enable(struct rte_eth_dev *dev, struct rte_eth_pfc_conf *pfc_conf) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + enum hns3_fc_status fc_status = hw->current_fc_status; + enum hns3_fc_mode current_mode = hw->current_mode; + uint8_t hw_pfc_map = hw->dcb_info.hw_pfc_map; + uint8_t pfc_en = hw->dcb_info.pfc_en; + uint8_t priority = pfc_conf->priority; + uint16_t pause_time = pf->pause_time; + int ret, status; + + pf->pause_time = pfc_conf->fc.pause_time; + hw->current_mode = hw->requested_mode; + hw->current_fc_status = HNS3_FC_STATUS_PFC; + hw->dcb_info.pfc_en |= BIT(priority); + hw->dcb_info.hw_pfc_map = + hns3_dcb_undrop_tc_map(hw, hw->dcb_info.pfc_en); + ret = hns3_buffer_alloc(hw); + if (ret) + goto pfc_setup_fail; + + /* + * The flow control mode of all UPs will be changed based on + * current_mode coming from user. + */ + ret = hns3_dcb_pause_setup_hw(hw); + if (ret) { + hns3_err(hw, "enable pfc failed! ret = %d", ret); + goto pfc_setup_fail; + } + + return 0; + +pfc_setup_fail: + hw->current_mode = current_mode; + hw->current_fc_status = fc_status; + pf->pause_time = pause_time; + hw->dcb_info.pfc_en = pfc_en; + hw->dcb_info.hw_pfc_map = hw_pfc_map; + status = hns3_buffer_alloc(hw); + if (status) + hns3_err(hw, "recover packet buffer fail: %d", status); + + return ret; +} + +/* + * hns3_fc_enable - Enable MAC pause + * @dev: pointer to ethernet device + * + * Configures the MAC pause settings. + */ +int +hns3_fc_enable(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + enum hns3_fc_status fc_status = hw->current_fc_status; + enum hns3_fc_mode current_mode = hw->current_mode; + uint16_t pause_time = pf->pause_time; + int ret; + + pf->pause_time = fc_conf->pause_time; + hw->current_mode = hw->requested_mode; + + /* + * In fact, current_fc_status is HNS3_FC_STATUS_NONE when mode + * of flow control is configured to be HNS3_FC_NONE. + */ + if (hw->current_mode == HNS3_FC_NONE) + hw->current_fc_status = HNS3_FC_STATUS_NONE; + else + hw->current_fc_status = HNS3_FC_STATUS_MAC_PAUSE; + + ret = hns3_dcb_pause_setup_hw(hw); + if (ret) { + hns3_err(hw, "enable MAC Pause failed! ret = %d", ret); + goto setup_fc_fail; + } + + return 0; + +setup_fc_fail: + hw->current_mode = current_mode; + hw->current_fc_status = fc_status; + pf->pause_time = pause_time; + + return ret; +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_dcb.h b/src/spdk/dpdk/drivers/net/hns3/hns3_dcb.h new file mode 100644 index 000000000..9c2c5f21c --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_dcb.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_DCB_H_ +#define _HNS3_DCB_H_ + +/* MAC Pause */ +#define HNS3_TX_MAC_PAUSE_EN_MSK BIT(0) +#define HNS3_RX_MAC_PAUSE_EN_MSK BIT(1) + +#define HNS3_DEFAULT_PAUSE_TRANS_GAP 0x18 +#define HNS3_DEFAULT_PAUSE_TRANS_TIME 0xFFFF + +/* SP or DWRR */ +#define HNS3_DCB_TX_SCHD_DWRR_MSK BIT(0) +#define HNS3_DCB_TX_SCHD_SP_MSK (0xFE) + +enum hns3_shap_bucket { + HNS3_DCB_SHAP_C_BUCKET = 0, + HNS3_DCB_SHAP_P_BUCKET, +}; + +struct hns3_priority_weight_cmd { + uint8_t pri_id; + uint8_t dwrr; +}; + +struct hns3_qs_weight_cmd { + uint16_t qs_id; + uint8_t dwrr; +}; + +struct hns3_pg_weight_cmd { + uint8_t pg_id; + uint8_t dwrr; +}; + +struct hns3_ets_tc_weight_cmd { + uint8_t tc_weight[HNS3_MAX_TC_NUM]; + uint8_t weight_offset; + uint8_t rsvd[15]; +}; + +struct hns3_qs_to_pri_link_cmd { + uint16_t qs_id; + uint16_t rsvd; + uint8_t priority; +#define HNS3_DCB_QS_PRI_LINK_VLD_MSK BIT(0) + uint8_t link_vld; +}; + +struct hns3_nq_to_qs_link_cmd { + uint16_t nq_id; + uint16_t rsvd; +#define HNS3_DCB_Q_QS_LINK_VLD_MSK BIT(10) + uint16_t qset_id; +}; + +#define HNS3_DCB_SHAP_IR_B_MSK GENMASK(7, 0) +#define HNS3_DCB_SHAP_IR_B_LSH 0 +#define HNS3_DCB_SHAP_IR_U_MSK GENMASK(11, 8) +#define HNS3_DCB_SHAP_IR_U_LSH 8 +#define HNS3_DCB_SHAP_IR_S_MSK GENMASK(15, 12) +#define HNS3_DCB_SHAP_IR_S_LSH 12 +#define HNS3_DCB_SHAP_BS_B_MSK GENMASK(20, 16) +#define HNS3_DCB_SHAP_BS_B_LSH 16 +#define HNS3_DCB_SHAP_BS_S_MSK GENMASK(25, 21) +#define HNS3_DCB_SHAP_BS_S_LSH 21 + +struct hns3_pri_shapping_cmd { + uint8_t pri_id; + uint8_t rsvd[3]; + uint32_t pri_shapping_para; +}; + +struct hns3_pg_shapping_cmd { + uint8_t pg_id; + uint8_t rsvd[3]; + uint32_t pg_shapping_para; +}; + +#define HNS3_BP_GRP_NUM 32 +#define HNS3_BP_SUB_GRP_ID_S 0 +#define HNS3_BP_SUB_GRP_ID_M GENMASK(4, 0) +#define HNS3_BP_GRP_ID_S 5 +#define HNS3_BP_GRP_ID_M GENMASK(9, 5) +struct hns3_bp_to_qs_map_cmd { + uint8_t tc_id; + uint8_t rsvd[2]; + uint8_t qs_group_id; + uint32_t qs_bit_map; + uint32_t rsvd1; +}; + +struct hns3_pfc_en_cmd { + uint8_t tx_rx_en_bitmap; + uint8_t pri_en_bitmap; +}; + +struct hns3_port_shapping_cmd { + uint32_t port_shapping_para; +}; + +struct hns3_cfg_pause_param_cmd { + uint8_t mac_addr[RTE_ETHER_ADDR_LEN]; + uint8_t pause_trans_gap; + uint8_t rsvd; + uint16_t pause_trans_time; + uint8_t rsvd1[6]; + /* extra mac address to do double check for pause frame */ + uint8_t mac_addr_extra[RTE_ETHER_ADDR_LEN]; + uint16_t rsvd2; +}; + +struct hns3_pg_to_pri_link_cmd { + uint8_t pg_id; + uint8_t rsvd1[3]; + uint8_t pri_bit_map; +}; + +enum hns3_shaper_level { + HNS3_SHAPER_LVL_PRI = 0, + HNS3_SHAPER_LVL_PG = 1, + HNS3_SHAPER_LVL_PORT = 2, + HNS3_SHAPER_LVL_QSET = 3, + HNS3_SHAPER_LVL_CNT = 4, + HNS3_SHAPER_LVL_VF = 0, + HNS3_SHAPER_LVL_PF = 1, +}; + +struct hns3_shaper_parameter { + uint32_t ir_b; /* IR_B parameter of IR shaper */ + uint32_t ir_u; /* IR_U parameter of IR shaper */ + uint32_t ir_s; /* IR_S parameter of IR shaper */ +}; + +#define hns3_dcb_set_field(dest, string, val) \ + hns3_set_field((dest), \ + (HNS3_DCB_SHAP_##string##_MSK), \ + (HNS3_DCB_SHAP_##string##_LSH), val) +#define hns3_dcb_get_field(src, string) \ + hns3_get_field((src), (HNS3_DCB_SHAP_##string##_MSK), \ + (HNS3_DCB_SHAP_##string##_LSH)) + +int hns3_pause_addr_cfg(struct hns3_hw *hw, const uint8_t *mac_addr); + +int hns3_dcb_configure(struct hns3_adapter *hns); + +int hns3_dcb_init(struct hns3_hw *hw); + +int hns3_dcb_init_hw(struct hns3_hw *hw); + +int hns3_dcb_info_init(struct hns3_hw *hw); + +int +hns3_fc_enable(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf); + +int +hns3_dcb_pfc_enable(struct rte_eth_dev *dev, struct rte_eth_pfc_conf *pfc_conf); + +void hns3_set_rss_size(struct hns3_hw *hw, uint16_t nb_rx_q); + +void hns3_tc_queue_mapping_cfg(struct hns3_hw *hw, uint16_t nb_queue); + +int hns3_dcb_cfg_update(struct hns3_adapter *hns); + +#endif /* _HNS3_DCB_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev.c b/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev.c new file mode 100644 index 000000000..a09ac082e --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev.c @@ -0,0 +1,5512 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> +#include <unistd.h> +#include <rte_atomic.h> +#include <rte_bus_pci.h> +#include <rte_common.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_eal.h> +#include <rte_ether.h> +#include <rte_ethdev_driver.h> +#include <rte_ethdev_pci.h> +#include <rte_interrupts.h> +#include <rte_io.h> +#include <rte_log.h> +#include <rte_pci.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" +#include "hns3_rxtx.h" +#include "hns3_intr.h" +#include "hns3_regs.h" +#include "hns3_dcb.h" +#include "hns3_mp.h" + +#define HNS3_DEFAULT_PORT_CONF_BURST_SIZE 32 +#define HNS3_DEFAULT_PORT_CONF_QUEUES_NUM 1 + +#define HNS3_SERVICE_INTERVAL 1000000 /* us */ +#define HNS3_PORT_BASE_VLAN_DISABLE 0 +#define HNS3_PORT_BASE_VLAN_ENABLE 1 +#define HNS3_INVLID_PVID 0xFFFF + +#define HNS3_FILTER_TYPE_VF 0 +#define HNS3_FILTER_TYPE_PORT 1 +#define HNS3_FILTER_FE_EGRESS_V1_B BIT(0) +#define HNS3_FILTER_FE_NIC_INGRESS_B BIT(0) +#define HNS3_FILTER_FE_NIC_EGRESS_B BIT(1) +#define HNS3_FILTER_FE_ROCE_INGRESS_B BIT(2) +#define HNS3_FILTER_FE_ROCE_EGRESS_B BIT(3) +#define HNS3_FILTER_FE_EGRESS (HNS3_FILTER_FE_NIC_EGRESS_B \ + | HNS3_FILTER_FE_ROCE_EGRESS_B) +#define HNS3_FILTER_FE_INGRESS (HNS3_FILTER_FE_NIC_INGRESS_B \ + | HNS3_FILTER_FE_ROCE_INGRESS_B) + +/* Reset related Registers */ +#define HNS3_GLOBAL_RESET_BIT 0 +#define HNS3_CORE_RESET_BIT 1 +#define HNS3_IMP_RESET_BIT 2 +#define HNS3_FUN_RST_ING_B 0 + +#define HNS3_VECTOR0_IMP_RESET_INT_B 1 + +#define HNS3_RESET_WAIT_MS 100 +#define HNS3_RESET_WAIT_CNT 200 + +int hns3_logtype_init; +int hns3_logtype_driver; + +enum hns3_evt_cause { + HNS3_VECTOR0_EVENT_RST, + HNS3_VECTOR0_EVENT_MBX, + HNS3_VECTOR0_EVENT_ERR, + HNS3_VECTOR0_EVENT_OTHER, +}; + +static enum hns3_reset_level hns3_get_reset_level(struct hns3_adapter *hns, + uint64_t *levels); +static int hns3_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu); +static int hns3_vlan_pvid_configure(struct hns3_adapter *hns, uint16_t pvid, + int on); +static int hns3_update_speed_duplex(struct rte_eth_dev *eth_dev); + +static int hns3_add_mc_addr(struct hns3_hw *hw, + struct rte_ether_addr *mac_addr); +static int hns3_remove_mc_addr(struct hns3_hw *hw, + struct rte_ether_addr *mac_addr); + +static void +hns3_pf_disable_irq0(struct hns3_hw *hw) +{ + hns3_write_dev(hw, HNS3_MISC_VECTOR_REG_BASE, 0); +} + +static void +hns3_pf_enable_irq0(struct hns3_hw *hw) +{ + hns3_write_dev(hw, HNS3_MISC_VECTOR_REG_BASE, 1); +} + +static enum hns3_evt_cause +hns3_check_event_cause(struct hns3_adapter *hns, uint32_t *clearval) +{ + struct hns3_hw *hw = &hns->hw; + uint32_t vector0_int_stats; + uint32_t cmdq_src_val; + uint32_t val; + enum hns3_evt_cause ret; + + /* fetch the events from their corresponding regs */ + vector0_int_stats = hns3_read_dev(hw, HNS3_VECTOR0_OTHER_INT_STS_REG); + cmdq_src_val = hns3_read_dev(hw, HNS3_VECTOR0_CMDQ_SRC_REG); + + /* + * Assumption: If by any chance reset and mailbox events are reported + * together then we will only process reset event and defer the + * processing of the mailbox events. Since, we would have not cleared + * RX CMDQ event this time we would receive again another interrupt + * from H/W just for the mailbox. + */ + if (BIT(HNS3_VECTOR0_IMPRESET_INT_B) & vector0_int_stats) { /* IMP */ + rte_atomic16_set(&hw->reset.disable_cmd, 1); + hns3_atomic_set_bit(HNS3_IMP_RESET, &hw->reset.pending); + val = BIT(HNS3_VECTOR0_IMPRESET_INT_B); + if (clearval) { + hw->reset.stats.imp_cnt++; + hns3_warn(hw, "IMP reset detected, clear reset status"); + } else { + hns3_schedule_delayed_reset(hns); + hns3_warn(hw, "IMP reset detected, don't clear reset status"); + } + + ret = HNS3_VECTOR0_EVENT_RST; + goto out; + } + + /* Global reset */ + if (BIT(HNS3_VECTOR0_GLOBALRESET_INT_B) & vector0_int_stats) { + rte_atomic16_set(&hw->reset.disable_cmd, 1); + hns3_atomic_set_bit(HNS3_GLOBAL_RESET, &hw->reset.pending); + val = BIT(HNS3_VECTOR0_GLOBALRESET_INT_B); + if (clearval) { + hw->reset.stats.global_cnt++; + hns3_warn(hw, "Global reset detected, clear reset status"); + } else { + hns3_schedule_delayed_reset(hns); + hns3_warn(hw, "Global reset detected, don't clear reset status"); + } + + ret = HNS3_VECTOR0_EVENT_RST; + goto out; + } + + /* check for vector0 msix event source */ + if (vector0_int_stats & HNS3_VECTOR0_REG_MSIX_MASK) { + val = vector0_int_stats; + ret = HNS3_VECTOR0_EVENT_ERR; + goto out; + } + + /* check for vector0 mailbox(=CMDQ RX) event source */ + if (BIT(HNS3_VECTOR0_RX_CMDQ_INT_B) & cmdq_src_val) { + cmdq_src_val &= ~BIT(HNS3_VECTOR0_RX_CMDQ_INT_B); + val = cmdq_src_val; + ret = HNS3_VECTOR0_EVENT_MBX; + goto out; + } + + if (clearval && (vector0_int_stats || cmdq_src_val)) + hns3_warn(hw, "surprise irq ector0_int_stats:0x%x cmdq_src_val:0x%x", + vector0_int_stats, cmdq_src_val); + val = vector0_int_stats; + ret = HNS3_VECTOR0_EVENT_OTHER; +out: + + if (clearval) + *clearval = val; + return ret; +} + +static void +hns3_clear_event_cause(struct hns3_hw *hw, uint32_t event_type, uint32_t regclr) +{ + if (event_type == HNS3_VECTOR0_EVENT_RST) + hns3_write_dev(hw, HNS3_MISC_RESET_STS_REG, regclr); + else if (event_type == HNS3_VECTOR0_EVENT_MBX) + hns3_write_dev(hw, HNS3_VECTOR0_CMDQ_SRC_REG, regclr); +} + +static void +hns3_clear_all_event_cause(struct hns3_hw *hw) +{ + uint32_t vector0_int_stats; + vector0_int_stats = hns3_read_dev(hw, HNS3_VECTOR0_OTHER_INT_STS_REG); + + if (BIT(HNS3_VECTOR0_IMPRESET_INT_B) & vector0_int_stats) + hns3_warn(hw, "Probe during IMP reset interrupt"); + + if (BIT(HNS3_VECTOR0_GLOBALRESET_INT_B) & vector0_int_stats) + hns3_warn(hw, "Probe during Global reset interrupt"); + + hns3_clear_event_cause(hw, HNS3_VECTOR0_EVENT_RST, + BIT(HNS3_VECTOR0_IMPRESET_INT_B) | + BIT(HNS3_VECTOR0_GLOBALRESET_INT_B) | + BIT(HNS3_VECTOR0_CORERESET_INT_B)); + hns3_clear_event_cause(hw, HNS3_VECTOR0_EVENT_MBX, 0); +} + +static void +hns3_interrupt_handler(void *param) +{ + struct rte_eth_dev *dev = (struct rte_eth_dev *)param; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + enum hns3_evt_cause event_cause; + uint32_t clearval = 0; + + /* Disable interrupt */ + hns3_pf_disable_irq0(hw); + + event_cause = hns3_check_event_cause(hns, &clearval); + + /* vector 0 interrupt is shared with reset and mailbox source events. */ + if (event_cause == HNS3_VECTOR0_EVENT_ERR) { + hns3_handle_msix_error(hns, &hw->reset.request); + hns3_schedule_reset(hns); + } else if (event_cause == HNS3_VECTOR0_EVENT_RST) + hns3_schedule_reset(hns); + else if (event_cause == HNS3_VECTOR0_EVENT_MBX) + hns3_dev_handle_mbx_msg(hw); + else + hns3_err(hw, "Received unknown event"); + + hns3_clear_event_cause(hw, event_cause, clearval); + /* Enable interrupt if it is not cause by reset */ + hns3_pf_enable_irq0(hw); +} + +static int +hns3_set_port_vlan_filter(struct hns3_adapter *hns, uint16_t vlan_id, int on) +{ +#define HNS3_VLAN_ID_OFFSET_STEP 160 +#define HNS3_VLAN_BYTE_SIZE 8 + struct hns3_vlan_filter_pf_cfg_cmd *req; + struct hns3_hw *hw = &hns->hw; + uint8_t vlan_offset_byte_val; + struct hns3_cmd_desc desc; + uint8_t vlan_offset_byte; + uint8_t vlan_offset_base; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_VLAN_FILTER_PF_CFG, false); + + vlan_offset_base = vlan_id / HNS3_VLAN_ID_OFFSET_STEP; + vlan_offset_byte = (vlan_id % HNS3_VLAN_ID_OFFSET_STEP) / + HNS3_VLAN_BYTE_SIZE; + vlan_offset_byte_val = 1 << (vlan_id % HNS3_VLAN_BYTE_SIZE); + + req = (struct hns3_vlan_filter_pf_cfg_cmd *)desc.data; + req->vlan_offset = vlan_offset_base; + req->vlan_cfg = on ? 0 : 1; + req->vlan_offset_bitmap[vlan_offset_byte] = vlan_offset_byte_val; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "set port vlan id failed, vlan_id =%u, ret =%d", + vlan_id, ret); + + return ret; +} + +static void +hns3_rm_dev_vlan_table(struct hns3_adapter *hns, uint16_t vlan_id) +{ + struct hns3_user_vlan_table *vlan_entry; + struct hns3_pf *pf = &hns->pf; + + LIST_FOREACH(vlan_entry, &pf->vlan_list, next) { + if (vlan_entry->vlan_id == vlan_id) { + if (vlan_entry->hd_tbl_status) + hns3_set_port_vlan_filter(hns, vlan_id, 0); + LIST_REMOVE(vlan_entry, next); + rte_free(vlan_entry); + break; + } + } +} + +static void +hns3_add_dev_vlan_table(struct hns3_adapter *hns, uint16_t vlan_id, + bool writen_to_tbl) +{ + struct hns3_user_vlan_table *vlan_entry; + struct hns3_hw *hw = &hns->hw; + struct hns3_pf *pf = &hns->pf; + + LIST_FOREACH(vlan_entry, &pf->vlan_list, next) { + if (vlan_entry->vlan_id == vlan_id) + return; + } + + vlan_entry = rte_zmalloc("hns3_vlan_tbl", sizeof(*vlan_entry), 0); + if (vlan_entry == NULL) { + hns3_err(hw, "Failed to malloc hns3 vlan table"); + return; + } + + vlan_entry->hd_tbl_status = writen_to_tbl; + vlan_entry->vlan_id = vlan_id; + + LIST_INSERT_HEAD(&pf->vlan_list, vlan_entry, next); +} + +static int +hns3_restore_vlan_table(struct hns3_adapter *hns) +{ + struct hns3_user_vlan_table *vlan_entry; + struct hns3_pf *pf = &hns->pf; + uint16_t vlan_id; + int ret = 0; + + if (pf->port_base_vlan_cfg.state == HNS3_PORT_BASE_VLAN_ENABLE) + return hns3_vlan_pvid_configure(hns, + pf->port_base_vlan_cfg.pvid, 1); + + LIST_FOREACH(vlan_entry, &pf->vlan_list, next) { + if (vlan_entry->hd_tbl_status) { + vlan_id = vlan_entry->vlan_id; + ret = hns3_set_port_vlan_filter(hns, vlan_id, 1); + if (ret) + break; + } + } + + return ret; +} + +static int +hns3_vlan_filter_configure(struct hns3_adapter *hns, uint16_t vlan_id, int on) +{ + struct hns3_pf *pf = &hns->pf; + bool writen_to_tbl = false; + int ret = 0; + + /* + * When vlan filter is enabled, hardware regards vlan id 0 as the entry + * for normal packet, deleting vlan id 0 is not allowed. + */ + if (on == 0 && vlan_id == 0) + return 0; + + /* + * When port base vlan enabled, we use port base vlan as the vlan + * filter condition. In this case, we don't update vlan filter table + * when user add new vlan or remove exist vlan, just update the + * vlan list. The vlan id in vlan list will be writen in vlan filter + * table until port base vlan disabled + */ + if (pf->port_base_vlan_cfg.state == HNS3_PORT_BASE_VLAN_DISABLE) { + ret = hns3_set_port_vlan_filter(hns, vlan_id, on); + writen_to_tbl = true; + } + + if (ret == 0 && vlan_id) { + if (on) + hns3_add_dev_vlan_table(hns, vlan_id, writen_to_tbl); + else + hns3_rm_dev_vlan_table(hns, vlan_id); + } + return ret; +} + +static int +hns3_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + rte_spinlock_lock(&hw->lock); + ret = hns3_vlan_filter_configure(hns, vlan_id, on); + rte_spinlock_unlock(&hw->lock); + return ret; +} + +static int +hns3_vlan_tpid_configure(struct hns3_adapter *hns, enum rte_vlan_type vlan_type, + uint16_t tpid) +{ + struct hns3_rx_vlan_type_cfg_cmd *rx_req; + struct hns3_tx_vlan_type_cfg_cmd *tx_req; + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc; + int ret; + + if ((vlan_type != ETH_VLAN_TYPE_INNER && + vlan_type != ETH_VLAN_TYPE_OUTER)) { + hns3_err(hw, "Unsupported vlan type, vlan_type =%d", vlan_type); + return -EINVAL; + } + + if (tpid != RTE_ETHER_TYPE_VLAN) { + hns3_err(hw, "Unsupported vlan tpid, vlan_type =%d", vlan_type); + return -EINVAL; + } + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_TYPE_ID, false); + rx_req = (struct hns3_rx_vlan_type_cfg_cmd *)desc.data; + + if (vlan_type == ETH_VLAN_TYPE_OUTER) { + rx_req->ot_fst_vlan_type = rte_cpu_to_le_16(tpid); + rx_req->ot_sec_vlan_type = rte_cpu_to_le_16(tpid); + } else if (vlan_type == ETH_VLAN_TYPE_INNER) { + rx_req->ot_fst_vlan_type = rte_cpu_to_le_16(tpid); + rx_req->ot_sec_vlan_type = rte_cpu_to_le_16(tpid); + rx_req->in_fst_vlan_type = rte_cpu_to_le_16(tpid); + rx_req->in_sec_vlan_type = rte_cpu_to_le_16(tpid); + } + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Send rxvlan protocol type command fail, ret =%d", + ret); + return ret; + } + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_INSERT, false); + + tx_req = (struct hns3_tx_vlan_type_cfg_cmd *)desc.data; + tx_req->ot_vlan_type = rte_cpu_to_le_16(tpid); + tx_req->in_vlan_type = rte_cpu_to_le_16(tpid); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Send txvlan protocol type command fail, ret =%d", + ret); + return ret; +} + +static int +hns3_vlan_tpid_set(struct rte_eth_dev *dev, enum rte_vlan_type vlan_type, + uint16_t tpid) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + rte_spinlock_lock(&hw->lock); + ret = hns3_vlan_tpid_configure(hns, vlan_type, tpid); + rte_spinlock_unlock(&hw->lock); + return ret; +} + +static int +hns3_set_vlan_rx_offload_cfg(struct hns3_adapter *hns, + struct hns3_rx_vtag_cfg *vcfg) +{ + struct hns3_vport_vtag_rx_cfg_cmd *req; + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc; + uint16_t vport_id; + uint8_t bitmap; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_VLAN_PORT_RX_CFG, false); + + req = (struct hns3_vport_vtag_rx_cfg_cmd *)desc.data; + hns3_set_bit(req->vport_vlan_cfg, HNS3_REM_TAG1_EN_B, + vcfg->strip_tag1_en ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_REM_TAG2_EN_B, + vcfg->strip_tag2_en ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_SHOW_TAG1_EN_B, + vcfg->vlan1_vlan_prionly ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_SHOW_TAG2_EN_B, + vcfg->vlan2_vlan_prionly ? 1 : 0); + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, the PF-related vf_id is 0, just need to configure parameters + * for vport_id 0. + */ + vport_id = 0; + req->vf_offset = vport_id / HNS3_VF_NUM_PER_CMD; + bitmap = 1 << (vport_id % HNS3_VF_NUM_PER_BYTE); + req->vf_bitmap[req->vf_offset] = bitmap; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Send port rxvlan cfg command fail, ret =%d", ret); + return ret; +} + +static void +hns3_update_rx_offload_cfg(struct hns3_adapter *hns, + struct hns3_rx_vtag_cfg *vcfg) +{ + struct hns3_pf *pf = &hns->pf; + memcpy(&pf->vtag_config.rx_vcfg, vcfg, sizeof(pf->vtag_config.rx_vcfg)); +} + +static void +hns3_update_tx_offload_cfg(struct hns3_adapter *hns, + struct hns3_tx_vtag_cfg *vcfg) +{ + struct hns3_pf *pf = &hns->pf; + memcpy(&pf->vtag_config.tx_vcfg, vcfg, sizeof(pf->vtag_config.tx_vcfg)); +} + +static int +hns3_en_hw_strip_rxvtag(struct hns3_adapter *hns, bool enable) +{ + struct hns3_rx_vtag_cfg rxvlan_cfg; + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (pf->port_base_vlan_cfg.state == HNS3_PORT_BASE_VLAN_DISABLE) { + rxvlan_cfg.strip_tag1_en = false; + rxvlan_cfg.strip_tag2_en = enable; + } else { + rxvlan_cfg.strip_tag1_en = enable; + rxvlan_cfg.strip_tag2_en = true; + } + + rxvlan_cfg.vlan1_vlan_prionly = false; + rxvlan_cfg.vlan2_vlan_prionly = false; + rxvlan_cfg.rx_vlan_offload_en = enable; + + ret = hns3_set_vlan_rx_offload_cfg(hns, &rxvlan_cfg); + if (ret) { + hns3_err(hw, "enable strip rx vtag failed, ret =%d", ret); + return ret; + } + + hns3_update_rx_offload_cfg(hns, &rxvlan_cfg); + + return ret; +} + +static int +hns3_set_vlan_filter_ctrl(struct hns3_hw *hw, uint8_t vlan_type, + uint8_t fe_type, bool filter_en, uint8_t vf_id) +{ + struct hns3_vlan_filter_ctrl_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_VLAN_FILTER_CTRL, false); + + req = (struct hns3_vlan_filter_ctrl_cmd *)desc.data; + req->vlan_type = vlan_type; + req->vlan_fe = filter_en ? fe_type : 0; + req->vf_id = vf_id; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "set vlan filter fail, ret =%d", ret); + + return ret; +} + +static int +hns3_vlan_filter_init(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_set_vlan_filter_ctrl(hw, HNS3_FILTER_TYPE_VF, + HNS3_FILTER_FE_EGRESS, false, 0); + if (ret) { + hns3_err(hw, "failed to init vf vlan filter, ret = %d", ret); + return ret; + } + + ret = hns3_set_vlan_filter_ctrl(hw, HNS3_FILTER_TYPE_PORT, + HNS3_FILTER_FE_INGRESS, false, 0); + if (ret) + hns3_err(hw, "failed to init port vlan filter, ret = %d", ret); + + return ret; +} + +static int +hns3_enable_vlan_filter(struct hns3_adapter *hns, bool enable) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_set_vlan_filter_ctrl(hw, HNS3_FILTER_TYPE_PORT, + HNS3_FILTER_FE_INGRESS, enable, 0); + if (ret) + hns3_err(hw, "failed to %s port vlan filter, ret = %d", + enable ? "enable" : "disable", ret); + + return ret; +} + +static int +hns3_vlan_offload_set(struct rte_eth_dev *dev, int mask) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct rte_eth_rxmode *rxmode; + unsigned int tmp_mask; + bool enable; + int ret = 0; + + rte_spinlock_lock(&hw->lock); + rxmode = &dev->data->dev_conf.rxmode; + tmp_mask = (unsigned int)mask; + if (tmp_mask & ETH_VLAN_FILTER_MASK) { + /* ignore vlan filter configuration during promiscuous mode */ + if (!dev->data->promiscuous) { + /* Enable or disable VLAN filter */ + enable = rxmode->offloads & DEV_RX_OFFLOAD_VLAN_FILTER ? + true : false; + + ret = hns3_enable_vlan_filter(hns, enable); + if (ret) { + rte_spinlock_unlock(&hw->lock); + hns3_err(hw, "failed to %s rx filter, ret = %d", + enable ? "enable" : "disable", ret); + return ret; + } + } + } + + if (tmp_mask & ETH_VLAN_STRIP_MASK) { + /* Enable or disable VLAN stripping */ + enable = rxmode->offloads & DEV_RX_OFFLOAD_VLAN_STRIP ? + true : false; + + ret = hns3_en_hw_strip_rxvtag(hns, enable); + if (ret) { + rte_spinlock_unlock(&hw->lock); + hns3_err(hw, "failed to %s rx strip, ret = %d", + enable ? "enable" : "disable", ret); + return ret; + } + } + + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_set_vlan_tx_offload_cfg(struct hns3_adapter *hns, + struct hns3_tx_vtag_cfg *vcfg) +{ + struct hns3_vport_vtag_tx_cfg_cmd *req; + struct hns3_cmd_desc desc; + struct hns3_hw *hw = &hns->hw; + uint16_t vport_id; + uint8_t bitmap; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_VLAN_PORT_TX_CFG, false); + + req = (struct hns3_vport_vtag_tx_cfg_cmd *)desc.data; + req->def_vlan_tag1 = vcfg->default_tag1; + req->def_vlan_tag2 = vcfg->default_tag2; + hns3_set_bit(req->vport_vlan_cfg, HNS3_ACCEPT_TAG1_B, + vcfg->accept_tag1 ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_ACCEPT_UNTAG1_B, + vcfg->accept_untag1 ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_ACCEPT_TAG2_B, + vcfg->accept_tag2 ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_ACCEPT_UNTAG2_B, + vcfg->accept_untag2 ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_PORT_INS_TAG1_EN_B, + vcfg->insert_tag1_en ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_PORT_INS_TAG2_EN_B, + vcfg->insert_tag2_en ? 1 : 0); + hns3_set_bit(req->vport_vlan_cfg, HNS3_CFG_NIC_ROCE_SEL_B, 0); + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, the PF-related vf_id is 0, just need to configure parameters + * for vport_id 0. + */ + vport_id = 0; + req->vf_offset = vport_id / HNS3_VF_NUM_PER_CMD; + bitmap = 1 << (vport_id % HNS3_VF_NUM_PER_BYTE); + req->vf_bitmap[req->vf_offset] = bitmap; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Send port txvlan cfg command fail, ret =%d", ret); + + return ret; +} + +static int +hns3_vlan_txvlan_cfg(struct hns3_adapter *hns, uint16_t port_base_vlan_state, + uint16_t pvid) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_tx_vtag_cfg txvlan_cfg; + int ret; + + if (port_base_vlan_state == HNS3_PORT_BASE_VLAN_DISABLE) { + txvlan_cfg.accept_tag1 = true; + txvlan_cfg.insert_tag1_en = false; + txvlan_cfg.default_tag1 = 0; + } else { + txvlan_cfg.accept_tag1 = false; + txvlan_cfg.insert_tag1_en = true; + txvlan_cfg.default_tag1 = pvid; + } + + txvlan_cfg.accept_untag1 = true; + txvlan_cfg.accept_tag2 = true; + txvlan_cfg.accept_untag2 = true; + txvlan_cfg.insert_tag2_en = false; + txvlan_cfg.default_tag2 = 0; + + ret = hns3_set_vlan_tx_offload_cfg(hns, &txvlan_cfg); + if (ret) { + hns3_err(hw, "pf vlan set pvid failed, pvid =%u ,ret =%d", pvid, + ret); + return ret; + } + + hns3_update_tx_offload_cfg(hns, &txvlan_cfg); + return ret; +} + +static void +hns3_store_port_base_vlan_info(struct hns3_adapter *hns, uint16_t pvid, int on) +{ + struct hns3_pf *pf = &hns->pf; + + pf->port_base_vlan_cfg.state = on ? + HNS3_PORT_BASE_VLAN_ENABLE : HNS3_PORT_BASE_VLAN_DISABLE; + + pf->port_base_vlan_cfg.pvid = pvid; +} + +static void +hns3_rm_all_vlan_table(struct hns3_adapter *hns, bool is_del_list) +{ + struct hns3_user_vlan_table *vlan_entry; + struct hns3_pf *pf = &hns->pf; + + LIST_FOREACH(vlan_entry, &pf->vlan_list, next) { + if (vlan_entry->hd_tbl_status) + hns3_set_port_vlan_filter(hns, vlan_entry->vlan_id, 0); + + vlan_entry->hd_tbl_status = false; + } + + if (is_del_list) { + vlan_entry = LIST_FIRST(&pf->vlan_list); + while (vlan_entry) { + LIST_REMOVE(vlan_entry, next); + rte_free(vlan_entry); + vlan_entry = LIST_FIRST(&pf->vlan_list); + } + } +} + +static void +hns3_add_all_vlan_table(struct hns3_adapter *hns) +{ + struct hns3_user_vlan_table *vlan_entry; + struct hns3_pf *pf = &hns->pf; + + LIST_FOREACH(vlan_entry, &pf->vlan_list, next) { + if (!vlan_entry->hd_tbl_status) + hns3_set_port_vlan_filter(hns, vlan_entry->vlan_id, 1); + + vlan_entry->hd_tbl_status = true; + } +} + +static void +hns3_remove_all_vlan_table(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_pf *pf = &hns->pf; + int ret; + + hns3_rm_all_vlan_table(hns, true); + if (pf->port_base_vlan_cfg.pvid != HNS3_INVLID_PVID) { + ret = hns3_set_port_vlan_filter(hns, + pf->port_base_vlan_cfg.pvid, 0); + if (ret) { + hns3_err(hw, "Failed to remove all vlan table, ret =%d", + ret); + return; + } + } +} + +static int +hns3_update_vlan_filter_entries(struct hns3_adapter *hns, + uint16_t port_base_vlan_state, + uint16_t new_pvid, uint16_t old_pvid) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + int ret = 0; + + if (port_base_vlan_state == HNS3_PORT_BASE_VLAN_ENABLE) { + if (old_pvid != HNS3_INVLID_PVID && old_pvid != 0) { + ret = hns3_set_port_vlan_filter(hns, old_pvid, 0); + if (ret) { + hns3_err(hw, + "Failed to clear clear old pvid filter, ret =%d", + ret); + return ret; + } + } + + hns3_rm_all_vlan_table(hns, false); + return hns3_set_port_vlan_filter(hns, new_pvid, 1); + } + + if (new_pvid != 0) { + ret = hns3_set_port_vlan_filter(hns, new_pvid, 0); + if (ret) { + hns3_err(hw, "Failed to set port vlan filter, ret =%d", + ret); + return ret; + } + } + + if (new_pvid == pf->port_base_vlan_cfg.pvid) + hns3_add_all_vlan_table(hns); + + return ret; +} + +static int +hns3_en_rx_strip_all(struct hns3_adapter *hns, int on) +{ + struct hns3_rx_vtag_cfg rx_vlan_cfg; + struct hns3_hw *hw = &hns->hw; + bool rx_strip_en; + int ret; + + rx_strip_en = on ? true : false; + rx_vlan_cfg.strip_tag1_en = rx_strip_en; + rx_vlan_cfg.strip_tag2_en = rx_strip_en; + rx_vlan_cfg.vlan1_vlan_prionly = false; + rx_vlan_cfg.vlan2_vlan_prionly = false; + rx_vlan_cfg.rx_vlan_offload_en = rx_strip_en; + + ret = hns3_set_vlan_rx_offload_cfg(hns, &rx_vlan_cfg); + if (ret) { + hns3_err(hw, "enable strip rx failed, ret =%d", ret); + return ret; + } + + hns3_update_rx_offload_cfg(hns, &rx_vlan_cfg); + return ret; +} + +static int +hns3_vlan_pvid_configure(struct hns3_adapter *hns, uint16_t pvid, int on) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + uint16_t port_base_vlan_state; + uint16_t old_pvid; + int ret; + + if (on == 0 && pvid != pf->port_base_vlan_cfg.pvid) { + if (pf->port_base_vlan_cfg.pvid != HNS3_INVLID_PVID) + hns3_warn(hw, "Invalid operation! As current pvid set " + "is %u, disable pvid %u is invalid", + pf->port_base_vlan_cfg.pvid, pvid); + return 0; + } + + port_base_vlan_state = on ? HNS3_PORT_BASE_VLAN_ENABLE : + HNS3_PORT_BASE_VLAN_DISABLE; + ret = hns3_vlan_txvlan_cfg(hns, port_base_vlan_state, pvid); + if (ret) { + hns3_err(hw, "Failed to config tx vlan, ret =%d", ret); + return ret; + } + + ret = hns3_en_rx_strip_all(hns, on); + if (ret) { + hns3_err(hw, "Failed to config rx vlan strip, ret =%d", ret); + return ret; + } + + if (pvid == HNS3_INVLID_PVID) + goto out; + old_pvid = pf->port_base_vlan_cfg.pvid; + ret = hns3_update_vlan_filter_entries(hns, port_base_vlan_state, pvid, + old_pvid); + if (ret) { + hns3_err(hw, "Failed to update vlan filter entries, ret =%d", + ret); + return ret; + } + +out: + hns3_store_port_base_vlan_info(hns, pvid, on); + return ret; +} + +static int +hns3_vlan_pvid_set(struct rte_eth_dev *dev, uint16_t pvid, int on) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (pvid > RTE_ETHER_MAX_VLAN_ID) { + hns3_err(hw, "Invalid vlan_id = %u > %d", pvid, + RTE_ETHER_MAX_VLAN_ID); + return -EINVAL; + } + + rte_spinlock_lock(&hw->lock); + ret = hns3_vlan_pvid_configure(hns, pvid, on); + rte_spinlock_unlock(&hw->lock); + return ret; +} + +static void +init_port_base_vlan_info(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + + pf->port_base_vlan_cfg.state = HNS3_PORT_BASE_VLAN_DISABLE; + pf->port_base_vlan_cfg.pvid = HNS3_INVLID_PVID; +} + +static int +hns3_default_vlan_config(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_set_port_vlan_filter(hns, 0, 1); + if (ret) + hns3_err(hw, "default vlan 0 config failed, ret =%d", ret); + return ret; +} + +static int +hns3_init_vlan_config(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + /* + * This function can be called in the initialization and reset process, + * when in reset process, it means that hardware had been reseted + * successfully and we need to restore the hardware configuration to + * ensure that the hardware configuration remains unchanged before and + * after reset. + */ + if (rte_atomic16_read(&hw->reset.resetting) == 0) + init_port_base_vlan_info(hw); + + ret = hns3_vlan_filter_init(hns); + if (ret) { + hns3_err(hw, "vlan init fail in pf, ret =%d", ret); + return ret; + } + + ret = hns3_vlan_tpid_configure(hns, ETH_VLAN_TYPE_INNER, + RTE_ETHER_TYPE_VLAN); + if (ret) { + hns3_err(hw, "tpid set fail in pf, ret =%d", ret); + return ret; + } + + /* + * When in the reinit dev stage of the reset process, the following + * vlan-related configurations may differ from those at initialization, + * we will restore configurations to hardware in hns3_restore_vlan_table + * and hns3_restore_vlan_conf later. + */ + if (rte_atomic16_read(&hw->reset.resetting) == 0) { + ret = hns3_vlan_pvid_configure(hns, HNS3_INVLID_PVID, 0); + if (ret) { + hns3_err(hw, "pvid set fail in pf, ret =%d", ret); + return ret; + } + + ret = hns3_en_hw_strip_rxvtag(hns, false); + if (ret) { + hns3_err(hw, "rx strip configure fail in pf, ret =%d", + ret); + return ret; + } + } + + return hns3_default_vlan_config(hns); +} + +static int +hns3_restore_vlan_conf(struct hns3_adapter *hns) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + uint64_t offloads; + bool enable; + int ret; + + if (!hw->data->promiscuous) { + /* restore vlan filter states */ + offloads = hw->data->dev_conf.rxmode.offloads; + enable = offloads & DEV_RX_OFFLOAD_VLAN_FILTER ? true : false; + ret = hns3_enable_vlan_filter(hns, enable); + if (ret) { + hns3_err(hw, "failed to restore vlan rx filter conf, " + "ret = %d", ret); + return ret; + } + } + + ret = hns3_set_vlan_rx_offload_cfg(hns, &pf->vtag_config.rx_vcfg); + if (ret) { + hns3_err(hw, "failed to restore vlan rx conf, ret = %d", ret); + return ret; + } + + ret = hns3_set_vlan_tx_offload_cfg(hns, &pf->vtag_config.tx_vcfg); + if (ret) + hns3_err(hw, "failed to restore vlan tx conf, ret = %d", ret); + + return ret; +} + +static int +hns3_dev_configure_vlan(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct rte_eth_dev_data *data = dev->data; + struct rte_eth_txmode *txmode; + struct hns3_hw *hw = &hns->hw; + int mask; + int ret; + + txmode = &data->dev_conf.txmode; + if (txmode->hw_vlan_reject_tagged || txmode->hw_vlan_reject_untagged) + hns3_warn(hw, + "hw_vlan_reject_tagged or hw_vlan_reject_untagged " + "configuration is not supported! Ignore these two " + "parameters: hw_vlan_reject_tagged(%d), " + "hw_vlan_reject_untagged(%d)", + txmode->hw_vlan_reject_tagged, + txmode->hw_vlan_reject_untagged); + + /* Apply vlan offload setting */ + mask = ETH_VLAN_STRIP_MASK | ETH_VLAN_FILTER_MASK; + ret = hns3_vlan_offload_set(dev, mask); + if (ret) { + hns3_err(hw, "dev config rx vlan offload failed, ret = %d", + ret); + return ret; + } + + /* + * If pvid config is not set in rte_eth_conf, driver needn't to set + * VLAN pvid related configuration to hardware. + */ + if (txmode->pvid == 0 && txmode->hw_vlan_insert_pvid == 0) + return 0; + + /* Apply pvid setting */ + ret = hns3_vlan_pvid_set(dev, txmode->pvid, + txmode->hw_vlan_insert_pvid); + if (ret) + hns3_err(hw, "dev config vlan pvid(%d) failed, ret = %d", + txmode->pvid, ret); + + return ret; +} + +static int +hns3_config_tso(struct hns3_hw *hw, unsigned int tso_mss_min, + unsigned int tso_mss_max) +{ + struct hns3_cfg_tso_status_cmd *req; + struct hns3_cmd_desc desc; + uint16_t tso_mss; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TSO_GENERIC_CONFIG, false); + + req = (struct hns3_cfg_tso_status_cmd *)desc.data; + + tso_mss = 0; + hns3_set_field(tso_mss, HNS3_TSO_MSS_MIN_M, HNS3_TSO_MSS_MIN_S, + tso_mss_min); + req->tso_mss_min = rte_cpu_to_le_16(tso_mss); + + tso_mss = 0; + hns3_set_field(tso_mss, HNS3_TSO_MSS_MIN_M, HNS3_TSO_MSS_MIN_S, + tso_mss_max); + req->tso_mss_max = rte_cpu_to_le_16(tso_mss); + + return hns3_cmd_send(hw, &desc, 1); +} + +int +hns3_config_gro(struct hns3_hw *hw, bool en) +{ + struct hns3_cfg_gro_status_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_GRO_GENERIC_CONFIG, false); + req = (struct hns3_cfg_gro_status_cmd *)desc.data; + + req->gro_en = rte_cpu_to_le_16(en ? 1 : 0); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "GRO hardware config cmd failed, ret = %d", ret); + + return ret; +} + +static int +hns3_set_umv_space(struct hns3_hw *hw, uint16_t space_size, + uint16_t *allocated_size, bool is_alloc) +{ + struct hns3_umv_spc_alc_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + req = (struct hns3_umv_spc_alc_cmd *)desc.data; + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_ALLOCATE, false); + hns3_set_bit(req->allocate, HNS3_UMV_SPC_ALC_B, is_alloc ? 0 : 1); + req->space_size = rte_cpu_to_le_32(space_size); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + PMD_INIT_LOG(ERR, "%s umv space failed for cmd_send, ret =%d", + is_alloc ? "allocate" : "free", ret); + return ret; + } + + if (is_alloc && allocated_size) + *allocated_size = rte_le_to_cpu_32(desc.data[1]); + + return 0; +} + +static int +hns3_init_umv_space(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint16_t allocated_size = 0; + int ret; + + ret = hns3_set_umv_space(hw, pf->wanted_umv_size, &allocated_size, + true); + if (ret) + return ret; + + if (allocated_size < pf->wanted_umv_size) + PMD_INIT_LOG(WARNING, "Alloc umv space failed, want %u, get %u", + pf->wanted_umv_size, allocated_size); + + pf->max_umv_size = (!!allocated_size) ? allocated_size : + pf->wanted_umv_size; + pf->used_umv_size = 0; + return 0; +} + +static int +hns3_uninit_umv_space(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + int ret; + + if (pf->max_umv_size == 0) + return 0; + + ret = hns3_set_umv_space(hw, pf->max_umv_size, NULL, false); + if (ret) + return ret; + + pf->max_umv_size = 0; + + return 0; +} + +static bool +hns3_is_umv_space_full(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + bool is_full; + + is_full = (pf->used_umv_size >= pf->max_umv_size); + + return is_full; +} + +static void +hns3_update_umv_space(struct hns3_hw *hw, bool is_free) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + + if (is_free) { + if (pf->used_umv_size > 0) + pf->used_umv_size--; + } else + pf->used_umv_size++; +} + +static void +hns3_prepare_mac_addr(struct hns3_mac_vlan_tbl_entry_cmd *new_req, + const uint8_t *addr, bool is_mc) +{ + const unsigned char *mac_addr = addr; + uint32_t high_val = ((uint32_t)mac_addr[3] << 24) | + ((uint32_t)mac_addr[2] << 16) | + ((uint32_t)mac_addr[1] << 8) | + (uint32_t)mac_addr[0]; + uint32_t low_val = ((uint32_t)mac_addr[5] << 8) | (uint32_t)mac_addr[4]; + + hns3_set_bit(new_req->flags, HNS3_MAC_VLAN_BIT0_EN_B, 1); + if (is_mc) { + hns3_set_bit(new_req->entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0); + hns3_set_bit(new_req->entry_type, HNS3_MAC_VLAN_BIT1_EN_B, 1); + hns3_set_bit(new_req->mc_mac_en, HNS3_MAC_VLAN_BIT0_EN_B, 1); + } + + new_req->mac_addr_hi32 = rte_cpu_to_le_32(high_val); + new_req->mac_addr_lo16 = rte_cpu_to_le_16(low_val & 0xffff); +} + +static int +hns3_get_mac_vlan_cmd_status(struct hns3_hw *hw, uint16_t cmdq_resp, + uint8_t resp_code, + enum hns3_mac_vlan_tbl_opcode op) +{ + if (cmdq_resp) { + hns3_err(hw, "cmdq execute failed for get_mac_vlan_cmd_status,status=%u", + cmdq_resp); + return -EIO; + } + + if (op == HNS3_MAC_VLAN_ADD) { + if (resp_code == 0 || resp_code == 1) { + return 0; + } else if (resp_code == HNS3_ADD_UC_OVERFLOW) { + hns3_err(hw, "add mac addr failed for uc_overflow"); + return -ENOSPC; + } else if (resp_code == HNS3_ADD_MC_OVERFLOW) { + hns3_err(hw, "add mac addr failed for mc_overflow"); + return -ENOSPC; + } + + hns3_err(hw, "add mac addr failed for undefined, code=%u", + resp_code); + return -EIO; + } else if (op == HNS3_MAC_VLAN_REMOVE) { + if (resp_code == 0) { + return 0; + } else if (resp_code == 1) { + hns3_dbg(hw, "remove mac addr failed for miss"); + return -ENOENT; + } + + hns3_err(hw, "remove mac addr failed for undefined, code=%u", + resp_code); + return -EIO; + } else if (op == HNS3_MAC_VLAN_LKUP) { + if (resp_code == 0) { + return 0; + } else if (resp_code == 1) { + hns3_dbg(hw, "lookup mac addr failed for miss"); + return -ENOENT; + } + + hns3_err(hw, "lookup mac addr failed for undefined, code=%u", + resp_code); + return -EIO; + } + + hns3_err(hw, "unknown opcode for get_mac_vlan_cmd_status, opcode=%u", + op); + + return -EINVAL; +} + +static int +hns3_lookup_mac_vlan_tbl(struct hns3_hw *hw, + struct hns3_mac_vlan_tbl_entry_cmd *req, + struct hns3_cmd_desc *desc, bool is_mc) +{ + uint8_t resp_code; + uint16_t retval; + int ret; + + hns3_cmd_setup_basic_desc(&desc[0], HNS3_OPC_MAC_VLAN_ADD, true); + if (is_mc) { + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + memcpy(desc[0].data, req, + sizeof(struct hns3_mac_vlan_tbl_entry_cmd)); + hns3_cmd_setup_basic_desc(&desc[1], HNS3_OPC_MAC_VLAN_ADD, + true); + desc[1].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_setup_basic_desc(&desc[2], HNS3_OPC_MAC_VLAN_ADD, + true); + ret = hns3_cmd_send(hw, desc, HNS3_MC_MAC_VLAN_ADD_DESC_NUM); + } else { + memcpy(desc[0].data, req, + sizeof(struct hns3_mac_vlan_tbl_entry_cmd)); + ret = hns3_cmd_send(hw, desc, 1); + } + if (ret) { + hns3_err(hw, "lookup mac addr failed for cmd_send, ret =%d.", + ret); + return ret; + } + resp_code = (rte_le_to_cpu_32(desc[0].data[0]) >> 8) & 0xff; + retval = rte_le_to_cpu_16(desc[0].retval); + + return hns3_get_mac_vlan_cmd_status(hw, retval, resp_code, + HNS3_MAC_VLAN_LKUP); +} + +static int +hns3_add_mac_vlan_tbl(struct hns3_hw *hw, + struct hns3_mac_vlan_tbl_entry_cmd *req, + struct hns3_cmd_desc *mc_desc) +{ + uint8_t resp_code; + uint16_t retval; + int cfg_status; + int ret; + + if (mc_desc == NULL) { + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_ADD, false); + memcpy(desc.data, req, + sizeof(struct hns3_mac_vlan_tbl_entry_cmd)); + ret = hns3_cmd_send(hw, &desc, 1); + resp_code = (rte_le_to_cpu_32(desc.data[0]) >> 8) & 0xff; + retval = rte_le_to_cpu_16(desc.retval); + + cfg_status = hns3_get_mac_vlan_cmd_status(hw, retval, resp_code, + HNS3_MAC_VLAN_ADD); + } else { + hns3_cmd_reuse_desc(&mc_desc[0], false); + mc_desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_reuse_desc(&mc_desc[1], false); + mc_desc[1].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_reuse_desc(&mc_desc[2], false); + mc_desc[2].flag &= rte_cpu_to_le_16(~HNS3_CMD_FLAG_NEXT); + memcpy(mc_desc[0].data, req, + sizeof(struct hns3_mac_vlan_tbl_entry_cmd)); + mc_desc[0].retval = 0; + ret = hns3_cmd_send(hw, mc_desc, HNS3_MC_MAC_VLAN_ADD_DESC_NUM); + resp_code = (rte_le_to_cpu_32(mc_desc[0].data[0]) >> 8) & 0xff; + retval = rte_le_to_cpu_16(mc_desc[0].retval); + + cfg_status = hns3_get_mac_vlan_cmd_status(hw, retval, resp_code, + HNS3_MAC_VLAN_ADD); + } + + if (ret) { + hns3_err(hw, "add mac addr failed for cmd_send, ret =%d", ret); + return ret; + } + + return cfg_status; +} + +static int +hns3_remove_mac_vlan_tbl(struct hns3_hw *hw, + struct hns3_mac_vlan_tbl_entry_cmd *req) +{ + struct hns3_cmd_desc desc; + uint8_t resp_code; + uint16_t retval; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_VLAN_REMOVE, false); + + memcpy(desc.data, req, sizeof(struct hns3_mac_vlan_tbl_entry_cmd)); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "del mac addr failed for cmd_send, ret =%d", ret); + return ret; + } + resp_code = (rte_le_to_cpu_32(desc.data[0]) >> 8) & 0xff; + retval = rte_le_to_cpu_16(desc.retval); + + return hns3_get_mac_vlan_cmd_status(hw, retval, resp_code, + HNS3_MAC_VLAN_REMOVE); +} + +static int +hns3_add_uc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_mac_vlan_tbl_entry_cmd req; + struct hns3_pf *pf = &hns->pf; + struct hns3_cmd_desc desc; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + uint16_t egress_port = 0; + uint8_t vf_id; + int ret; + + /* check if mac addr is valid */ + if (!rte_is_valid_assigned_ether_addr(mac_addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Add unicast mac addr err! addr(%s) invalid", + mac_str); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, the PF-related vf_id is 0, just need to configure parameters + * for vf_id 0. + */ + vf_id = 0; + hns3_set_field(egress_port, HNS3_MAC_EPORT_VFID_M, + HNS3_MAC_EPORT_VFID_S, vf_id); + + req.egress_port = rte_cpu_to_le_16(egress_port); + + hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, false); + + /* + * Lookup the mac address in the mac_vlan table, and add + * it if the entry is inexistent. Repeated unicast entry + * is not allowed in the mac vlan table. + */ + ret = hns3_lookup_mac_vlan_tbl(hw, &req, &desc, false); + if (ret == -ENOENT) { + if (!hns3_is_umv_space_full(hw)) { + ret = hns3_add_mac_vlan_tbl(hw, &req, NULL); + if (!ret) + hns3_update_umv_space(hw, false); + return ret; + } + + hns3_err(hw, "UC MAC table full(%u)", pf->used_umv_size); + + return -ENOSPC; + } + + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, mac_addr); + + /* check if we just hit the duplicate */ + if (ret == 0) { + hns3_dbg(hw, "mac addr(%s) has been in the MAC table", mac_str); + return 0; + } + + hns3_err(hw, "PF failed to add unicast entry(%s) in the MAC table", + mac_str); + + return ret; +} + +static int +hns3_add_mc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct rte_ether_addr *addr; + int ret; + int i; + + for (i = 0; i < hw->mc_addrs_num; i++) { + addr = &hw->mc_addrs[i]; + /* Check if there are duplicate addresses */ + if (rte_is_same_ether_addr(addr, mac_addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to add mc mac addr, same addrs" + "(%s) is added by the set_mc_mac_addr_list " + "API", mac_str); + return -EINVAL; + } + } + + ret = hns3_add_mc_addr(hw, mac_addr); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add mc mac addr(%s), ret = %d", + mac_str, ret); + } + return ret; +} + +static int +hns3_remove_mc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + ret = hns3_remove_mc_addr(hw, mac_addr); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to remove mc mac addr(%s), ret = %d", + mac_str, ret); + } + return ret; +} + +static int +hns3_add_mac_addr(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr, + uint32_t idx, __rte_unused uint32_t pool) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + rte_spinlock_lock(&hw->lock); + + /* + * In hns3 network engine adding UC and MC mac address with different + * commands with firmware. We need to determine whether the input + * address is a UC or a MC address to call different commands. + * By the way, it is recommended calling the API function named + * rte_eth_dev_set_mc_addr_list to set the MC mac address, because + * using the rte_eth_dev_mac_addr_add API function to set MC mac address + * may affect the specifications of UC mac addresses. + */ + if (rte_is_multicast_ether_addr(mac_addr)) + ret = hns3_add_mc_addr_common(hw, mac_addr); + else + ret = hns3_add_uc_addr_common(hw, mac_addr); + + if (ret) { + rte_spinlock_unlock(&hw->lock); + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add mac addr(%s), ret = %d", mac_str, + ret); + return ret; + } + + if (idx == 0) + hw->mac.default_addr_setted = true; + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_remove_uc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + struct hns3_mac_vlan_tbl_entry_cmd req; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + /* check if mac addr is valid */ + if (!rte_is_valid_assigned_ether_addr(mac_addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "remove unicast mac addr err! addr(%s) invalid", + mac_str); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hns3_set_bit(req.entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0); + hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, false); + ret = hns3_remove_mac_vlan_tbl(hw, &req); + if (ret == -ENOENT) /* mac addr isn't existent in the mac vlan table. */ + return 0; + else if (ret == 0) + hns3_update_umv_space(hw, true); + + return ret; +} + +static void +hns3_remove_mac_addr(struct rte_eth_dev *dev, uint32_t idx) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + /* index will be checked by upper level rte interface */ + struct rte_ether_addr *mac_addr = &dev->data->mac_addrs[idx]; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + rte_spinlock_lock(&hw->lock); + + if (rte_is_multicast_ether_addr(mac_addr)) + ret = hns3_remove_mc_addr_common(hw, mac_addr); + else + ret = hns3_remove_uc_addr_common(hw, mac_addr); + rte_spinlock_unlock(&hw->lock); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to remove mac addr(%s), ret = %d", mac_str, + ret); + } +} + +static int +hns3_set_default_mac_addr(struct rte_eth_dev *dev, + struct rte_ether_addr *mac_addr) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_ether_addr *oaddr; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + bool default_addr_setted; + bool rm_succes = false; + int ret, ret_val; + + /* + * It has been guaranteed that input parameter named mac_addr is valid + * address in the rte layer of DPDK framework. + */ + oaddr = (struct rte_ether_addr *)hw->mac.mac_addr; + default_addr_setted = hw->mac.default_addr_setted; + if (default_addr_setted && !!rte_is_same_ether_addr(mac_addr, oaddr)) + return 0; + + rte_spinlock_lock(&hw->lock); + if (default_addr_setted) { + ret = hns3_remove_uc_addr_common(hw, oaddr); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + oaddr); + hns3_warn(hw, "Remove old uc mac address(%s) fail: %d", + mac_str, ret); + rm_succes = false; + } else + rm_succes = true; + } + + ret = hns3_add_uc_addr_common(hw, mac_addr); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Failed to set mac addr(%s): %d", mac_str, ret); + goto err_add_uc_addr; + } + + ret = hns3_pause_addr_cfg(hw, mac_addr->addr_bytes); + if (ret) { + hns3_err(hw, "Failed to configure mac pause address: %d", ret); + goto err_pause_addr_cfg; + } + + rte_ether_addr_copy(mac_addr, + (struct rte_ether_addr *)hw->mac.mac_addr); + hw->mac.default_addr_setted = true; + rte_spinlock_unlock(&hw->lock); + + return 0; + +err_pause_addr_cfg: + ret_val = hns3_remove_uc_addr_common(hw, mac_addr); + if (ret_val) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_warn(hw, + "Failed to roll back to del setted mac addr(%s): %d", + mac_str, ret_val); + } + +err_add_uc_addr: + if (rm_succes) { + ret_val = hns3_add_uc_addr_common(hw, oaddr); + if (ret_val) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + oaddr); + hns3_warn(hw, + "Failed to restore old uc mac addr(%s): %d", + mac_str, ret_val); + hw->mac.default_addr_setted = false; + } + } + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_configure_all_mac_addr(struct hns3_adapter *hns, bool del) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct hns3_hw *hw = &hns->hw; + struct rte_ether_addr *addr; + int err = 0; + int ret; + int i; + + for (i = 0; i < HNS3_UC_MACADDR_NUM; i++) { + addr = &hw->data->mac_addrs[i]; + if (rte_is_zero_ether_addr(addr)) + continue; + if (rte_is_multicast_ether_addr(addr)) + ret = del ? hns3_remove_mc_addr(hw, addr) : + hns3_add_mc_addr(hw, addr); + else + ret = del ? hns3_remove_uc_addr_common(hw, addr) : + hns3_add_uc_addr_common(hw, addr); + + if (ret) { + err = ret; + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to %s mac addr(%s) index:%d " + "ret = %d.", del ? "remove" : "restore", + mac_str, i, ret); + } + } + return err; +} + +static void +hns3_update_desc_vfid(struct hns3_cmd_desc *desc, uint8_t vfid, bool clr) +{ +#define HNS3_VF_NUM_IN_FIRST_DESC 192 + uint8_t word_num; + uint8_t bit_num; + + if (vfid < HNS3_VF_NUM_IN_FIRST_DESC) { + word_num = vfid / 32; + bit_num = vfid % 32; + if (clr) + desc[1].data[word_num] &= + rte_cpu_to_le_32(~(1UL << bit_num)); + else + desc[1].data[word_num] |= + rte_cpu_to_le_32(1UL << bit_num); + } else { + word_num = (vfid - HNS3_VF_NUM_IN_FIRST_DESC) / 32; + bit_num = vfid % 32; + if (clr) + desc[2].data[word_num] &= + rte_cpu_to_le_32(~(1UL << bit_num)); + else + desc[2].data[word_num] |= + rte_cpu_to_le_32(1UL << bit_num); + } +} + +static int +hns3_add_mc_addr(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + struct hns3_mac_vlan_tbl_entry_cmd req; + struct hns3_cmd_desc desc[3]; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + uint8_t vf_id; + int ret; + + /* Check if mac addr is valid */ + if (!rte_is_multicast_ether_addr(mac_addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add mc mac addr, addr(%s) invalid", + mac_str); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hns3_set_bit(req.entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0); + hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, true); + ret = hns3_lookup_mac_vlan_tbl(hw, &req, desc, true); + if (ret) { + /* This mac addr do not exist, add new entry for it */ + memset(desc[0].data, 0, sizeof(desc[0].data)); + memset(desc[1].data, 0, sizeof(desc[0].data)); + memset(desc[2].data, 0, sizeof(desc[0].data)); + } + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, the PF-related vf_id is 0, just need to configure parameters + * for vf_id 0. + */ + vf_id = 0; + hns3_update_desc_vfid(desc, vf_id, false); + ret = hns3_add_mac_vlan_tbl(hw, &req, desc); + if (ret) { + if (ret == -ENOSPC) + hns3_err(hw, "mc mac vlan table is full"); + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add mc mac addr(%s): %d", mac_str, ret); + } + + return ret; +} + +static int +hns3_remove_mc_addr(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + struct hns3_mac_vlan_tbl_entry_cmd req; + struct hns3_cmd_desc desc[3]; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + uint8_t vf_id; + int ret; + + /* Check if mac addr is valid */ + if (!rte_is_multicast_ether_addr(mac_addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Failed to rm mc mac addr, addr(%s) invalid", + mac_str); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hns3_set_bit(req.entry_type, HNS3_MAC_VLAN_BIT0_EN_B, 0); + hns3_prepare_mac_addr(&req, mac_addr->addr_bytes, true); + ret = hns3_lookup_mac_vlan_tbl(hw, &req, desc, true); + if (ret == 0) { + /* + * This mac addr exist, remove this handle's VFID for it. + * In current version VF is not supported when PF is driven by + * DPDK driver, the PF-related vf_id is 0, just need to + * configure parameters for vf_id 0. + */ + vf_id = 0; + hns3_update_desc_vfid(desc, vf_id, true); + + /* All the vfid is zero, so need to delete this entry */ + ret = hns3_remove_mac_vlan_tbl(hw, &req); + } else if (ret == -ENOENT) { + /* This mac addr doesn't exist. */ + return 0; + } + + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Failed to rm mc mac addr(%s): %d", mac_str, ret); + } + + return ret; +} + +static int +hns3_set_mc_addr_chk_param(struct hns3_hw *hw, + struct rte_ether_addr *mc_addr_set, + uint32_t nb_mc_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct rte_ether_addr *addr; + uint32_t i; + uint32_t j; + + if (nb_mc_addr > HNS3_MC_MACADDR_NUM) { + hns3_err(hw, "failed to set mc mac addr, nb_mc_addr(%d) " + "invalid. valid range: 0~%d", + nb_mc_addr, HNS3_MC_MACADDR_NUM); + return -EINVAL; + } + + /* Check if input mac addresses are valid */ + for (i = 0; i < nb_mc_addr; i++) { + addr = &mc_addr_set[i]; + if (!rte_is_multicast_ether_addr(addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, + "failed to set mc mac addr, addr(%s) invalid.", + mac_str); + return -EINVAL; + } + + /* Check if there are duplicate addresses */ + for (j = i + 1; j < nb_mc_addr; j++) { + if (rte_is_same_ether_addr(addr, &mc_addr_set[j])) { + rte_ether_format_addr(mac_str, + RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to set mc mac addr, " + "addrs invalid. two same addrs(%s).", + mac_str); + return -EINVAL; + } + } + + /* + * Check if there are duplicate addresses between mac_addrs + * and mc_addr_set + */ + for (j = 0; j < HNS3_UC_MACADDR_NUM; j++) { + if (rte_is_same_ether_addr(addr, + &hw->data->mac_addrs[j])) { + rte_ether_format_addr(mac_str, + RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to set mc mac addr, " + "addrs invalid. addrs(%s) has already " + "configured in mac_addr add API", + mac_str); + return -EINVAL; + } + } + } + + return 0; +} + +static void +hns3_set_mc_addr_calc_addr(struct hns3_hw *hw, + struct rte_ether_addr *mc_addr_set, + int mc_addr_num, + struct rte_ether_addr *reserved_addr_list, + int *reserved_addr_num, + struct rte_ether_addr *add_addr_list, + int *add_addr_num, + struct rte_ether_addr *rm_addr_list, + int *rm_addr_num) +{ + struct rte_ether_addr *addr; + int current_addr_num; + int reserved_num = 0; + int add_num = 0; + int rm_num = 0; + int num; + int i; + int j; + bool same_addr; + + /* Calculate the mc mac address list that should be removed */ + current_addr_num = hw->mc_addrs_num; + for (i = 0; i < current_addr_num; i++) { + addr = &hw->mc_addrs[i]; + same_addr = false; + for (j = 0; j < mc_addr_num; j++) { + if (rte_is_same_ether_addr(addr, &mc_addr_set[j])) { + same_addr = true; + break; + } + } + + if (!same_addr) { + rte_ether_addr_copy(addr, &rm_addr_list[rm_num]); + rm_num++; + } else { + rte_ether_addr_copy(addr, + &reserved_addr_list[reserved_num]); + reserved_num++; + } + } + + /* Calculate the mc mac address list that should be added */ + for (i = 0; i < mc_addr_num; i++) { + addr = &mc_addr_set[i]; + same_addr = false; + for (j = 0; j < current_addr_num; j++) { + if (rte_is_same_ether_addr(addr, &hw->mc_addrs[j])) { + same_addr = true; + break; + } + } + + if (!same_addr) { + rte_ether_addr_copy(addr, &add_addr_list[add_num]); + add_num++; + } + } + + /* Reorder the mc mac address list maintained by driver */ + for (i = 0; i < reserved_num; i++) + rte_ether_addr_copy(&reserved_addr_list[i], &hw->mc_addrs[i]); + + for (i = 0; i < rm_num; i++) { + num = reserved_num + i; + rte_ether_addr_copy(&rm_addr_list[i], &hw->mc_addrs[num]); + } + + *reserved_addr_num = reserved_num; + *add_addr_num = add_num; + *rm_addr_num = rm_num; +} + +static int +hns3_set_mc_mac_addr_list(struct rte_eth_dev *dev, + struct rte_ether_addr *mc_addr_set, + uint32_t nb_mc_addr) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_ether_addr reserved_addr_list[HNS3_MC_MACADDR_NUM]; + struct rte_ether_addr add_addr_list[HNS3_MC_MACADDR_NUM]; + struct rte_ether_addr rm_addr_list[HNS3_MC_MACADDR_NUM]; + struct rte_ether_addr *addr; + int reserved_addr_num; + int add_addr_num; + int rm_addr_num; + int mc_addr_num; + int num; + int ret; + int i; + + /* Check if input parameters are valid */ + ret = hns3_set_mc_addr_chk_param(hw, mc_addr_set, nb_mc_addr); + if (ret) + return ret; + + rte_spinlock_lock(&hw->lock); + + /* + * Calculate the mc mac address lists those should be removed and be + * added, Reorder the mc mac address list maintained by driver. + */ + mc_addr_num = (int)nb_mc_addr; + hns3_set_mc_addr_calc_addr(hw, mc_addr_set, mc_addr_num, + reserved_addr_list, &reserved_addr_num, + add_addr_list, &add_addr_num, + rm_addr_list, &rm_addr_num); + + /* Remove mc mac addresses */ + for (i = 0; i < rm_addr_num; i++) { + num = rm_addr_num - i - 1; + addr = &rm_addr_list[num]; + ret = hns3_remove_mc_addr(hw, addr); + if (ret) { + rte_spinlock_unlock(&hw->lock); + return ret; + } + hw->mc_addrs_num--; + } + + /* Add mc mac addresses */ + for (i = 0; i < add_addr_num; i++) { + addr = &add_addr_list[i]; + ret = hns3_add_mc_addr(hw, addr); + if (ret) { + rte_spinlock_unlock(&hw->lock); + return ret; + } + + num = reserved_addr_num + i; + rte_ether_addr_copy(addr, &hw->mc_addrs[num]); + hw->mc_addrs_num++; + } + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3_configure_all_mc_mac_addr(struct hns3_adapter *hns, bool del) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct hns3_hw *hw = &hns->hw; + struct rte_ether_addr *addr; + int err = 0; + int ret; + int i; + + for (i = 0; i < hw->mc_addrs_num; i++) { + addr = &hw->mc_addrs[i]; + if (!rte_is_multicast_ether_addr(addr)) + continue; + if (del) + ret = hns3_remove_mc_addr(hw, addr); + else + ret = hns3_add_mc_addr(hw, addr); + if (ret) { + err = ret; + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_dbg(hw, "%s mc mac addr: %s failed for pf: ret = %d", + del ? "Remove" : "Restore", mac_str, ret); + } + } + return err; +} + +static int +hns3_check_mq_mode(struct rte_eth_dev *dev) +{ + enum rte_eth_rx_mq_mode rx_mq_mode = dev->data->dev_conf.rxmode.mq_mode; + enum rte_eth_tx_mq_mode tx_mq_mode = dev->data->dev_conf.txmode.mq_mode; + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + struct rte_eth_dcb_rx_conf *dcb_rx_conf; + struct rte_eth_dcb_tx_conf *dcb_tx_conf; + uint8_t num_tc; + int max_tc = 0; + int i; + + dcb_rx_conf = &dev->data->dev_conf.rx_adv_conf.dcb_rx_conf; + dcb_tx_conf = &dev->data->dev_conf.tx_adv_conf.dcb_tx_conf; + + if (rx_mq_mode == ETH_MQ_RX_VMDQ_DCB_RSS) { + hns3_err(hw, "ETH_MQ_RX_VMDQ_DCB_RSS is not supported. " + "rx_mq_mode = %d", rx_mq_mode); + return -EINVAL; + } + + if (rx_mq_mode == ETH_MQ_RX_VMDQ_DCB || + tx_mq_mode == ETH_MQ_TX_VMDQ_DCB) { + hns3_err(hw, "ETH_MQ_RX_VMDQ_DCB and ETH_MQ_TX_VMDQ_DCB " + "is not supported. rx_mq_mode = %d, tx_mq_mode = %d", + rx_mq_mode, tx_mq_mode); + return -EINVAL; + } + + if (rx_mq_mode == ETH_MQ_RX_DCB_RSS) { + if (dcb_rx_conf->nb_tcs > pf->tc_max) { + hns3_err(hw, "nb_tcs(%u) > max_tc(%u) driver supported.", + dcb_rx_conf->nb_tcs, pf->tc_max); + return -EINVAL; + } + + if (!(dcb_rx_conf->nb_tcs == HNS3_4_TCS || + dcb_rx_conf->nb_tcs == HNS3_8_TCS)) { + hns3_err(hw, "on ETH_MQ_RX_DCB_RSS mode, " + "nb_tcs(%d) != %d or %d in rx direction.", + dcb_rx_conf->nb_tcs, HNS3_4_TCS, HNS3_8_TCS); + return -EINVAL; + } + + if (dcb_rx_conf->nb_tcs != dcb_tx_conf->nb_tcs) { + hns3_err(hw, "num_tcs(%d) of tx is not equal to rx(%d)", + dcb_tx_conf->nb_tcs, dcb_rx_conf->nb_tcs); + return -EINVAL; + } + + for (i = 0; i < HNS3_MAX_USER_PRIO; i++) { + if (dcb_rx_conf->dcb_tc[i] != dcb_tx_conf->dcb_tc[i]) { + hns3_err(hw, "dcb_tc[%d] = %d in rx direction, " + "is not equal to one in tx direction.", + i, dcb_rx_conf->dcb_tc[i]); + return -EINVAL; + } + if (dcb_rx_conf->dcb_tc[i] > max_tc) + max_tc = dcb_rx_conf->dcb_tc[i]; + } + + num_tc = max_tc + 1; + if (num_tc > dcb_rx_conf->nb_tcs) { + hns3_err(hw, "max num_tc(%u) mapped > nb_tcs(%u)", + num_tc, dcb_rx_conf->nb_tcs); + return -EINVAL; + } + } + + return 0; +} + +static int +hns3_check_dcb_cfg(struct rte_eth_dev *dev) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + if (!hns3_dev_dcb_supported(hw)) { + hns3_err(hw, "this port does not support dcb configurations."); + return -EOPNOTSUPP; + } + + if (hw->current_fc_status == HNS3_FC_STATUS_MAC_PAUSE) { + hns3_err(hw, "MAC pause enabled, cannot config dcb info."); + return -EOPNOTSUPP; + } + + /* Check multiple queue mode */ + return hns3_check_mq_mode(dev); +} + +static int +hns3_bind_ring_with_vector(struct hns3_hw *hw, uint8_t vector_id, bool mmap, + enum hns3_ring_type queue_type, uint16_t queue_id) +{ + struct hns3_cmd_desc desc; + struct hns3_ctrl_vector_chain_cmd *req = + (struct hns3_ctrl_vector_chain_cmd *)desc.data; + enum hns3_cmd_status status; + enum hns3_opcode_type op; + uint16_t tqp_type_and_id = 0; + const char *op_str; + uint16_t type; + uint16_t gl; + + op = mmap ? HNS3_OPC_ADD_RING_TO_VECTOR : HNS3_OPC_DEL_RING_TO_VECTOR; + hns3_cmd_setup_basic_desc(&desc, op, false); + req->int_vector_id = vector_id; + + if (queue_type == HNS3_RING_TYPE_RX) + gl = HNS3_RING_GL_RX; + else + gl = HNS3_RING_GL_TX; + + type = queue_type; + + hns3_set_field(tqp_type_and_id, HNS3_INT_TYPE_M, HNS3_INT_TYPE_S, + type); + hns3_set_field(tqp_type_and_id, HNS3_TQP_ID_M, HNS3_TQP_ID_S, queue_id); + hns3_set_field(tqp_type_and_id, HNS3_INT_GL_IDX_M, HNS3_INT_GL_IDX_S, + gl); + req->tqp_type_and_id[0] = rte_cpu_to_le_16(tqp_type_and_id); + req->int_cause_num = 1; + op_str = mmap ? "Map" : "Unmap"; + status = hns3_cmd_send(hw, &desc, 1); + if (status) { + hns3_err(hw, "%s TQP %d fail, vector_id is %d, status is %d.", + op_str, queue_id, req->int_vector_id, status); + return status; + } + + return 0; +} + +static int +hns3_init_ring_with_vector(struct hns3_hw *hw) +{ + uint8_t vec; + int ret; + int i; + + /* + * In hns3 network engine, vector 0 is always the misc interrupt of this + * function, vector 1~N can be used respectively for the queues of the + * function. Tx and Rx queues with the same number share the interrupt + * vector. In the initialization clearing the all hardware mapping + * relationship configurations between queues and interrupt vectors is + * needed, so some error caused by the residual configurations, such as + * the unexpected Tx interrupt, can be avoid. Because of the hardware + * constraints in hns3 hardware engine, we have to implement clearing + * the mapping relationship configurations by binding all queues to the + * last interrupt vector and reserving the last interrupt vector. This + * method results in a decrease of the maximum queues when upper + * applications call the rte_eth_dev_configure API function to enable + * Rx interrupt. + */ + vec = hw->num_msi - 1; /* vector 0 for misc interrupt, not for queue */ + /* vec - 1: the last interrupt is reserved */ + hw->intr_tqps_num = vec > hw->tqps_num ? hw->tqps_num : vec - 1; + for (i = 0; i < hw->intr_tqps_num; i++) { + /* + * Set gap limiter and rate limiter configuration of queue's + * interrupt. + */ + hns3_set_queue_intr_gl(hw, i, HNS3_RING_GL_RX, + HNS3_TQP_INTR_GL_DEFAULT); + hns3_set_queue_intr_gl(hw, i, HNS3_RING_GL_TX, + HNS3_TQP_INTR_GL_DEFAULT); + hns3_set_queue_intr_rl(hw, i, HNS3_TQP_INTR_RL_DEFAULT); + + ret = hns3_bind_ring_with_vector(hw, vec, false, + HNS3_RING_TYPE_TX, i); + if (ret) { + PMD_INIT_LOG(ERR, "PF fail to unbind TX ring(%d) with " + "vector: %d, ret=%d", i, vec, ret); + return ret; + } + + ret = hns3_bind_ring_with_vector(hw, vec, false, + HNS3_RING_TYPE_RX, i); + if (ret) { + PMD_INIT_LOG(ERR, "PF fail to unbind RX ring(%d) with " + "vector: %d, ret=%d", i, vec, ret); + return ret; + } + } + + return 0; +} + +static int +hns3_dev_configure(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct rte_eth_conf *conf = &dev->data->dev_conf; + enum rte_eth_rx_mq_mode mq_mode = conf->rxmode.mq_mode; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint16_t nb_rx_q = dev->data->nb_rx_queues; + uint16_t nb_tx_q = dev->data->nb_tx_queues; + struct rte_eth_rss_conf rss_conf; + uint16_t mtu; + int ret; + + /* + * Hardware does not support individually enable/disable/reset the Tx or + * Rx queue in hns3 network engine. Driver must enable/disable/reset Tx + * and Rx queues at the same time. When the numbers of Tx queues + * allocated by upper applications are not equal to the numbers of Rx + * queues, driver needs to setup fake Tx or Rx queues to adjust numbers + * of Tx/Rx queues. otherwise, network engine can not work as usual. But + * these fake queues are imperceptible, and can not be used by upper + * applications. + */ + ret = hns3_set_fake_rx_or_tx_queues(dev, nb_rx_q, nb_tx_q); + if (ret) { + hns3_err(hw, "Failed to set rx/tx fake queues: %d", ret); + return ret; + } + + hw->adapter_state = HNS3_NIC_CONFIGURING; + if (conf->link_speeds & ETH_LINK_SPEED_FIXED) { + hns3_err(hw, "setting link speed/duplex not supported"); + ret = -EINVAL; + goto cfg_err; + } + + if ((uint32_t)mq_mode & ETH_MQ_RX_DCB_FLAG) { + ret = hns3_check_dcb_cfg(dev); + if (ret) + goto cfg_err; + } + + /* When RSS is not configured, redirect the packet queue 0 */ + if ((uint32_t)mq_mode & ETH_MQ_RX_RSS_FLAG) { + rss_conf = conf->rx_adv_conf.rss_conf; + if (rss_conf.rss_key == NULL) { + rss_conf.rss_key = rss_cfg->key; + rss_conf.rss_key_len = HNS3_RSS_KEY_SIZE; + } + + ret = hns3_dev_rss_hash_update(dev, &rss_conf); + if (ret) + goto cfg_err; + } + + /* + * If jumbo frames are enabled, MTU needs to be refreshed + * according to the maximum RX packet length. + */ + if (conf->rxmode.offloads & DEV_RX_OFFLOAD_JUMBO_FRAME) { + /* + * Security of max_rx_pkt_len is guaranteed in dpdk frame. + * Maximum value of max_rx_pkt_len is HNS3_MAX_FRAME_LEN, so it + * can safely assign to "uint16_t" type variable. + */ + mtu = (uint16_t)HNS3_PKTLEN_TO_MTU(conf->rxmode.max_rx_pkt_len); + ret = hns3_dev_mtu_set(dev, mtu); + if (ret) + goto cfg_err; + dev->data->mtu = mtu; + } + + ret = hns3_dev_configure_vlan(dev); + if (ret) + goto cfg_err; + + hw->adapter_state = HNS3_NIC_CONFIGURED; + + return 0; + +cfg_err: + (void)hns3_set_fake_rx_or_tx_queues(dev, 0, 0); + hw->adapter_state = HNS3_NIC_INITIALIZED; + + return ret; +} + +static int +hns3_set_mac_mtu(struct hns3_hw *hw, uint16_t new_mps) +{ + struct hns3_config_max_frm_size_cmd *req; + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CONFIG_MAX_FRM_SIZE, false); + + req = (struct hns3_config_max_frm_size_cmd *)desc.data; + req->max_frm_size = rte_cpu_to_le_16(new_mps); + req->min_frm_size = RTE_ETHER_MIN_LEN; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_config_mtu(struct hns3_hw *hw, uint16_t mps) +{ + int ret; + + ret = hns3_set_mac_mtu(hw, mps); + if (ret) { + hns3_err(hw, "Failed to set mtu, ret = %d", ret); + return ret; + } + + ret = hns3_buffer_alloc(hw); + if (ret) + hns3_err(hw, "Failed to allocate buffer, ret = %d", ret); + + return ret; +} + +static int +hns3_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu) +{ + struct hns3_adapter *hns = dev->data->dev_private; + uint32_t frame_size = mtu + HNS3_ETH_OVERHEAD; + struct hns3_hw *hw = &hns->hw; + bool is_jumbo_frame; + int ret; + + if (dev->data->dev_started) { + hns3_err(hw, "Failed to set mtu, port %u must be stopped " + "before configuration", dev->data->port_id); + return -EBUSY; + } + + rte_spinlock_lock(&hw->lock); + is_jumbo_frame = frame_size > RTE_ETHER_MAX_LEN ? true : false; + frame_size = RTE_MAX(frame_size, HNS3_DEFAULT_FRAME_LEN); + + /* + * Maximum value of frame_size is HNS3_MAX_FRAME_LEN, so it can safely + * assign to "uint16_t" type variable. + */ + ret = hns3_config_mtu(hw, (uint16_t)frame_size); + if (ret) { + rte_spinlock_unlock(&hw->lock); + hns3_err(hw, "Failed to set mtu, port %u mtu %u: %d", + dev->data->port_id, mtu, ret); + return ret; + } + hns->pf.mps = (uint16_t)frame_size; + if (is_jumbo_frame) + dev->data->dev_conf.rxmode.offloads |= + DEV_RX_OFFLOAD_JUMBO_FRAME; + else + dev->data->dev_conf.rxmode.offloads &= + ~DEV_RX_OFFLOAD_JUMBO_FRAME; + dev->data->dev_conf.rxmode.max_rx_pkt_len = frame_size; + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint16_t queue_num = hw->tqps_num; + + /* + * In interrupt mode, 'max_rx_queues' is set based on the number of + * MSI-X interrupt resources of the hardware. + */ + if (hw->data->dev_conf.intr_conf.rxq == 1) + queue_num = hw->intr_tqps_num; + + info->max_rx_queues = queue_num; + info->max_tx_queues = hw->tqps_num; + info->max_rx_pktlen = HNS3_MAX_FRAME_LEN; /* CRC included */ + info->min_rx_bufsize = hw->rx_buf_len; + info->max_mac_addrs = HNS3_UC_MACADDR_NUM; + info->max_mtu = info->max_rx_pktlen - HNS3_ETH_OVERHEAD; + info->rx_offload_capa = (DEV_RX_OFFLOAD_IPV4_CKSUM | + DEV_RX_OFFLOAD_TCP_CKSUM | + DEV_RX_OFFLOAD_UDP_CKSUM | + DEV_RX_OFFLOAD_SCTP_CKSUM | + DEV_RX_OFFLOAD_OUTER_IPV4_CKSUM | + DEV_RX_OFFLOAD_OUTER_UDP_CKSUM | + DEV_RX_OFFLOAD_KEEP_CRC | + DEV_RX_OFFLOAD_SCATTER | + DEV_RX_OFFLOAD_VLAN_STRIP | + DEV_RX_OFFLOAD_QINQ_STRIP | + DEV_RX_OFFLOAD_VLAN_FILTER | + DEV_RX_OFFLOAD_VLAN_EXTEND | + DEV_RX_OFFLOAD_JUMBO_FRAME | + DEV_RX_OFFLOAD_RSS_HASH); + info->tx_queue_offload_capa = DEV_TX_OFFLOAD_MBUF_FAST_FREE; + info->tx_offload_capa = (DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM | + DEV_TX_OFFLOAD_IPV4_CKSUM | + DEV_TX_OFFLOAD_TCP_CKSUM | + DEV_TX_OFFLOAD_UDP_CKSUM | + DEV_TX_OFFLOAD_SCTP_CKSUM | + DEV_TX_OFFLOAD_VLAN_INSERT | + DEV_TX_OFFLOAD_QINQ_INSERT | + DEV_TX_OFFLOAD_MULTI_SEGS | + DEV_TX_OFFLOAD_TCP_TSO | + DEV_TX_OFFLOAD_VXLAN_TNL_TSO | + DEV_TX_OFFLOAD_GRE_TNL_TSO | + DEV_TX_OFFLOAD_GENEVE_TNL_TSO | + info->tx_queue_offload_capa); + + info->rx_desc_lim = (struct rte_eth_desc_lim) { + .nb_max = HNS3_MAX_RING_DESC, + .nb_min = HNS3_MIN_RING_DESC, + .nb_align = HNS3_ALIGN_RING_DESC, + }; + + info->tx_desc_lim = (struct rte_eth_desc_lim) { + .nb_max = HNS3_MAX_RING_DESC, + .nb_min = HNS3_MIN_RING_DESC, + .nb_align = HNS3_ALIGN_RING_DESC, + }; + + info->vmdq_queue_num = 0; + + info->reta_size = HNS3_RSS_IND_TBL_SIZE; + info->hash_key_size = HNS3_RSS_KEY_SIZE; + info->flow_type_rss_offloads = HNS3_ETH_RSS_SUPPORT; + + info->default_rxportconf.burst_size = HNS3_DEFAULT_PORT_CONF_BURST_SIZE; + info->default_txportconf.burst_size = HNS3_DEFAULT_PORT_CONF_BURST_SIZE; + info->default_rxportconf.nb_queues = HNS3_DEFAULT_PORT_CONF_QUEUES_NUM; + info->default_txportconf.nb_queues = HNS3_DEFAULT_PORT_CONF_QUEUES_NUM; + info->default_rxportconf.ring_size = HNS3_DEFAULT_RING_DESC; + info->default_txportconf.ring_size = HNS3_DEFAULT_RING_DESC; + + return 0; +} + +static int +hns3_fw_version_get(struct rte_eth_dev *eth_dev, char *fw_version, + size_t fw_size) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint32_t version = hw->fw_version; + int ret; + + ret = snprintf(fw_version, fw_size, "%lu.%lu.%lu.%lu", + hns3_get_field(version, HNS3_FW_VERSION_BYTE3_M, + HNS3_FW_VERSION_BYTE3_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE2_M, + HNS3_FW_VERSION_BYTE2_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE1_M, + HNS3_FW_VERSION_BYTE1_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE0_M, + HNS3_FW_VERSION_BYTE0_S)); + ret += 1; /* add the size of '\0' */ + if (fw_size < (uint32_t)ret) + return ret; + else + return 0; +} + +static int +hns3_dev_link_update(struct rte_eth_dev *eth_dev, + __rte_unused int wait_to_complete) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_mac *mac = &hw->mac; + struct rte_eth_link new_link; + + if (!hns3_is_reset_pending(hns)) { + hns3_update_speed_duplex(eth_dev); + hns3_update_link_status(hw); + } + + memset(&new_link, 0, sizeof(new_link)); + switch (mac->link_speed) { + case ETH_SPEED_NUM_10M: + case ETH_SPEED_NUM_100M: + case ETH_SPEED_NUM_1G: + case ETH_SPEED_NUM_10G: + case ETH_SPEED_NUM_25G: + case ETH_SPEED_NUM_40G: + case ETH_SPEED_NUM_50G: + case ETH_SPEED_NUM_100G: + new_link.link_speed = mac->link_speed; + break; + default: + new_link.link_speed = ETH_SPEED_NUM_100M; + break; + } + + new_link.link_duplex = mac->link_duplex; + new_link.link_status = mac->link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + new_link.link_autoneg = + !(eth_dev->data->dev_conf.link_speeds & ETH_LINK_SPEED_FIXED); + + return rte_eth_linkstatus_set(eth_dev, &new_link); +} + +static int +hns3_parse_func_status(struct hns3_hw *hw, struct hns3_func_status_cmd *status) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + + if (!(status->pf_state & HNS3_PF_STATE_DONE)) + return -EINVAL; + + pf->is_main_pf = (status->pf_state & HNS3_PF_STATE_MAIN) ? true : false; + + return 0; +} + +static int +hns3_query_function_status(struct hns3_hw *hw) +{ +#define HNS3_QUERY_MAX_CNT 10 +#define HNS3_QUERY_SLEEP_MSCOEND 1 + struct hns3_func_status_cmd *req; + struct hns3_cmd_desc desc; + int timeout = 0; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_FUNC_STATUS, true); + req = (struct hns3_func_status_cmd *)desc.data; + + do { + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + PMD_INIT_LOG(ERR, "query function status failed %d", + ret); + return ret; + } + + /* Check pf reset is done */ + if (req->pf_state) + break; + + rte_delay_ms(HNS3_QUERY_SLEEP_MSCOEND); + } while (timeout++ < HNS3_QUERY_MAX_CNT); + + return hns3_parse_func_status(hw, req); +} + +static int +hns3_query_pf_resource(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_pf_res_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_PF_RSRC, true); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + PMD_INIT_LOG(ERR, "query pf resource failed %d", ret); + return ret; + } + + req = (struct hns3_pf_res_cmd *)desc.data; + hw->total_tqps_num = rte_le_to_cpu_16(req->tqp_num); + pf->pkt_buf_size = rte_le_to_cpu_16(req->buf_size) << HNS3_BUF_UNIT_S; + hw->tqps_num = RTE_MIN(hw->total_tqps_num, HNS3_MAX_TQP_NUM_PER_FUNC); + pf->func_num = rte_le_to_cpu_16(req->pf_own_fun_number); + + if (req->tx_buf_size) + pf->tx_buf_size = + rte_le_to_cpu_16(req->tx_buf_size) << HNS3_BUF_UNIT_S; + else + pf->tx_buf_size = HNS3_DEFAULT_TX_BUF; + + pf->tx_buf_size = roundup(pf->tx_buf_size, HNS3_BUF_SIZE_UNIT); + + if (req->dv_buf_size) + pf->dv_buf_size = + rte_le_to_cpu_16(req->dv_buf_size) << HNS3_BUF_UNIT_S; + else + pf->dv_buf_size = HNS3_DEFAULT_DV; + + pf->dv_buf_size = roundup(pf->dv_buf_size, HNS3_BUF_SIZE_UNIT); + + hw->num_msi = + hns3_get_field(rte_le_to_cpu_16(req->pf_intr_vector_number), + HNS3_VEC_NUM_M, HNS3_VEC_NUM_S); + + return 0; +} + +static void +hns3_parse_cfg(struct hns3_cfg *cfg, struct hns3_cmd_desc *desc) +{ + struct hns3_cfg_param_cmd *req; + uint64_t mac_addr_tmp_high; + uint64_t mac_addr_tmp; + uint32_t i; + + req = (struct hns3_cfg_param_cmd *)desc[0].data; + + /* get the configuration */ + cfg->vmdq_vport_num = hns3_get_field(rte_le_to_cpu_32(req->param[0]), + HNS3_CFG_VMDQ_M, HNS3_CFG_VMDQ_S); + cfg->tc_num = hns3_get_field(rte_le_to_cpu_32(req->param[0]), + HNS3_CFG_TC_NUM_M, HNS3_CFG_TC_NUM_S); + cfg->tqp_desc_num = hns3_get_field(rte_le_to_cpu_32(req->param[0]), + HNS3_CFG_TQP_DESC_N_M, + HNS3_CFG_TQP_DESC_N_S); + + cfg->phy_addr = hns3_get_field(rte_le_to_cpu_32(req->param[1]), + HNS3_CFG_PHY_ADDR_M, + HNS3_CFG_PHY_ADDR_S); + cfg->media_type = hns3_get_field(rte_le_to_cpu_32(req->param[1]), + HNS3_CFG_MEDIA_TP_M, + HNS3_CFG_MEDIA_TP_S); + cfg->rx_buf_len = hns3_get_field(rte_le_to_cpu_32(req->param[1]), + HNS3_CFG_RX_BUF_LEN_M, + HNS3_CFG_RX_BUF_LEN_S); + /* get mac address */ + mac_addr_tmp = rte_le_to_cpu_32(req->param[2]); + mac_addr_tmp_high = hns3_get_field(rte_le_to_cpu_32(req->param[3]), + HNS3_CFG_MAC_ADDR_H_M, + HNS3_CFG_MAC_ADDR_H_S); + + mac_addr_tmp |= (mac_addr_tmp_high << 31) << 1; + + cfg->default_speed = hns3_get_field(rte_le_to_cpu_32(req->param[3]), + HNS3_CFG_DEFAULT_SPEED_M, + HNS3_CFG_DEFAULT_SPEED_S); + cfg->rss_size_max = hns3_get_field(rte_le_to_cpu_32(req->param[3]), + HNS3_CFG_RSS_SIZE_M, + HNS3_CFG_RSS_SIZE_S); + + for (i = 0; i < RTE_ETHER_ADDR_LEN; i++) + cfg->mac_addr[i] = (mac_addr_tmp >> (8 * i)) & 0xff; + + req = (struct hns3_cfg_param_cmd *)desc[1].data; + cfg->numa_node_map = rte_le_to_cpu_32(req->param[0]); + + cfg->speed_ability = hns3_get_field(rte_le_to_cpu_32(req->param[1]), + HNS3_CFG_SPEED_ABILITY_M, + HNS3_CFG_SPEED_ABILITY_S); + cfg->umv_space = hns3_get_field(rte_le_to_cpu_32(req->param[1]), + HNS3_CFG_UMV_TBL_SPACE_M, + HNS3_CFG_UMV_TBL_SPACE_S); + if (!cfg->umv_space) + cfg->umv_space = HNS3_DEFAULT_UMV_SPACE_PER_PF; +} + +/* hns3_get_board_cfg: query the static parameter from NCL_config file in flash + * @hw: pointer to struct hns3_hw + * @hcfg: the config structure to be getted + */ +static int +hns3_get_board_cfg(struct hns3_hw *hw, struct hns3_cfg *hcfg) +{ + struct hns3_cmd_desc desc[HNS3_PF_CFG_DESC_NUM]; + struct hns3_cfg_param_cmd *req; + uint32_t offset; + uint32_t i; + int ret; + + for (i = 0; i < HNS3_PF_CFG_DESC_NUM; i++) { + offset = 0; + req = (struct hns3_cfg_param_cmd *)desc[i].data; + hns3_cmd_setup_basic_desc(&desc[i], HNS3_OPC_GET_CFG_PARAM, + true); + hns3_set_field(offset, HNS3_CFG_OFFSET_M, HNS3_CFG_OFFSET_S, + i * HNS3_CFG_RD_LEN_BYTES); + /* Len should be divided by 4 when send to hardware */ + hns3_set_field(offset, HNS3_CFG_RD_LEN_M, HNS3_CFG_RD_LEN_S, + HNS3_CFG_RD_LEN_BYTES / HNS3_CFG_RD_LEN_UNIT); + req->offset = rte_cpu_to_le_32(offset); + } + + ret = hns3_cmd_send(hw, desc, HNS3_PF_CFG_DESC_NUM); + if (ret) { + PMD_INIT_LOG(ERR, "get config failed %d.", ret); + return ret; + } + + hns3_parse_cfg(hcfg, desc); + + return 0; +} + +static int +hns3_parse_speed(int speed_cmd, uint32_t *speed) +{ + switch (speed_cmd) { + case HNS3_CFG_SPEED_10M: + *speed = ETH_SPEED_NUM_10M; + break; + case HNS3_CFG_SPEED_100M: + *speed = ETH_SPEED_NUM_100M; + break; + case HNS3_CFG_SPEED_1G: + *speed = ETH_SPEED_NUM_1G; + break; + case HNS3_CFG_SPEED_10G: + *speed = ETH_SPEED_NUM_10G; + break; + case HNS3_CFG_SPEED_25G: + *speed = ETH_SPEED_NUM_25G; + break; + case HNS3_CFG_SPEED_40G: + *speed = ETH_SPEED_NUM_40G; + break; + case HNS3_CFG_SPEED_50G: + *speed = ETH_SPEED_NUM_50G; + break; + case HNS3_CFG_SPEED_100G: + *speed = ETH_SPEED_NUM_100G; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int +hns3_get_board_configuration(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_cfg cfg; + int ret; + + ret = hns3_get_board_cfg(hw, &cfg); + if (ret) { + PMD_INIT_LOG(ERR, "get board config failed %d", ret); + return ret; + } + + if (cfg.media_type == HNS3_MEDIA_TYPE_COPPER) { + PMD_INIT_LOG(ERR, "media type is copper, not supported."); + return -EOPNOTSUPP; + } + + hw->mac.media_type = cfg.media_type; + hw->rss_size_max = cfg.rss_size_max; + hw->rss_dis_flag = false; + hw->rx_buf_len = cfg.rx_buf_len; + memcpy(hw->mac.mac_addr, cfg.mac_addr, RTE_ETHER_ADDR_LEN); + hw->mac.phy_addr = cfg.phy_addr; + hw->mac.default_addr_setted = false; + hw->num_tx_desc = cfg.tqp_desc_num; + hw->num_rx_desc = cfg.tqp_desc_num; + hw->dcb_info.num_pg = 1; + hw->dcb_info.hw_pfc_map = 0; + + ret = hns3_parse_speed(cfg.default_speed, &hw->mac.link_speed); + if (ret) { + PMD_INIT_LOG(ERR, "Get wrong speed %d, ret = %d", + cfg.default_speed, ret); + return ret; + } + + pf->tc_max = cfg.tc_num; + if (pf->tc_max > HNS3_MAX_TC_NUM || pf->tc_max < 1) { + PMD_INIT_LOG(WARNING, + "Get TC num(%u) from flash, set TC num to 1", + pf->tc_max); + pf->tc_max = 1; + } + + /* Dev does not support DCB */ + if (!hns3_dev_dcb_supported(hw)) { + pf->tc_max = 1; + pf->pfc_max = 0; + } else + pf->pfc_max = pf->tc_max; + + hw->dcb_info.num_tc = 1; + hw->alloc_rss_size = RTE_MIN(hw->rss_size_max, + hw->tqps_num / hw->dcb_info.num_tc); + hns3_set_bit(hw->hw_tc_map, 0, 1); + pf->tx_sch_mode = HNS3_FLAG_TC_BASE_SCH_MODE; + + pf->wanted_umv_size = cfg.umv_space; + + return ret; +} + +static int +hns3_get_configuration(struct hns3_hw *hw) +{ + int ret; + + ret = hns3_query_function_status(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to query function status: %d.", ret); + return ret; + } + + /* Get pf resource */ + ret = hns3_query_pf_resource(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to query pf resource: %d", ret); + return ret; + } + + ret = hns3_get_board_configuration(hw); + if (ret) + PMD_INIT_LOG(ERR, "Failed to get board configuration: %d", ret); + + return ret; +} + +static int +hns3_map_tqps_to_func(struct hns3_hw *hw, uint16_t func_id, uint16_t tqp_pid, + uint16_t tqp_vid, bool is_pf) +{ + struct hns3_tqp_map_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_SET_TQP_MAP, false); + + req = (struct hns3_tqp_map_cmd *)desc.data; + req->tqp_id = rte_cpu_to_le_16(tqp_pid); + req->tqp_vf = func_id; + req->tqp_flag = 1 << HNS3_TQP_MAP_EN_B; + if (!is_pf) + req->tqp_flag |= (1 << HNS3_TQP_MAP_TYPE_B); + req->tqp_vid = rte_cpu_to_le_16(tqp_vid); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "TQP map failed %d", ret); + + return ret; +} + +static int +hns3_map_tqp(struct hns3_hw *hw) +{ + uint16_t tqps_num = hw->total_tqps_num; + uint16_t func_id; + uint16_t tqp_id; + bool is_pf; + int num; + int ret; + int i; + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, so we allocate tqps to PF as much as possible. + */ + tqp_id = 0; + num = DIV_ROUND_UP(hw->total_tqps_num, HNS3_MAX_TQP_NUM_PER_FUNC); + for (func_id = 0; func_id < num; func_id++) { + is_pf = func_id == 0 ? true : false; + for (i = 0; + i < HNS3_MAX_TQP_NUM_PER_FUNC && tqp_id < tqps_num; i++) { + ret = hns3_map_tqps_to_func(hw, func_id, tqp_id++, i, + is_pf); + if (ret) + return ret; + } + } + + return 0; +} + +static int +hns3_cfg_mac_speed_dup_hw(struct hns3_hw *hw, uint32_t speed, uint8_t duplex) +{ + struct hns3_config_mac_speed_dup_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + req = (struct hns3_config_mac_speed_dup_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CONFIG_SPEED_DUP, false); + + hns3_set_bit(req->speed_dup, HNS3_CFG_DUPLEX_B, !!duplex ? 1 : 0); + + switch (speed) { + case ETH_SPEED_NUM_10M: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_10M); + break; + case ETH_SPEED_NUM_100M: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_100M); + break; + case ETH_SPEED_NUM_1G: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_1G); + break; + case ETH_SPEED_NUM_10G: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_10G); + break; + case ETH_SPEED_NUM_25G: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_25G); + break; + case ETH_SPEED_NUM_40G: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_40G); + break; + case ETH_SPEED_NUM_50G: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_50G); + break; + case ETH_SPEED_NUM_100G: + hns3_set_field(req->speed_dup, HNS3_CFG_SPEED_M, + HNS3_CFG_SPEED_S, HNS3_CFG_SPEED_100G); + break; + default: + PMD_INIT_LOG(ERR, "invalid speed (%u)", speed); + return -EINVAL; + } + + hns3_set_bit(req->mac_change_fec_en, HNS3_CFG_MAC_SPEED_CHANGE_EN_B, 1); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "mac speed/duplex config cmd failed %d", ret); + + return ret; +} + +static int +hns3_tx_buffer_calc(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_priv_buf *priv; + uint32_t i, total_size; + + total_size = pf->pkt_buf_size; + + /* alloc tx buffer for all enabled tc */ + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + priv = &buf_alloc->priv_buf[i]; + + if (hw->hw_tc_map & BIT(i)) { + if (total_size < pf->tx_buf_size) + return -ENOMEM; + + priv->tx_buf_size = pf->tx_buf_size; + } else + priv->tx_buf_size = 0; + + total_size -= priv->tx_buf_size; + } + + return 0; +} + +static int +hns3_tx_buffer_alloc(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ +/* TX buffer size is unit by 128 byte */ +#define HNS3_BUF_SIZE_UNIT_SHIFT 7 +#define HNS3_BUF_SIZE_UPDATE_EN_MSK BIT(15) + struct hns3_tx_buff_alloc_cmd *req; + struct hns3_cmd_desc desc; + uint32_t buf_size; + uint32_t i; + int ret; + + req = (struct hns3_tx_buff_alloc_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_TX_BUFF_ALLOC, 0); + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + buf_size = buf_alloc->priv_buf[i].tx_buf_size; + + buf_size = buf_size >> HNS3_BUF_SIZE_UNIT_SHIFT; + req->tx_pkt_buff[i] = rte_cpu_to_le_16(buf_size | + HNS3_BUF_SIZE_UPDATE_EN_MSK); + } + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "tx buffer alloc cmd failed %d", ret); + + return ret; +} + +static int +hns3_get_tc_num(struct hns3_hw *hw) +{ + int cnt = 0; + uint8_t i; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) + if (hw->hw_tc_map & BIT(i)) + cnt++; + return cnt; +} + +static uint32_t +hns3_get_rx_priv_buff_alloced(struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_priv_buf *priv; + uint32_t rx_priv = 0; + int i; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + priv = &buf_alloc->priv_buf[i]; + if (priv->enable) + rx_priv += priv->buf_size; + } + return rx_priv; +} + +static uint32_t +hns3_get_tx_buff_alloced(struct hns3_pkt_buf_alloc *buf_alloc) +{ + uint32_t total_tx_size = 0; + uint32_t i; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) + total_tx_size += buf_alloc->priv_buf[i].tx_buf_size; + + return total_tx_size; +} + +/* Get the number of pfc enabled TCs, which have private buffer */ +static int +hns3_get_pfc_priv_num(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_priv_buf *priv; + int cnt = 0; + uint8_t i; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + priv = &buf_alloc->priv_buf[i]; + if ((hw->dcb_info.hw_pfc_map & BIT(i)) && priv->enable) + cnt++; + } + + return cnt; +} + +/* Get the number of pfc disabled TCs, which have private buffer */ +static int +hns3_get_no_pfc_priv_num(struct hns3_hw *hw, + struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_priv_buf *priv; + int cnt = 0; + uint8_t i; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + priv = &buf_alloc->priv_buf[i]; + if (hw->hw_tc_map & BIT(i) && + !(hw->dcb_info.hw_pfc_map & BIT(i)) && priv->enable) + cnt++; + } + + return cnt; +} + +static bool +hns3_is_rx_buf_ok(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc, + uint32_t rx_all) +{ + uint32_t shared_buf_min, shared_buf_tc, shared_std, hi_thrd, lo_thrd; + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint32_t shared_buf, aligned_mps; + uint32_t rx_priv; + uint8_t tc_num; + uint8_t i; + + tc_num = hns3_get_tc_num(hw); + aligned_mps = roundup(pf->mps, HNS3_BUF_SIZE_UNIT); + + if (hns3_dev_dcb_supported(hw)) + shared_buf_min = HNS3_BUF_MUL_BY * aligned_mps + + pf->dv_buf_size; + else + shared_buf_min = aligned_mps + HNS3_NON_DCB_ADDITIONAL_BUF + + pf->dv_buf_size; + + shared_buf_tc = tc_num * aligned_mps + aligned_mps; + shared_std = roundup(max_t(uint32_t, shared_buf_min, shared_buf_tc), + HNS3_BUF_SIZE_UNIT); + + rx_priv = hns3_get_rx_priv_buff_alloced(buf_alloc); + if (rx_all < rx_priv + shared_std) + return false; + + shared_buf = rounddown(rx_all - rx_priv, HNS3_BUF_SIZE_UNIT); + buf_alloc->s_buf.buf_size = shared_buf; + if (hns3_dev_dcb_supported(hw)) { + buf_alloc->s_buf.self.high = shared_buf - pf->dv_buf_size; + buf_alloc->s_buf.self.low = buf_alloc->s_buf.self.high + - roundup(aligned_mps / HNS3_BUF_DIV_BY, + HNS3_BUF_SIZE_UNIT); + } else { + buf_alloc->s_buf.self.high = + aligned_mps + HNS3_NON_DCB_ADDITIONAL_BUF; + buf_alloc->s_buf.self.low = aligned_mps; + } + + if (hns3_dev_dcb_supported(hw)) { + hi_thrd = shared_buf - pf->dv_buf_size; + + if (tc_num <= NEED_RESERVE_TC_NUM) + hi_thrd = hi_thrd * BUF_RESERVE_PERCENT + / BUF_MAX_PERCENT; + + if (tc_num) + hi_thrd = hi_thrd / tc_num; + + hi_thrd = max_t(uint32_t, hi_thrd, + HNS3_BUF_MUL_BY * aligned_mps); + hi_thrd = rounddown(hi_thrd, HNS3_BUF_SIZE_UNIT); + lo_thrd = hi_thrd - aligned_mps / HNS3_BUF_DIV_BY; + } else { + hi_thrd = aligned_mps + HNS3_NON_DCB_ADDITIONAL_BUF; + lo_thrd = aligned_mps; + } + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + buf_alloc->s_buf.tc_thrd[i].low = lo_thrd; + buf_alloc->s_buf.tc_thrd[i].high = hi_thrd; + } + + return true; +} + +static bool +hns3_rx_buf_calc_all(struct hns3_hw *hw, bool max, + struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_priv_buf *priv; + uint32_t aligned_mps; + uint32_t rx_all; + uint8_t i; + + rx_all = pf->pkt_buf_size - hns3_get_tx_buff_alloced(buf_alloc); + aligned_mps = roundup(pf->mps, HNS3_BUF_SIZE_UNIT); + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + priv = &buf_alloc->priv_buf[i]; + + priv->enable = 0; + priv->wl.low = 0; + priv->wl.high = 0; + priv->buf_size = 0; + + if (!(hw->hw_tc_map & BIT(i))) + continue; + + priv->enable = 1; + if (hw->dcb_info.hw_pfc_map & BIT(i)) { + priv->wl.low = max ? aligned_mps : HNS3_BUF_SIZE_UNIT; + priv->wl.high = roundup(priv->wl.low + aligned_mps, + HNS3_BUF_SIZE_UNIT); + } else { + priv->wl.low = 0; + priv->wl.high = max ? (aligned_mps * HNS3_BUF_MUL_BY) : + aligned_mps; + } + + priv->buf_size = priv->wl.high + pf->dv_buf_size; + } + + return hns3_is_rx_buf_ok(hw, buf_alloc, rx_all); +} + +static bool +hns3_drop_nopfc_buf_till_fit(struct hns3_hw *hw, + struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_priv_buf *priv; + int no_pfc_priv_num; + uint32_t rx_all; + uint8_t mask; + int i; + + rx_all = pf->pkt_buf_size - hns3_get_tx_buff_alloced(buf_alloc); + no_pfc_priv_num = hns3_get_no_pfc_priv_num(hw, buf_alloc); + + /* let the last to be cleared first */ + for (i = HNS3_MAX_TC_NUM - 1; i >= 0; i--) { + priv = &buf_alloc->priv_buf[i]; + mask = BIT((uint8_t)i); + + if (hw->hw_tc_map & mask && + !(hw->dcb_info.hw_pfc_map & mask)) { + /* Clear the no pfc TC private buffer */ + priv->wl.low = 0; + priv->wl.high = 0; + priv->buf_size = 0; + priv->enable = 0; + no_pfc_priv_num--; + } + + if (hns3_is_rx_buf_ok(hw, buf_alloc, rx_all) || + no_pfc_priv_num == 0) + break; + } + + return hns3_is_rx_buf_ok(hw, buf_alloc, rx_all); +} + +static bool +hns3_drop_pfc_buf_till_fit(struct hns3_hw *hw, + struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_priv_buf *priv; + uint32_t rx_all; + int pfc_priv_num; + uint8_t mask; + int i; + + rx_all = pf->pkt_buf_size - hns3_get_tx_buff_alloced(buf_alloc); + pfc_priv_num = hns3_get_pfc_priv_num(hw, buf_alloc); + + /* let the last to be cleared first */ + for (i = HNS3_MAX_TC_NUM - 1; i >= 0; i--) { + priv = &buf_alloc->priv_buf[i]; + mask = BIT((uint8_t)i); + + if (hw->hw_tc_map & mask && + hw->dcb_info.hw_pfc_map & mask) { + /* Reduce the number of pfc TC with private buffer */ + priv->wl.low = 0; + priv->enable = 0; + priv->wl.high = 0; + priv->buf_size = 0; + pfc_priv_num--; + } + if (hns3_is_rx_buf_ok(hw, buf_alloc, rx_all) || + pfc_priv_num == 0) + break; + } + + return hns3_is_rx_buf_ok(hw, buf_alloc, rx_all); +} + +static bool +hns3_only_alloc_priv_buff(struct hns3_hw *hw, + struct hns3_pkt_buf_alloc *buf_alloc) +{ +#define COMPENSATE_BUFFER 0x3C00 +#define COMPENSATE_HALF_MPS_NUM 5 +#define PRIV_WL_GAP 0x1800 + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint32_t tc_num = hns3_get_tc_num(hw); + uint32_t half_mps = pf->mps >> 1; + struct hns3_priv_buf *priv; + uint32_t min_rx_priv; + uint32_t rx_priv; + uint8_t i; + + rx_priv = pf->pkt_buf_size - hns3_get_tx_buff_alloced(buf_alloc); + if (tc_num) + rx_priv = rx_priv / tc_num; + + if (tc_num <= NEED_RESERVE_TC_NUM) + rx_priv = rx_priv * BUF_RESERVE_PERCENT / BUF_MAX_PERCENT; + + /* + * Minimum value of private buffer in rx direction (min_rx_priv) is + * equal to "DV + 2.5 * MPS + 15KB". Driver only allocates rx private + * buffer if rx_priv is greater than min_rx_priv. + */ + min_rx_priv = pf->dv_buf_size + COMPENSATE_BUFFER + + COMPENSATE_HALF_MPS_NUM * half_mps; + min_rx_priv = roundup(min_rx_priv, HNS3_BUF_SIZE_UNIT); + rx_priv = rounddown(rx_priv, HNS3_BUF_SIZE_UNIT); + + if (rx_priv < min_rx_priv) + return false; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + priv = &buf_alloc->priv_buf[i]; + + priv->enable = 0; + priv->wl.low = 0; + priv->wl.high = 0; + priv->buf_size = 0; + + if (!(hw->hw_tc_map & BIT(i))) + continue; + + priv->enable = 1; + priv->buf_size = rx_priv; + priv->wl.high = rx_priv - pf->dv_buf_size; + priv->wl.low = priv->wl.high - PRIV_WL_GAP; + } + + buf_alloc->s_buf.buf_size = 0; + + return true; +} + +/* + * hns3_rx_buffer_calc: calculate the rx private buffer size for all TCs + * @hw: pointer to struct hns3_hw + * @buf_alloc: pointer to buffer calculation data + * @return: 0: calculate sucessful, negative: fail + */ +static int +hns3_rx_buffer_calc(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ + /* When DCB is not supported, rx private buffer is not allocated. */ + if (!hns3_dev_dcb_supported(hw)) { + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + uint32_t rx_all = pf->pkt_buf_size; + + rx_all -= hns3_get_tx_buff_alloced(buf_alloc); + if (!hns3_is_rx_buf_ok(hw, buf_alloc, rx_all)) + return -ENOMEM; + + return 0; + } + + /* + * Try to allocate privated packet buffer for all TCs without share + * buffer. + */ + if (hns3_only_alloc_priv_buff(hw, buf_alloc)) + return 0; + + /* + * Try to allocate privated packet buffer for all TCs with share + * buffer. + */ + if (hns3_rx_buf_calc_all(hw, true, buf_alloc)) + return 0; + + /* + * For different application scenes, the enabled port number, TC number + * and no_drop TC number are different. In order to obtain the better + * performance, software could allocate the buffer size and configure + * the waterline by tring to decrease the private buffer size according + * to the order, namely, waterline of valided tc, pfc disabled tc, pfc + * enabled tc. + */ + if (hns3_rx_buf_calc_all(hw, false, buf_alloc)) + return 0; + + if (hns3_drop_nopfc_buf_till_fit(hw, buf_alloc)) + return 0; + + if (hns3_drop_pfc_buf_till_fit(hw, buf_alloc)) + return 0; + + return -ENOMEM; +} + +static int +hns3_rx_priv_buf_alloc(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_rx_priv_buff_cmd *req; + struct hns3_cmd_desc desc; + uint32_t buf_size; + int ret; + int i; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RX_PRIV_BUFF_ALLOC, false); + req = (struct hns3_rx_priv_buff_cmd *)desc.data; + + /* Alloc private buffer TCs */ + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + struct hns3_priv_buf *priv = &buf_alloc->priv_buf[i]; + + req->buf_num[i] = + rte_cpu_to_le_16(priv->buf_size >> HNS3_BUF_UNIT_S); + req->buf_num[i] |= rte_cpu_to_le_16(1 << HNS3_TC0_PRI_BUF_EN_B); + } + + buf_size = buf_alloc->s_buf.buf_size; + req->shared_buf = rte_cpu_to_le_16((buf_size >> HNS3_BUF_UNIT_S) | + (1 << HNS3_TC0_PRI_BUF_EN_B)); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "rx private buffer alloc cmd failed %d", ret); + + return ret; +} + +static int +hns3_rx_priv_wl_config(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ +#define HNS3_RX_PRIV_WL_ALLOC_DESC_NUM 2 + struct hns3_rx_priv_wl_buf *req; + struct hns3_priv_buf *priv; + struct hns3_cmd_desc desc[HNS3_RX_PRIV_WL_ALLOC_DESC_NUM]; + int i, j; + int ret; + + for (i = 0; i < HNS3_RX_PRIV_WL_ALLOC_DESC_NUM; i++) { + hns3_cmd_setup_basic_desc(&desc[i], HNS3_OPC_RX_PRIV_WL_ALLOC, + false); + req = (struct hns3_rx_priv_wl_buf *)desc[i].data; + + /* The first descriptor set the NEXT bit to 1 */ + if (i == 0) + desc[i].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + else + desc[i].flag &= ~rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + + for (j = 0; j < HNS3_TC_NUM_ONE_DESC; j++) { + uint32_t idx = i * HNS3_TC_NUM_ONE_DESC + j; + + priv = &buf_alloc->priv_buf[idx]; + req->tc_wl[j].high = rte_cpu_to_le_16(priv->wl.high >> + HNS3_BUF_UNIT_S); + req->tc_wl[j].high |= + rte_cpu_to_le_16(BIT(HNS3_RX_PRIV_EN_B)); + req->tc_wl[j].low = rte_cpu_to_le_16(priv->wl.low >> + HNS3_BUF_UNIT_S); + req->tc_wl[j].low |= + rte_cpu_to_le_16(BIT(HNS3_RX_PRIV_EN_B)); + } + } + + /* Send 2 descriptor at one time */ + ret = hns3_cmd_send(hw, desc, HNS3_RX_PRIV_WL_ALLOC_DESC_NUM); + if (ret) + PMD_INIT_LOG(ERR, "rx private waterline config cmd failed %d", + ret); + return ret; +} + +static int +hns3_common_thrd_config(struct hns3_hw *hw, + struct hns3_pkt_buf_alloc *buf_alloc) +{ +#define HNS3_RX_COM_THRD_ALLOC_DESC_NUM 2 + struct hns3_shared_buf *s_buf = &buf_alloc->s_buf; + struct hns3_rx_com_thrd *req; + struct hns3_cmd_desc desc[HNS3_RX_COM_THRD_ALLOC_DESC_NUM]; + struct hns3_tc_thrd *tc; + int tc_idx; + int i, j; + int ret; + + for (i = 0; i < HNS3_RX_COM_THRD_ALLOC_DESC_NUM; i++) { + hns3_cmd_setup_basic_desc(&desc[i], HNS3_OPC_RX_COM_THRD_ALLOC, + false); + req = (struct hns3_rx_com_thrd *)&desc[i].data; + + /* The first descriptor set the NEXT bit to 1 */ + if (i == 0) + desc[i].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + else + desc[i].flag &= ~rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + + for (j = 0; j < HNS3_TC_NUM_ONE_DESC; j++) { + tc_idx = i * HNS3_TC_NUM_ONE_DESC + j; + tc = &s_buf->tc_thrd[tc_idx]; + + req->com_thrd[j].high = + rte_cpu_to_le_16(tc->high >> HNS3_BUF_UNIT_S); + req->com_thrd[j].high |= + rte_cpu_to_le_16(BIT(HNS3_RX_PRIV_EN_B)); + req->com_thrd[j].low = + rte_cpu_to_le_16(tc->low >> HNS3_BUF_UNIT_S); + req->com_thrd[j].low |= + rte_cpu_to_le_16(BIT(HNS3_RX_PRIV_EN_B)); + } + } + + /* Send 2 descriptors at one time */ + ret = hns3_cmd_send(hw, desc, HNS3_RX_COM_THRD_ALLOC_DESC_NUM); + if (ret) + PMD_INIT_LOG(ERR, "common threshold config cmd failed %d", ret); + + return ret; +} + +static int +hns3_common_wl_config(struct hns3_hw *hw, struct hns3_pkt_buf_alloc *buf_alloc) +{ + struct hns3_shared_buf *buf = &buf_alloc->s_buf; + struct hns3_rx_com_wl *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RX_COM_WL_ALLOC, false); + + req = (struct hns3_rx_com_wl *)desc.data; + req->com_wl.high = rte_cpu_to_le_16(buf->self.high >> HNS3_BUF_UNIT_S); + req->com_wl.high |= rte_cpu_to_le_16(BIT(HNS3_RX_PRIV_EN_B)); + + req->com_wl.low = rte_cpu_to_le_16(buf->self.low >> HNS3_BUF_UNIT_S); + req->com_wl.low |= rte_cpu_to_le_16(BIT(HNS3_RX_PRIV_EN_B)); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "common waterline config cmd failed %d", ret); + + return ret; +} + +int +hns3_buffer_alloc(struct hns3_hw *hw) +{ + struct hns3_pkt_buf_alloc pkt_buf; + int ret; + + memset(&pkt_buf, 0, sizeof(pkt_buf)); + ret = hns3_tx_buffer_calc(hw, &pkt_buf); + if (ret) { + PMD_INIT_LOG(ERR, + "could not calc tx buffer size for all TCs %d", + ret); + return ret; + } + + ret = hns3_tx_buffer_alloc(hw, &pkt_buf); + if (ret) { + PMD_INIT_LOG(ERR, "could not alloc tx buffers %d", ret); + return ret; + } + + ret = hns3_rx_buffer_calc(hw, &pkt_buf); + if (ret) { + PMD_INIT_LOG(ERR, + "could not calc rx priv buffer size for all TCs %d", + ret); + return ret; + } + + ret = hns3_rx_priv_buf_alloc(hw, &pkt_buf); + if (ret) { + PMD_INIT_LOG(ERR, "could not alloc rx priv buffer %d", ret); + return ret; + } + + if (hns3_dev_dcb_supported(hw)) { + ret = hns3_rx_priv_wl_config(hw, &pkt_buf); + if (ret) { + PMD_INIT_LOG(ERR, + "could not configure rx private waterline %d", + ret); + return ret; + } + + ret = hns3_common_thrd_config(hw, &pkt_buf); + if (ret) { + PMD_INIT_LOG(ERR, + "could not configure common threshold %d", + ret); + return ret; + } + } + + ret = hns3_common_wl_config(hw, &pkt_buf); + if (ret) + PMD_INIT_LOG(ERR, "could not configure common waterline %d", + ret); + + return ret; +} + +static int +hns3_mac_init(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_mac *mac = &hw->mac; + struct hns3_pf *pf = &hns->pf; + int ret; + + pf->support_sfp_query = true; + mac->link_duplex = ETH_LINK_FULL_DUPLEX; + ret = hns3_cfg_mac_speed_dup_hw(hw, mac->link_speed, mac->link_duplex); + if (ret) { + PMD_INIT_LOG(ERR, "Config mac speed dup fail ret = %d", ret); + return ret; + } + + mac->link_status = ETH_LINK_DOWN; + + return hns3_config_mtu(hw, pf->mps); +} + +static int +hns3_get_mac_ethertype_cmd_status(uint16_t cmdq_resp, uint8_t resp_code) +{ +#define HNS3_ETHERTYPE_SUCCESS_ADD 0 +#define HNS3_ETHERTYPE_ALREADY_ADD 1 +#define HNS3_ETHERTYPE_MGR_TBL_OVERFLOW 2 +#define HNS3_ETHERTYPE_KEY_CONFLICT 3 + int return_status; + + if (cmdq_resp) { + PMD_INIT_LOG(ERR, + "cmdq execute failed for get_mac_ethertype_cmd_status, status=%d.\n", + cmdq_resp); + return -EIO; + } + + switch (resp_code) { + case HNS3_ETHERTYPE_SUCCESS_ADD: + case HNS3_ETHERTYPE_ALREADY_ADD: + return_status = 0; + break; + case HNS3_ETHERTYPE_MGR_TBL_OVERFLOW: + PMD_INIT_LOG(ERR, + "add mac ethertype failed for manager table overflow."); + return_status = -EIO; + break; + case HNS3_ETHERTYPE_KEY_CONFLICT: + PMD_INIT_LOG(ERR, "add mac ethertype failed for key conflict."); + return_status = -EIO; + break; + default: + PMD_INIT_LOG(ERR, + "add mac ethertype failed for undefined, code=%d.", + resp_code); + return_status = -EIO; + break; + } + + return return_status; +} + +static int +hns3_add_mgr_tbl(struct hns3_hw *hw, + const struct hns3_mac_mgr_tbl_entry_cmd *req) +{ + struct hns3_cmd_desc desc; + uint8_t resp_code; + uint16_t retval; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MAC_ETHTYPE_ADD, false); + memcpy(desc.data, req, sizeof(struct hns3_mac_mgr_tbl_entry_cmd)); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + PMD_INIT_LOG(ERR, + "add mac ethertype failed for cmd_send, ret =%d.", + ret); + return ret; + } + + resp_code = (rte_le_to_cpu_32(desc.data[0]) >> 8) & 0xff; + retval = rte_le_to_cpu_16(desc.retval); + + return hns3_get_mac_ethertype_cmd_status(retval, resp_code); +} + +static void +hns3_prepare_mgr_tbl(struct hns3_mac_mgr_tbl_entry_cmd *mgr_table, + int *table_item_num) +{ + struct hns3_mac_mgr_tbl_entry_cmd *tbl; + + /* + * In current version, we add one item in management table as below: + * 0x0180C200000E -- LLDP MC address + */ + tbl = mgr_table; + tbl->flags = HNS3_MAC_MGR_MASK_VLAN_B; + tbl->ethter_type = rte_cpu_to_le_16(HNS3_MAC_ETHERTYPE_LLDP); + tbl->mac_addr_hi32 = rte_cpu_to_le_32(htonl(0x0180C200)); + tbl->mac_addr_lo16 = rte_cpu_to_le_16(htons(0x000E)); + tbl->i_port_bitmap = 0x1; + *table_item_num = 1; +} + +static int +hns3_init_mgr_tbl(struct hns3_hw *hw) +{ +#define HNS_MAC_MGR_TBL_MAX_SIZE 16 + struct hns3_mac_mgr_tbl_entry_cmd mgr_table[HNS_MAC_MGR_TBL_MAX_SIZE]; + int table_item_num; + int ret; + int i; + + memset(mgr_table, 0, sizeof(mgr_table)); + hns3_prepare_mgr_tbl(mgr_table, &table_item_num); + for (i = 0; i < table_item_num; i++) { + ret = hns3_add_mgr_tbl(hw, &mgr_table[i]); + if (ret) { + PMD_INIT_LOG(ERR, "add mac ethertype failed, ret =%d", + ret); + return ret; + } + } + + return 0; +} + +static void +hns3_promisc_param_init(struct hns3_promisc_param *param, bool en_uc, + bool en_mc, bool en_bc, int vport_id) +{ + if (!param) + return; + + memset(param, 0, sizeof(struct hns3_promisc_param)); + if (en_uc) + param->enable = HNS3_PROMISC_EN_UC; + if (en_mc) + param->enable |= HNS3_PROMISC_EN_MC; + if (en_bc) + param->enable |= HNS3_PROMISC_EN_BC; + param->vf_id = vport_id; +} + +static int +hns3_cmd_set_promisc_mode(struct hns3_hw *hw, struct hns3_promisc_param *param) +{ + struct hns3_promisc_cfg_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_PROMISC_MODE, false); + + req = (struct hns3_promisc_cfg_cmd *)desc.data; + req->vf_id = param->vf_id; + req->flag = (param->enable << HNS3_PROMISC_EN_B) | + HNS3_PROMISC_TX_EN_B | HNS3_PROMISC_RX_EN_B; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "Set promisc mode fail, ret = %d", ret); + + return ret; +} + +static int +hns3_set_promisc_mode(struct hns3_hw *hw, bool en_uc_pmc, bool en_mc_pmc) +{ + struct hns3_promisc_param param; + bool en_bc_pmc = true; + uint8_t vf_id; + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, the PF-related vf_id is 0, just need to configure parameters + * for vf_id 0. + */ + vf_id = 0; + + hns3_promisc_param_init(¶m, en_uc_pmc, en_mc_pmc, en_bc_pmc, vf_id); + return hns3_cmd_set_promisc_mode(hw, ¶m); +} + +static int +hns3_clear_all_vfs_promisc_mode(struct hns3_hw *hw) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_pf *pf = &hns->pf; + struct hns3_promisc_param param; + uint16_t func_id; + int ret; + + /* func_id 0 is denoted PF, the VFs start from 1 */ + for (func_id = 1; func_id < pf->func_num; func_id++) { + hns3_promisc_param_init(¶m, false, false, false, func_id); + ret = hns3_cmd_set_promisc_mode(hw, ¶m); + if (ret) + return ret; + } + + return 0; +} + +static int +hns3_dev_promiscuous_enable(struct rte_eth_dev *dev) +{ + bool allmulti = dev->data->all_multicast ? true : false; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint64_t offloads; + int err; + int ret; + + rte_spinlock_lock(&hw->lock); + ret = hns3_set_promisc_mode(hw, true, true); + if (ret) { + rte_spinlock_unlock(&hw->lock); + hns3_err(hw, "failed to enable promiscuous mode, ret = %d", + ret); + return ret; + } + + /* + * When promiscuous mode was enabled, disable the vlan filter to let + * all packets coming in in the receiving direction. + */ + offloads = dev->data->dev_conf.rxmode.offloads; + if (offloads & DEV_RX_OFFLOAD_VLAN_FILTER) { + ret = hns3_enable_vlan_filter(hns, false); + if (ret) { + hns3_err(hw, "failed to enable promiscuous mode due to " + "failure to disable vlan filter, ret = %d", + ret); + err = hns3_set_promisc_mode(hw, false, allmulti); + if (err) + hns3_err(hw, "failed to restore promiscuous " + "status after disable vlan filter " + "failed during enabling promiscuous " + "mode, ret = %d", ret); + } + } + + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_dev_promiscuous_disable(struct rte_eth_dev *dev) +{ + bool allmulti = dev->data->all_multicast ? true : false; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint64_t offloads; + int err; + int ret; + + /* If now in all_multicast mode, must remain in all_multicast mode. */ + rte_spinlock_lock(&hw->lock); + ret = hns3_set_promisc_mode(hw, false, allmulti); + if (ret) { + rte_spinlock_unlock(&hw->lock); + hns3_err(hw, "failed to disable promiscuous mode, ret = %d", + ret); + return ret; + } + /* when promiscuous mode was disabled, restore the vlan filter status */ + offloads = dev->data->dev_conf.rxmode.offloads; + if (offloads & DEV_RX_OFFLOAD_VLAN_FILTER) { + ret = hns3_enable_vlan_filter(hns, true); + if (ret) { + hns3_err(hw, "failed to disable promiscuous mode due to" + " failure to restore vlan filter, ret = %d", + ret); + err = hns3_set_promisc_mode(hw, true, true); + if (err) + hns3_err(hw, "failed to restore promiscuous " + "status after enabling vlan filter " + "failed during disabling promiscuous " + "mode, ret = %d", ret); + } + } + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_dev_allmulticast_enable(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (dev->data->promiscuous) + return 0; + + rte_spinlock_lock(&hw->lock); + ret = hns3_set_promisc_mode(hw, false, true); + rte_spinlock_unlock(&hw->lock); + if (ret) + hns3_err(hw, "failed to enable allmulticast mode, ret = %d", + ret); + + return ret; +} + +static int +hns3_dev_allmulticast_disable(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + /* If now in promiscuous mode, must remain in all_multicast mode. */ + if (dev->data->promiscuous) + return 0; + + rte_spinlock_lock(&hw->lock); + ret = hns3_set_promisc_mode(hw, false, false); + rte_spinlock_unlock(&hw->lock); + if (ret) + hns3_err(hw, "failed to disable allmulticast mode, ret = %d", + ret); + + return ret; +} + +static int +hns3_dev_promisc_restore(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + bool allmulti = hw->data->all_multicast ? true : false; + int ret; + + if (hw->data->promiscuous) { + ret = hns3_set_promisc_mode(hw, true, true); + if (ret) + hns3_err(hw, "failed to restore promiscuous mode, " + "ret = %d", ret); + return ret; + } + + ret = hns3_set_promisc_mode(hw, false, allmulti); + if (ret) + hns3_err(hw, "failed to restore allmulticast mode, ret = %d", + ret); + return ret; +} + +static int +hns3_get_sfp_speed(struct hns3_hw *hw, uint32_t *speed) +{ + struct hns3_sfp_speed_cmd *resp; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_SFP_GET_SPEED, true); + resp = (struct hns3_sfp_speed_cmd *)desc.data; + ret = hns3_cmd_send(hw, &desc, 1); + if (ret == -EOPNOTSUPP) { + hns3_err(hw, "IMP do not support get SFP speed %d", ret); + return ret; + } else if (ret) { + hns3_err(hw, "get sfp speed failed %d", ret); + return ret; + } + + *speed = resp->sfp_speed; + + return 0; +} + +static uint8_t +hns3_check_speed_dup(uint8_t duplex, uint32_t speed) +{ + if (!(speed == ETH_SPEED_NUM_10M || speed == ETH_SPEED_NUM_100M)) + duplex = ETH_LINK_FULL_DUPLEX; + + return duplex; +} + +static int +hns3_cfg_mac_speed_dup(struct hns3_hw *hw, uint32_t speed, uint8_t duplex) +{ + struct hns3_mac *mac = &hw->mac; + int ret; + + duplex = hns3_check_speed_dup(duplex, speed); + if (mac->link_speed == speed && mac->link_duplex == duplex) + return 0; + + ret = hns3_cfg_mac_speed_dup_hw(hw, speed, duplex); + if (ret) + return ret; + + mac->link_speed = speed; + mac->link_duplex = duplex; + + return 0; +} + +static int +hns3_update_speed_duplex(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_pf *pf = &hns->pf; + uint32_t speed; + int ret; + + /* If IMP do not support get SFP/qSFP speed, return directly */ + if (!pf->support_sfp_query) + return 0; + + ret = hns3_get_sfp_speed(hw, &speed); + if (ret == -EOPNOTSUPP) { + pf->support_sfp_query = false; + return ret; + } else if (ret) + return ret; + + if (speed == ETH_SPEED_NUM_NONE) + return 0; /* do nothing if no SFP */ + + /* Config full duplex for SFP */ + return hns3_cfg_mac_speed_dup(hw, speed, ETH_LINK_FULL_DUPLEX); +} + +static int +hns3_cfg_mac_mode(struct hns3_hw *hw, bool enable) +{ + struct hns3_config_mac_mode_cmd *req; + struct hns3_cmd_desc desc; + uint32_t loop_en = 0; + uint8_t val = 0; + int ret; + + req = (struct hns3_config_mac_mode_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CONFIG_MAC_MODE, false); + if (enable) + val = 1; + hns3_set_bit(loop_en, HNS3_MAC_TX_EN_B, val); + hns3_set_bit(loop_en, HNS3_MAC_RX_EN_B, val); + hns3_set_bit(loop_en, HNS3_MAC_PAD_TX_B, val); + hns3_set_bit(loop_en, HNS3_MAC_PAD_RX_B, val); + hns3_set_bit(loop_en, HNS3_MAC_1588_TX_B, 0); + hns3_set_bit(loop_en, HNS3_MAC_1588_RX_B, 0); + hns3_set_bit(loop_en, HNS3_MAC_APP_LP_B, 0); + hns3_set_bit(loop_en, HNS3_MAC_LINE_LP_B, 0); + hns3_set_bit(loop_en, HNS3_MAC_FCS_TX_B, val); + hns3_set_bit(loop_en, HNS3_MAC_RX_FCS_B, val); + hns3_set_bit(loop_en, HNS3_MAC_RX_FCS_STRIP_B, val); + hns3_set_bit(loop_en, HNS3_MAC_TX_OVERSIZE_TRUNCATE_B, val); + hns3_set_bit(loop_en, HNS3_MAC_RX_OVERSIZE_TRUNCATE_B, val); + hns3_set_bit(loop_en, HNS3_MAC_TX_UNDER_MIN_ERR_B, val); + req->txrx_pad_fcs_loop_en = rte_cpu_to_le_32(loop_en); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + PMD_INIT_LOG(ERR, "mac enable fail, ret =%d.", ret); + + return ret; +} + +static int +hns3_get_mac_link_status(struct hns3_hw *hw) +{ + struct hns3_link_status_cmd *req; + struct hns3_cmd_desc desc; + int link_status; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_LINK_STATUS, true); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "get link status cmd failed %d", ret); + return ETH_LINK_DOWN; + } + + req = (struct hns3_link_status_cmd *)desc.data; + link_status = req->status & HNS3_LINK_STATUS_UP_M; + + return !!link_status; +} + +void +hns3_update_link_status(struct hns3_hw *hw) +{ + int state; + + state = hns3_get_mac_link_status(hw); + if (state != hw->mac.link_status) { + hw->mac.link_status = state; + hns3_warn(hw, "Link status change to %s!", state ? "up" : "down"); + } +} + +static void +hns3_service_handler(void *param) +{ + struct rte_eth_dev *eth_dev = (struct rte_eth_dev *)param; + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + if (!hns3_is_reset_pending(hns)) { + hns3_update_speed_duplex(eth_dev); + hns3_update_link_status(hw); + } else + hns3_warn(hw, "Cancel the query when reset is pending"); + + rte_eal_alarm_set(HNS3_SERVICE_INTERVAL, hns3_service_handler, eth_dev); +} + +static int +hns3_init_hardware(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_map_tqp(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to map tqp: %d", ret); + return ret; + } + + ret = hns3_init_umv_space(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init umv space: %d", ret); + return ret; + } + + ret = hns3_mac_init(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init MAC: %d", ret); + goto err_mac_init; + } + + ret = hns3_init_mgr_tbl(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init manager table: %d", ret); + goto err_mac_init; + } + + ret = hns3_set_promisc_mode(hw, false, false); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to set promisc mode: %d", ret); + goto err_mac_init; + } + + ret = hns3_clear_all_vfs_promisc_mode(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to clear all vfs promisc mode: %d", + ret); + goto err_mac_init; + } + + ret = hns3_init_vlan_config(hns); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init vlan: %d", ret); + goto err_mac_init; + } + + ret = hns3_dcb_init(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init dcb: %d", ret); + goto err_mac_init; + } + + ret = hns3_init_fd_config(hns); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init flow director: %d", ret); + goto err_mac_init; + } + + ret = hns3_config_tso(hw, HNS3_TSO_MSS_MIN, HNS3_TSO_MSS_MAX); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to config tso: %d", ret); + goto err_mac_init; + } + + ret = hns3_config_gro(hw, false); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to config gro: %d", ret); + goto err_mac_init; + } + + /* + * In the initialization clearing the all hardware mapping relationship + * configurations between queues and interrupt vectors is needed, so + * some error caused by the residual configurations, such as the + * unexpected interrupt, can be avoid. + */ + ret = hns3_init_ring_with_vector(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init ring intr vector: %d", ret); + goto err_mac_init; + } + + return 0; + +err_mac_init: + hns3_uninit_umv_space(hw); + return ret; +} + +static int +hns3_init_pf(struct rte_eth_dev *eth_dev) +{ + struct rte_device *dev = eth_dev->device; + struct rte_pci_device *pci_dev = RTE_DEV_TO_PCI(dev); + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + PMD_INIT_FUNC_TRACE(); + + /* Get hardware io base address from pcie BAR2 IO space */ + hw->io_base = pci_dev->mem_resource[2].addr; + + /* Firmware command queue initialize */ + ret = hns3_cmd_init_queue(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init cmd queue: %d", ret); + goto err_cmd_init_queue; + } + + hns3_clear_all_event_cause(hw); + + /* Firmware command initialize */ + ret = hns3_cmd_init(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init cmd: %d", ret); + goto err_cmd_init; + } + + ret = rte_intr_callback_register(&pci_dev->intr_handle, + hns3_interrupt_handler, + eth_dev); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to register intr: %d", ret); + goto err_intr_callback_register; + } + + /* Enable interrupt */ + rte_intr_enable(&pci_dev->intr_handle); + hns3_pf_enable_irq0(hw); + + /* Get configuration */ + ret = hns3_get_configuration(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to fetch configuration: %d", ret); + goto err_get_config; + } + + ret = hns3_init_hardware(hns); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init hardware: %d", ret); + goto err_get_config; + } + + /* Initialize flow director filter list & hash */ + ret = hns3_fdir_filter_init(hns); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to alloc hashmap for fdir: %d", ret); + goto err_hw_init; + } + + hns3_set_default_rss_args(hw); + + ret = hns3_enable_hw_error_intr(hns, true); + if (ret) { + PMD_INIT_LOG(ERR, "fail to enable hw error interrupts: %d", + ret); + goto err_fdir; + } + + return 0; + +err_fdir: + hns3_fdir_filter_uninit(hns); +err_hw_init: + hns3_uninit_umv_space(hw); + +err_get_config: + hns3_pf_disable_irq0(hw); + rte_intr_disable(&pci_dev->intr_handle); + hns3_intr_unregister(&pci_dev->intr_handle, hns3_interrupt_handler, + eth_dev); +err_intr_callback_register: +err_cmd_init: + hns3_cmd_uninit(hw); + hns3_cmd_destroy_queue(hw); +err_cmd_init_queue: + hw->io_base = NULL; + + return ret; +} + +static void +hns3_uninit_pf(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct rte_device *dev = eth_dev->device; + struct rte_pci_device *pci_dev = RTE_DEV_TO_PCI(dev); + struct hns3_hw *hw = &hns->hw; + + PMD_INIT_FUNC_TRACE(); + + hns3_enable_hw_error_intr(hns, false); + hns3_rss_uninit(hns); + hns3_fdir_filter_uninit(hns); + hns3_uninit_umv_space(hw); + hns3_pf_disable_irq0(hw); + rte_intr_disable(&pci_dev->intr_handle); + hns3_intr_unregister(&pci_dev->intr_handle, hns3_interrupt_handler, + eth_dev); + hns3_cmd_uninit(hw); + hns3_cmd_destroy_queue(hw); + hw->io_base = NULL; +} + +static int +hns3_do_start(struct hns3_adapter *hns, bool reset_queue) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_dcb_cfg_update(hns); + if (ret) + return ret; + + /* Enable queues */ + ret = hns3_start_queues(hns, reset_queue); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to start queues: %d", ret); + return ret; + } + + /* Enable MAC */ + ret = hns3_cfg_mac_mode(hw, true); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to enable MAC: %d", ret); + goto err_config_mac_mode; + } + return 0; + +err_config_mac_mode: + hns3_stop_queues(hns, true); + return ret; +} + +static int +hns3_map_rx_interrupt(struct rte_eth_dev *dev) +{ + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + uint8_t base = RTE_INTR_VEC_ZERO_OFFSET; + uint8_t vec = RTE_INTR_VEC_ZERO_OFFSET; + uint32_t intr_vector; + uint16_t q_id; + int ret; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return 0; + + /* disable uio/vfio intr/eventfd mapping */ + rte_intr_disable(intr_handle); + + /* check and configure queue intr-vector mapping */ + if (rte_intr_cap_multiple(intr_handle) || + !RTE_ETH_DEV_SRIOV(dev).active) { + intr_vector = hw->used_rx_queues; + /* creates event fd for each intr vector when MSIX is used */ + if (rte_intr_efd_enable(intr_handle, intr_vector)) + return -EINVAL; + } + if (rte_intr_dp_is_en(intr_handle) && !intr_handle->intr_vec) { + intr_handle->intr_vec = + rte_zmalloc("intr_vec", + hw->used_rx_queues * sizeof(int), 0); + if (intr_handle->intr_vec == NULL) { + hns3_err(hw, "Failed to allocate %d rx_queues" + " intr_vec", hw->used_rx_queues); + ret = -ENOMEM; + goto alloc_intr_vec_error; + } + } + + if (rte_intr_allow_others(intr_handle)) { + vec = RTE_INTR_VEC_RXTX_OFFSET; + base = RTE_INTR_VEC_RXTX_OFFSET; + } + if (rte_intr_dp_is_en(intr_handle)) { + for (q_id = 0; q_id < hw->used_rx_queues; q_id++) { + ret = hns3_bind_ring_with_vector(hw, vec, true, + HNS3_RING_TYPE_RX, + q_id); + if (ret) + goto bind_vector_error; + intr_handle->intr_vec[q_id] = vec; + if (vec < base + intr_handle->nb_efd - 1) + vec++; + } + } + rte_intr_enable(intr_handle); + return 0; + +bind_vector_error: + rte_intr_efd_disable(intr_handle); + if (intr_handle->intr_vec) { + free(intr_handle->intr_vec); + intr_handle->intr_vec = NULL; + } + return ret; +alloc_intr_vec_error: + rte_intr_efd_disable(intr_handle); + return ret; +} + +static int +hns3_restore_rx_interrupt(struct hns3_hw *hw) +{ + struct rte_eth_dev *dev = &rte_eth_devices[hw->data->port_id]; + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + uint16_t q_id; + int ret; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return 0; + + if (rte_intr_dp_is_en(intr_handle)) { + for (q_id = 0; q_id < hw->used_rx_queues; q_id++) { + ret = hns3_bind_ring_with_vector(hw, + intr_handle->intr_vec[q_id], true, + HNS3_RING_TYPE_RX, q_id); + if (ret) + return ret; + } + } + + return 0; +} + +static void +hns3_restore_filter(struct rte_eth_dev *dev) +{ + hns3_restore_rss_filter(dev); +} + +static int +hns3_dev_start(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + PMD_INIT_FUNC_TRACE(); + if (rte_atomic16_read(&hw->reset.resetting)) + return -EBUSY; + + rte_spinlock_lock(&hw->lock); + hw->adapter_state = HNS3_NIC_STARTING; + + ret = hns3_do_start(hns, true); + if (ret) { + hw->adapter_state = HNS3_NIC_CONFIGURED; + rte_spinlock_unlock(&hw->lock); + return ret; + } + ret = hns3_map_rx_interrupt(dev); + if (ret) { + hw->adapter_state = HNS3_NIC_CONFIGURED; + rte_spinlock_unlock(&hw->lock); + return ret; + } + + hw->adapter_state = HNS3_NIC_STARTED; + rte_spinlock_unlock(&hw->lock); + + hns3_set_rxtx_function(dev); + hns3_mp_req_start_rxtx(dev); + rte_eal_alarm_set(HNS3_SERVICE_INTERVAL, hns3_service_handler, dev); + + hns3_restore_filter(dev); + + /* Enable interrupt of all rx queues before enabling queues */ + hns3_dev_all_rx_queue_intr_enable(hw, true); + /* + * When finished the initialization, enable queues to receive/transmit + * packets. + */ + hns3_enable_all_queues(hw, true); + + hns3_info(hw, "hns3 dev start successful!"); + return 0; +} + +static int +hns3_do_stop(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + bool reset_queue; + int ret; + + ret = hns3_cfg_mac_mode(hw, false); + if (ret) + return ret; + hw->mac.link_status = ETH_LINK_DOWN; + + if (rte_atomic16_read(&hw->reset.disable_cmd) == 0) { + hns3_configure_all_mac_addr(hns, true); + reset_queue = true; + } else + reset_queue = false; + hw->mac.default_addr_setted = false; + return hns3_stop_queues(hns, reset_queue); +} + +static void +hns3_unmap_rx_interrupt(struct rte_eth_dev *dev) +{ + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint8_t base = RTE_INTR_VEC_ZERO_OFFSET; + uint8_t vec = RTE_INTR_VEC_ZERO_OFFSET; + uint16_t q_id; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return; + + /* unmap the ring with vector */ + if (rte_intr_allow_others(intr_handle)) { + vec = RTE_INTR_VEC_RXTX_OFFSET; + base = RTE_INTR_VEC_RXTX_OFFSET; + } + if (rte_intr_dp_is_en(intr_handle)) { + for (q_id = 0; q_id < hw->used_rx_queues; q_id++) { + (void)hns3_bind_ring_with_vector(hw, vec, false, + HNS3_RING_TYPE_RX, + q_id); + if (vec < base + intr_handle->nb_efd - 1) + vec++; + } + } + /* Clean datapath event and queue/vec mapping */ + rte_intr_efd_disable(intr_handle); + if (intr_handle->intr_vec) { + rte_free(intr_handle->intr_vec); + intr_handle->intr_vec = NULL; + } +} + +static void +hns3_dev_stop(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + PMD_INIT_FUNC_TRACE(); + + hw->adapter_state = HNS3_NIC_STOPPING; + hns3_set_rxtx_function(dev); + rte_wmb(); + /* Disable datapath on secondary process. */ + hns3_mp_req_stop_rxtx(dev); + /* Prevent crashes when queues are still in use. */ + rte_delay_ms(hw->tqps_num); + + rte_spinlock_lock(&hw->lock); + if (rte_atomic16_read(&hw->reset.resetting) == 0) { + hns3_do_stop(hns); + hns3_unmap_rx_interrupt(dev); + hns3_dev_release_mbufs(hns); + hw->adapter_state = HNS3_NIC_CONFIGURED; + } + rte_eal_alarm_cancel(hns3_service_handler, dev); + rte_spinlock_unlock(&hw->lock); +} + +static void +hns3_dev_close(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { + rte_free(eth_dev->process_private); + eth_dev->process_private = NULL; + return; + } + + if (hw->adapter_state == HNS3_NIC_STARTED) + hns3_dev_stop(eth_dev); + + hw->adapter_state = HNS3_NIC_CLOSING; + hns3_reset_abort(hns); + hw->adapter_state = HNS3_NIC_CLOSED; + + hns3_configure_all_mc_mac_addr(hns, true); + hns3_remove_all_vlan_table(hns); + hns3_vlan_txvlan_cfg(hns, HNS3_PORT_BASE_VLAN_DISABLE, 0); + hns3_uninit_pf(eth_dev); + hns3_free_all_queues(eth_dev); + rte_free(hw->reset.wait_data); + rte_free(eth_dev->process_private); + eth_dev->process_private = NULL; + hns3_mp_uninit_primary(); + hns3_warn(hw, "Close port %d finished", hw->data->port_id); +} + +static int +hns3_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + + fc_conf->pause_time = pf->pause_time; + + /* return fc current mode */ + switch (hw->current_mode) { + case HNS3_FC_FULL: + fc_conf->mode = RTE_FC_FULL; + break; + case HNS3_FC_TX_PAUSE: + fc_conf->mode = RTE_FC_TX_PAUSE; + break; + case HNS3_FC_RX_PAUSE: + fc_conf->mode = RTE_FC_RX_PAUSE; + break; + case HNS3_FC_NONE: + default: + fc_conf->mode = RTE_FC_NONE; + break; + } + + return 0; +} + +static void +hns3_get_fc_mode(struct hns3_hw *hw, enum rte_eth_fc_mode mode) +{ + switch (mode) { + case RTE_FC_NONE: + hw->requested_mode = HNS3_FC_NONE; + break; + case RTE_FC_RX_PAUSE: + hw->requested_mode = HNS3_FC_RX_PAUSE; + break; + case RTE_FC_TX_PAUSE: + hw->requested_mode = HNS3_FC_TX_PAUSE; + break; + case RTE_FC_FULL: + hw->requested_mode = HNS3_FC_FULL; + break; + default: + hw->requested_mode = HNS3_FC_NONE; + hns3_warn(hw, "fc_mode(%u) exceeds member scope and is " + "configured to RTE_FC_NONE", mode); + break; + } +} + +static int +hns3_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + int ret; + + if (fc_conf->high_water || fc_conf->low_water || + fc_conf->send_xon || fc_conf->mac_ctrl_frame_fwd) { + hns3_err(hw, "Unsupported flow control settings specified, " + "high_water(%u), low_water(%u), send_xon(%u) and " + "mac_ctrl_frame_fwd(%u) must be set to '0'", + fc_conf->high_water, fc_conf->low_water, + fc_conf->send_xon, fc_conf->mac_ctrl_frame_fwd); + return -EINVAL; + } + if (fc_conf->autoneg) { + hns3_err(hw, "Unsupported fc auto-negotiation setting."); + return -EINVAL; + } + if (!fc_conf->pause_time) { + hns3_err(hw, "Invalid pause time %d setting.", + fc_conf->pause_time); + return -EINVAL; + } + + if (!(hw->current_fc_status == HNS3_FC_STATUS_NONE || + hw->current_fc_status == HNS3_FC_STATUS_MAC_PAUSE)) { + hns3_err(hw, "PFC is enabled. Cannot set MAC pause. " + "current_fc_status = %d", hw->current_fc_status); + return -EOPNOTSUPP; + } + + hns3_get_fc_mode(hw, fc_conf->mode); + if (hw->requested_mode == hw->current_mode && + pf->pause_time == fc_conf->pause_time) + return 0; + + rte_spinlock_lock(&hw->lock); + ret = hns3_fc_enable(dev, fc_conf); + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_priority_flow_ctrl_set(struct rte_eth_dev *dev, + struct rte_eth_pfc_conf *pfc_conf) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + uint8_t priority; + int ret; + + if (!hns3_dev_dcb_supported(hw)) { + hns3_err(hw, "This port does not support dcb configurations."); + return -EOPNOTSUPP; + } + + if (pfc_conf->fc.high_water || pfc_conf->fc.low_water || + pfc_conf->fc.send_xon || pfc_conf->fc.mac_ctrl_frame_fwd) { + hns3_err(hw, "Unsupported flow control settings specified, " + "high_water(%u), low_water(%u), send_xon(%u) and " + "mac_ctrl_frame_fwd(%u) must be set to '0'", + pfc_conf->fc.high_water, pfc_conf->fc.low_water, + pfc_conf->fc.send_xon, + pfc_conf->fc.mac_ctrl_frame_fwd); + return -EINVAL; + } + if (pfc_conf->fc.autoneg) { + hns3_err(hw, "Unsupported fc auto-negotiation setting."); + return -EINVAL; + } + if (pfc_conf->fc.pause_time == 0) { + hns3_err(hw, "Invalid pause time %d setting.", + pfc_conf->fc.pause_time); + return -EINVAL; + } + + if (!(hw->current_fc_status == HNS3_FC_STATUS_NONE || + hw->current_fc_status == HNS3_FC_STATUS_PFC)) { + hns3_err(hw, "MAC pause is enabled. Cannot set PFC." + "current_fc_status = %d", hw->current_fc_status); + return -EOPNOTSUPP; + } + + priority = pfc_conf->priority; + hns3_get_fc_mode(hw, pfc_conf->fc.mode); + if (hw->dcb_info.pfc_en & BIT(priority) && + hw->requested_mode == hw->current_mode && + pfc_conf->fc.pause_time == pf->pause_time) + return 0; + + rte_spinlock_lock(&hw->lock); + ret = hns3_dcb_pfc_enable(dev, pfc_conf); + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3_get_dcb_info(struct rte_eth_dev *dev, struct rte_eth_dcb_info *dcb_info) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_pf *pf = HNS3_DEV_PRIVATE_TO_PF(dev->data->dev_private); + enum rte_eth_rx_mq_mode mq_mode = dev->data->dev_conf.rxmode.mq_mode; + int i; + + rte_spinlock_lock(&hw->lock); + if ((uint32_t)mq_mode & ETH_MQ_RX_DCB_FLAG) + dcb_info->nb_tcs = pf->local_max_tc; + else + dcb_info->nb_tcs = 1; + + for (i = 0; i < HNS3_MAX_USER_PRIO; i++) + dcb_info->prio_tc[i] = hw->dcb_info.prio_tc[i]; + for (i = 0; i < dcb_info->nb_tcs; i++) + dcb_info->tc_bws[i] = hw->dcb_info.pg_info[0].tc_dwrr[i]; + + for (i = 0; i < hw->num_tc; i++) { + dcb_info->tc_queue.tc_rxq[0][i].base = hw->alloc_rss_size * i; + dcb_info->tc_queue.tc_txq[0][i].base = + hw->tc_queue[i].tqp_offset; + dcb_info->tc_queue.tc_rxq[0][i].nb_queue = hw->alloc_rss_size; + dcb_info->tc_queue.tc_txq[0][i].nb_queue = + hw->tc_queue[i].tqp_count; + } + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3_reinit_dev(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_cmd_init(hw); + if (ret) { + hns3_err(hw, "Failed to init cmd: %d", ret); + return ret; + } + + ret = hns3_reset_all_queues(hns); + if (ret) { + hns3_err(hw, "Failed to reset all queues: %d", ret); + return ret; + } + + ret = hns3_init_hardware(hns); + if (ret) { + hns3_err(hw, "Failed to init hardware: %d", ret); + return ret; + } + + ret = hns3_enable_hw_error_intr(hns, true); + if (ret) { + hns3_err(hw, "fail to enable hw error interrupts: %d", + ret); + return ret; + } + hns3_info(hw, "Reset done, driver initialization finished."); + + return 0; +} + +static bool +is_pf_reset_done(struct hns3_hw *hw) +{ + uint32_t val, reg, reg_bit; + + switch (hw->reset.level) { + case HNS3_IMP_RESET: + reg = HNS3_GLOBAL_RESET_REG; + reg_bit = HNS3_IMP_RESET_BIT; + break; + case HNS3_GLOBAL_RESET: + reg = HNS3_GLOBAL_RESET_REG; + reg_bit = HNS3_GLOBAL_RESET_BIT; + break; + case HNS3_FUNC_RESET: + reg = HNS3_FUN_RST_ING; + reg_bit = HNS3_FUN_RST_ING_B; + break; + case HNS3_FLR_RESET: + default: + hns3_err(hw, "Wait for unsupported reset level: %d", + hw->reset.level); + return true; + } + val = hns3_read_dev(hw, reg); + if (hns3_get_bit(val, reg_bit)) + return false; + else + return true; +} + +bool +hns3_is_reset_pending(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + enum hns3_reset_level reset; + + hns3_check_event_cause(hns, NULL); + reset = hns3_get_reset_level(hns, &hw->reset.pending); + if (hw->reset.level != HNS3_NONE_RESET && hw->reset.level < reset) { + hns3_warn(hw, "High level reset %d is pending", reset); + return true; + } + reset = hns3_get_reset_level(hns, &hw->reset.request); + if (hw->reset.level != HNS3_NONE_RESET && hw->reset.level < reset) { + hns3_warn(hw, "High level reset %d is request", reset); + return true; + } + return false; +} + +static int +hns3_wait_hardware_ready(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_wait_data *wait_data = hw->reset.wait_data; + struct timeval tv; + + if (wait_data->result == HNS3_WAIT_SUCCESS) + return 0; + else if (wait_data->result == HNS3_WAIT_TIMEOUT) { + gettimeofday(&tv, NULL); + hns3_warn(hw, "Reset step4 hardware not ready after reset time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + return -ETIME; + } else if (wait_data->result == HNS3_WAIT_REQUEST) + return -EAGAIN; + + wait_data->hns = hns; + wait_data->check_completion = is_pf_reset_done; + wait_data->end_ms = (uint64_t)HNS3_RESET_WAIT_CNT * + HNS3_RESET_WAIT_MS + get_timeofday_ms(); + wait_data->interval = HNS3_RESET_WAIT_MS * USEC_PER_MSEC; + wait_data->count = HNS3_RESET_WAIT_CNT; + wait_data->result = HNS3_WAIT_REQUEST; + rte_eal_alarm_set(wait_data->interval, hns3_wait_callback, wait_data); + return -EAGAIN; +} + +static int +hns3_func_reset_cmd(struct hns3_hw *hw, int func_id) +{ + struct hns3_cmd_desc desc; + struct hns3_reset_cmd *req = (struct hns3_reset_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_RST_TRIGGER, false); + hns3_set_bit(req->mac_func_reset, HNS3_CFG_RESET_FUNC_B, 1); + req->fun_reset_vfid = func_id; + + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_imp_reset_cmd(struct hns3_hw *hw) +{ + struct hns3_cmd_desc desc; + + hns3_cmd_setup_basic_desc(&desc, 0xFFFE, false); + desc.data[0] = 0xeedd; + + return hns3_cmd_send(hw, &desc, 1); +} + +static void +hns3_msix_process(struct hns3_adapter *hns, enum hns3_reset_level reset_level) +{ + struct hns3_hw *hw = &hns->hw; + struct timeval tv; + uint32_t val; + + gettimeofday(&tv, NULL); + if (hns3_read_dev(hw, HNS3_GLOBAL_RESET_REG) || + hns3_read_dev(hw, HNS3_FUN_RST_ING)) { + hns3_warn(hw, "Don't process msix during resetting time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + return; + } + + switch (reset_level) { + case HNS3_IMP_RESET: + hns3_imp_reset_cmd(hw); + hns3_warn(hw, "IMP Reset requested time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + break; + case HNS3_GLOBAL_RESET: + val = hns3_read_dev(hw, HNS3_GLOBAL_RESET_REG); + hns3_set_bit(val, HNS3_GLOBAL_RESET_BIT, 1); + hns3_write_dev(hw, HNS3_GLOBAL_RESET_REG, val); + hns3_warn(hw, "Global Reset requested time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + break; + case HNS3_FUNC_RESET: + hns3_warn(hw, "PF Reset requested time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + /* schedule again to check later */ + hns3_atomic_set_bit(HNS3_FUNC_RESET, &hw->reset.pending); + hns3_schedule_reset(hns); + break; + default: + hns3_warn(hw, "Unsupported reset level: %d", reset_level); + return; + } + hns3_atomic_clear_bit(reset_level, &hw->reset.request); +} + +static enum hns3_reset_level +hns3_get_reset_level(struct hns3_adapter *hns, uint64_t *levels) +{ + struct hns3_hw *hw = &hns->hw; + enum hns3_reset_level reset_level = HNS3_NONE_RESET; + + /* Return the highest priority reset level amongst all */ + if (hns3_atomic_test_bit(HNS3_IMP_RESET, levels)) + reset_level = HNS3_IMP_RESET; + else if (hns3_atomic_test_bit(HNS3_GLOBAL_RESET, levels)) + reset_level = HNS3_GLOBAL_RESET; + else if (hns3_atomic_test_bit(HNS3_FUNC_RESET, levels)) + reset_level = HNS3_FUNC_RESET; + else if (hns3_atomic_test_bit(HNS3_FLR_RESET, levels)) + reset_level = HNS3_FLR_RESET; + + if (hw->reset.level != HNS3_NONE_RESET && reset_level < hw->reset.level) + return HNS3_NONE_RESET; + + return reset_level; +} + +static int +hns3_prepare_reset(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + uint32_t reg_val; + int ret; + + switch (hw->reset.level) { + case HNS3_FUNC_RESET: + ret = hns3_func_reset_cmd(hw, 0); + if (ret) + return ret; + + /* + * After performaning pf reset, it is not necessary to do the + * mailbox handling or send any command to firmware, because + * any mailbox handling or command to firmware is only valid + * after hns3_cmd_init is called. + */ + rte_atomic16_set(&hw->reset.disable_cmd, 1); + hw->reset.stats.request_cnt++; + break; + case HNS3_IMP_RESET: + reg_val = hns3_read_dev(hw, HNS3_VECTOR0_OTER_EN_REG); + hns3_write_dev(hw, HNS3_VECTOR0_OTER_EN_REG, reg_val | + BIT(HNS3_VECTOR0_IMP_RESET_INT_B)); + break; + default: + break; + } + return 0; +} + +static int +hns3_set_rst_done(struct hns3_hw *hw) +{ + struct hns3_pf_rst_done_cmd *req; + struct hns3_cmd_desc desc; + + req = (struct hns3_pf_rst_done_cmd *)desc.data; + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_PF_RST_DONE, false); + req->pf_rst_done |= HNS3_PF_RESET_DONE_BIT; + return hns3_cmd_send(hw, &desc, 1); +} + +static int +hns3_stop_service(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct rte_eth_dev *eth_dev; + + eth_dev = &rte_eth_devices[hw->data->port_id]; + if (hw->adapter_state == HNS3_NIC_STARTED) + rte_eal_alarm_cancel(hns3_service_handler, eth_dev); + hw->mac.link_status = ETH_LINK_DOWN; + + hns3_set_rxtx_function(eth_dev); + rte_wmb(); + /* Disable datapath on secondary process. */ + hns3_mp_req_stop_rxtx(eth_dev); + rte_delay_ms(hw->tqps_num); + + rte_spinlock_lock(&hw->lock); + if (hns->hw.adapter_state == HNS3_NIC_STARTED || + hw->adapter_state == HNS3_NIC_STOPPING) { + hns3_do_stop(hns); + hw->reset.mbuf_deferred_free = true; + } else + hw->reset.mbuf_deferred_free = false; + + /* + * It is cumbersome for hardware to pick-and-choose entries for deletion + * from table space. Hence, for function reset software intervention is + * required to delete the entries + */ + if (rte_atomic16_read(&hw->reset.disable_cmd) == 0) + hns3_configure_all_mc_mac_addr(hns, true); + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3_start_service(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct rte_eth_dev *eth_dev; + + if (hw->reset.level == HNS3_IMP_RESET || + hw->reset.level == HNS3_GLOBAL_RESET) + hns3_set_rst_done(hw); + eth_dev = &rte_eth_devices[hw->data->port_id]; + hns3_set_rxtx_function(eth_dev); + hns3_mp_req_start_rxtx(eth_dev); + if (hw->adapter_state == HNS3_NIC_STARTED) { + hns3_service_handler(eth_dev); + + /* Enable interrupt of all rx queues before enabling queues */ + hns3_dev_all_rx_queue_intr_enable(hw, true); + /* + * When finished the initialization, enable queues to receive + * and transmit packets. + */ + hns3_enable_all_queues(hw, true); + } + + return 0; +} + +static int +hns3_restore_conf(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3_configure_all_mac_addr(hns, false); + if (ret) + return ret; + + ret = hns3_configure_all_mc_mac_addr(hns, false); + if (ret) + goto err_mc_mac; + + ret = hns3_dev_promisc_restore(hns); + if (ret) + goto err_promisc; + + ret = hns3_restore_vlan_table(hns); + if (ret) + goto err_promisc; + + ret = hns3_restore_vlan_conf(hns); + if (ret) + goto err_promisc; + + ret = hns3_restore_all_fdir_filter(hns); + if (ret) + goto err_promisc; + + ret = hns3_restore_rx_interrupt(hw); + if (ret) + goto err_promisc; + + if (hns->hw.adapter_state == HNS3_NIC_STARTED) { + ret = hns3_do_start(hns, false); + if (ret) + goto err_promisc; + hns3_info(hw, "hns3 dev restart successful!"); + } else if (hw->adapter_state == HNS3_NIC_STOPPING) + hw->adapter_state = HNS3_NIC_CONFIGURED; + return 0; + +err_promisc: + hns3_configure_all_mc_mac_addr(hns, true); +err_mc_mac: + hns3_configure_all_mac_addr(hns, true); + return ret; +} + +static void +hns3_reset_service(void *param) +{ + struct hns3_adapter *hns = (struct hns3_adapter *)param; + struct hns3_hw *hw = &hns->hw; + enum hns3_reset_level reset_level; + struct timeval tv_delta; + struct timeval tv_start; + struct timeval tv; + uint64_t msec; + int ret; + + /* + * The interrupt is not triggered within the delay time. + * The interrupt may have been lost. It is necessary to handle + * the interrupt to recover from the error. + */ + if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_DEFERRED) { + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_REQUESTED); + hns3_err(hw, "Handling interrupts in delayed tasks"); + hns3_interrupt_handler(&rte_eth_devices[hw->data->port_id]); + reset_level = hns3_get_reset_level(hns, &hw->reset.pending); + if (reset_level == HNS3_NONE_RESET) { + hns3_err(hw, "No reset level is set, try IMP reset"); + hns3_atomic_set_bit(HNS3_IMP_RESET, &hw->reset.pending); + } + } + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_NONE); + + /* + * Check if there is any ongoing reset in the hardware. This status can + * be checked from reset_pending. If there is then, we need to wait for + * hardware to complete reset. + * a. If we are able to figure out in reasonable time that hardware + * has fully resetted then, we can proceed with driver, client + * reset. + * b. else, we can come back later to check this status so re-sched + * now. + */ + reset_level = hns3_get_reset_level(hns, &hw->reset.pending); + if (reset_level != HNS3_NONE_RESET) { + gettimeofday(&tv_start, NULL); + ret = hns3_reset_process(hns, reset_level); + gettimeofday(&tv, NULL); + timersub(&tv, &tv_start, &tv_delta); + msec = tv_delta.tv_sec * MSEC_PER_SEC + + tv_delta.tv_usec / USEC_PER_MSEC; + if (msec > HNS3_RESET_PROCESS_MS) + hns3_err(hw, "%d handle long time delta %" PRIx64 + " ms time=%ld.%.6ld", + hw->reset.level, msec, + tv.tv_sec, tv.tv_usec); + if (ret == -EAGAIN) + return; + } + + /* Check if we got any *new* reset requests to be honored */ + reset_level = hns3_get_reset_level(hns, &hw->reset.request); + if (reset_level != HNS3_NONE_RESET) + hns3_msix_process(hns, reset_level); +} + +static const struct eth_dev_ops hns3_eth_dev_ops = { + .dev_start = hns3_dev_start, + .dev_stop = hns3_dev_stop, + .dev_close = hns3_dev_close, + .promiscuous_enable = hns3_dev_promiscuous_enable, + .promiscuous_disable = hns3_dev_promiscuous_disable, + .allmulticast_enable = hns3_dev_allmulticast_enable, + .allmulticast_disable = hns3_dev_allmulticast_disable, + .mtu_set = hns3_dev_mtu_set, + .stats_get = hns3_stats_get, + .stats_reset = hns3_stats_reset, + .xstats_get = hns3_dev_xstats_get, + .xstats_get_names = hns3_dev_xstats_get_names, + .xstats_reset = hns3_dev_xstats_reset, + .xstats_get_by_id = hns3_dev_xstats_get_by_id, + .xstats_get_names_by_id = hns3_dev_xstats_get_names_by_id, + .dev_infos_get = hns3_dev_infos_get, + .fw_version_get = hns3_fw_version_get, + .rx_queue_setup = hns3_rx_queue_setup, + .tx_queue_setup = hns3_tx_queue_setup, + .rx_queue_release = hns3_dev_rx_queue_release, + .tx_queue_release = hns3_dev_tx_queue_release, + .rx_queue_intr_enable = hns3_dev_rx_queue_intr_enable, + .rx_queue_intr_disable = hns3_dev_rx_queue_intr_disable, + .dev_configure = hns3_dev_configure, + .flow_ctrl_get = hns3_flow_ctrl_get, + .flow_ctrl_set = hns3_flow_ctrl_set, + .priority_flow_ctrl_set = hns3_priority_flow_ctrl_set, + .mac_addr_add = hns3_add_mac_addr, + .mac_addr_remove = hns3_remove_mac_addr, + .mac_addr_set = hns3_set_default_mac_addr, + .set_mc_addr_list = hns3_set_mc_mac_addr_list, + .link_update = hns3_dev_link_update, + .rss_hash_update = hns3_dev_rss_hash_update, + .rss_hash_conf_get = hns3_dev_rss_hash_conf_get, + .reta_update = hns3_dev_rss_reta_update, + .reta_query = hns3_dev_rss_reta_query, + .filter_ctrl = hns3_dev_filter_ctrl, + .vlan_filter_set = hns3_vlan_filter_set, + .vlan_tpid_set = hns3_vlan_tpid_set, + .vlan_offload_set = hns3_vlan_offload_set, + .vlan_pvid_set = hns3_vlan_pvid_set, + .get_reg = hns3_get_regs, + .get_dcb_info = hns3_get_dcb_info, + .dev_supported_ptypes_get = hns3_dev_supported_ptypes_get, +}; + +static const struct hns3_reset_ops hns3_reset_ops = { + .reset_service = hns3_reset_service, + .stop_service = hns3_stop_service, + .prepare_reset = hns3_prepare_reset, + .wait_hardware_ready = hns3_wait_hardware_ready, + .reinit_dev = hns3_reinit_dev, + .restore_conf = hns3_restore_conf, + .start_service = hns3_start_service, +}; + +static int +hns3_dev_init(struct rte_eth_dev *eth_dev) +{ + struct rte_device *dev = eth_dev->device; + struct rte_pci_device *pci_dev = RTE_DEV_TO_PCI(dev); + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint16_t device_id = pci_dev->id.device_id; + uint8_t revision; + int ret; + + PMD_INIT_FUNC_TRACE(); + + /* Get PCI revision id */ + ret = rte_pci_read_config(pci_dev, &revision, HNS3_PCI_REVISION_ID_LEN, + HNS3_PCI_REVISION_ID); + if (ret != HNS3_PCI_REVISION_ID_LEN) { + PMD_INIT_LOG(ERR, "Failed to read pci revision id, ret = %d", + ret); + return -EIO; + } + hw->revision = revision; + + eth_dev->process_private = (struct hns3_process_private *) + rte_zmalloc_socket("hns3_filter_list", + sizeof(struct hns3_process_private), + RTE_CACHE_LINE_SIZE, eth_dev->device->numa_node); + if (eth_dev->process_private == NULL) { + PMD_INIT_LOG(ERR, "Failed to alloc memory for process private"); + return -ENOMEM; + } + /* initialize flow filter lists */ + hns3_filterlist_init(eth_dev); + + hns3_set_rxtx_function(eth_dev); + eth_dev->dev_ops = &hns3_eth_dev_ops; + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { + hns3_mp_init_secondary(); + hw->secondary_cnt++; + return 0; + } + + hns3_mp_init_primary(); + hw->adapter_state = HNS3_NIC_UNINITIALIZED; + + if (device_id == HNS3_DEV_ID_25GE_RDMA || + device_id == HNS3_DEV_ID_50GE_RDMA || + device_id == HNS3_DEV_ID_100G_RDMA_MACSEC) + hns3_set_bit(hw->flag, HNS3_DEV_SUPPORT_DCB_B, 1); + + hns->is_vf = false; + hw->data = eth_dev->data; + + /* + * Set default max packet size according to the mtu + * default vale in DPDK frame. + */ + hns->pf.mps = hw->data->mtu + HNS3_ETH_OVERHEAD; + + ret = hns3_reset_init(hw); + if (ret) + goto err_init_reset; + hw->reset.ops = &hns3_reset_ops; + + ret = hns3_init_pf(eth_dev); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init pf: %d", ret); + goto err_init_pf; + } + + /* Allocate memory for storing MAC addresses */ + eth_dev->data->mac_addrs = rte_zmalloc("hns3-mac", + sizeof(struct rte_ether_addr) * + HNS3_UC_MACADDR_NUM, 0); + if (eth_dev->data->mac_addrs == NULL) { + PMD_INIT_LOG(ERR, "Failed to allocate %zx bytes needed " + "to store MAC addresses", + sizeof(struct rte_ether_addr) * + HNS3_UC_MACADDR_NUM); + ret = -ENOMEM; + goto err_rte_zmalloc; + } + + rte_ether_addr_copy((struct rte_ether_addr *)hw->mac.mac_addr, + ð_dev->data->mac_addrs[0]); + + hw->adapter_state = HNS3_NIC_INITIALIZED; + /* + * Pass the information to the rte_eth_dev_close() that it should also + * release the private port resources. + */ + eth_dev->data->dev_flags |= RTE_ETH_DEV_CLOSE_REMOVE; + + if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_PENDING) { + hns3_err(hw, "Reschedule reset service after dev_init"); + hns3_schedule_reset(hns); + } else { + /* IMP will wait ready flag before reset */ + hns3_notify_reset_ready(hw, false); + } + + hns3_info(hw, "hns3 dev initialization successful!"); + return 0; + +err_rte_zmalloc: + hns3_uninit_pf(eth_dev); + +err_init_pf: + rte_free(hw->reset.wait_data); +err_init_reset: + eth_dev->dev_ops = NULL; + eth_dev->rx_pkt_burst = NULL; + eth_dev->tx_pkt_burst = NULL; + eth_dev->tx_pkt_prepare = NULL; + rte_free(eth_dev->process_private); + eth_dev->process_private = NULL; + return ret; +} + +static int +hns3_dev_uninit(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + PMD_INIT_FUNC_TRACE(); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return -EPERM; + + eth_dev->dev_ops = NULL; + eth_dev->rx_pkt_burst = NULL; + eth_dev->tx_pkt_burst = NULL; + eth_dev->tx_pkt_prepare = NULL; + if (hw->adapter_state < HNS3_NIC_CLOSING) + hns3_dev_close(eth_dev); + + hw->adapter_state = HNS3_NIC_REMOVED; + return 0; +} + +static int +eth_hns3_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, + struct rte_pci_device *pci_dev) +{ + return rte_eth_dev_pci_generic_probe(pci_dev, + sizeof(struct hns3_adapter), + hns3_dev_init); +} + +static int +eth_hns3_pci_remove(struct rte_pci_device *pci_dev) +{ + return rte_eth_dev_pci_generic_remove(pci_dev, hns3_dev_uninit); +} + +static const struct rte_pci_id pci_id_hns3_map[] = { + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_GE) }, + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_25GE) }, + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_25GE_RDMA) }, + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_50GE_RDMA) }, + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_100G_RDMA_MACSEC) }, + { .vendor_id = 0, /* sentinel */ }, +}; + +static struct rte_pci_driver rte_hns3_pmd = { + .id_table = pci_id_hns3_map, + .drv_flags = RTE_PCI_DRV_NEED_MAPPING, + .probe = eth_hns3_pci_probe, + .remove = eth_hns3_pci_remove, +}; + +RTE_PMD_REGISTER_PCI(net_hns3, rte_hns3_pmd); +RTE_PMD_REGISTER_PCI_TABLE(net_hns3, pci_id_hns3_map); +RTE_PMD_REGISTER_KMOD_DEP(net_hns3, "* igb_uio | vfio-pci"); + +RTE_INIT(hns3_init_log) +{ + hns3_logtype_init = rte_log_register("pmd.net.hns3.init"); + if (hns3_logtype_init >= 0) + rte_log_set_level(hns3_logtype_init, RTE_LOG_NOTICE); + hns3_logtype_driver = rte_log_register("pmd.net.hns3.driver"); + if (hns3_logtype_driver >= 0) + rte_log_set_level(hns3_logtype_driver, RTE_LOG_NOTICE); +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev.h b/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev.h new file mode 100644 index 000000000..06a186451 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev.h @@ -0,0 +1,670 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_ETHDEV_H_ +#define _HNS3_ETHDEV_H_ + +#include <sys/time.h> +#include <rte_alarm.h> + +#include "hns3_cmd.h" +#include "hns3_mbx.h" +#include "hns3_rss.h" +#include "hns3_fdir.h" +#include "hns3_stats.h" + +/* Vendor ID */ +#define PCI_VENDOR_ID_HUAWEI 0x19e5 + +/* Device IDs */ +#define HNS3_DEV_ID_GE 0xA220 +#define HNS3_DEV_ID_25GE 0xA221 +#define HNS3_DEV_ID_25GE_RDMA 0xA222 +#define HNS3_DEV_ID_50GE_RDMA 0xA224 +#define HNS3_DEV_ID_100G_RDMA_MACSEC 0xA226 +#define HNS3_DEV_ID_100G_VF 0xA22E +#define HNS3_DEV_ID_100G_RDMA_PFC_VF 0xA22F + +/* PCI Config offsets */ +#define HNS3_PCI_REVISION_ID 0x08 +#define HNS3_PCI_REVISION_ID_LEN 1 + +#define HNS3_UC_MACADDR_NUM 128 +#define HNS3_VF_UC_MACADDR_NUM 48 +#define HNS3_MC_MACADDR_NUM 128 + +#define HNS3_MAX_BD_SIZE 65535 +#define HNS3_MAX_NON_TSO_BD_PER_PKT 8 +#define HNS3_MAX_TSO_BD_PER_PKT 63 +#define HNS3_MAX_FRAME_LEN 9728 +#define HNS3_VLAN_TAG_SIZE 4 +#define HNS3_DEFAULT_RX_BUF_LEN 2048 +#define HNS3_MAX_BD_PAYLEN (1024 * 1024 - 1) +#define HNS3_MAX_TSO_HDR_SIZE 512 +#define HNS3_MAX_TSO_HDR_BD_NUM 3 + +#define HNS3_ETH_OVERHEAD \ + (RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + HNS3_VLAN_TAG_SIZE * 2) +#define HNS3_PKTLEN_TO_MTU(pktlen) ((pktlen) - HNS3_ETH_OVERHEAD) +#define HNS3_MAX_MTU (HNS3_MAX_FRAME_LEN - HNS3_ETH_OVERHEAD) +#define HNS3_DEFAULT_MTU 1500UL +#define HNS3_DEFAULT_FRAME_LEN (HNS3_DEFAULT_MTU + HNS3_ETH_OVERHEAD) +#define HNS3_MIN_PKT_SIZE 60 + +#define HNS3_4_TCS 4 +#define HNS3_8_TCS 8 + +#define HNS3_MAX_PF_NUM 8 +#define HNS3_UMV_TBL_SIZE 3072 +#define HNS3_DEFAULT_UMV_SPACE_PER_PF \ + (HNS3_UMV_TBL_SIZE / HNS3_MAX_PF_NUM) + +#define HNS3_PF_CFG_BLOCK_SIZE 32 +#define HNS3_PF_CFG_DESC_NUM \ + (HNS3_PF_CFG_BLOCK_SIZE / HNS3_CFG_RD_LEN_BYTES) + +#define HNS3_DEFAULT_ENABLE_PFC_NUM 0 + +#define HNS3_INTR_UNREG_FAIL_RETRY_CNT 5 +#define HNS3_INTR_UNREG_FAIL_DELAY_MS 500 + +#define HNS3_QUIT_RESET_CNT 10 +#define HNS3_QUIT_RESET_DELAY_MS 100 + +#define HNS3_POLL_RESPONE_MS 1 + +#define HNS3_MAX_USER_PRIO 8 +#define HNS3_PG_NUM 4 +enum hns3_fc_mode { + HNS3_FC_NONE, + HNS3_FC_RX_PAUSE, + HNS3_FC_TX_PAUSE, + HNS3_FC_FULL, + HNS3_FC_DEFAULT +}; + +#define HNS3_SCH_MODE_SP 0 +#define HNS3_SCH_MODE_DWRR 1 +struct hns3_pg_info { + uint8_t pg_id; + uint8_t pg_sch_mode; /* 0: sp; 1: dwrr */ + uint8_t tc_bit_map; + uint32_t bw_limit; + uint8_t tc_dwrr[HNS3_MAX_TC_NUM]; +}; + +struct hns3_tc_info { + uint8_t tc_id; + uint8_t tc_sch_mode; /* 0: sp; 1: dwrr */ + uint8_t pgid; + uint32_t bw_limit; + uint8_t up_to_tc_map; /* user priority maping on the TC */ +}; + +struct hns3_dcb_info { + uint8_t num_tc; + uint8_t num_pg; /* It must be 1 if vNET-Base schd */ + uint8_t pg_dwrr[HNS3_PG_NUM]; + uint8_t prio_tc[HNS3_MAX_USER_PRIO]; + struct hns3_pg_info pg_info[HNS3_PG_NUM]; + struct hns3_tc_info tc_info[HNS3_MAX_TC_NUM]; + uint8_t hw_pfc_map; /* Allow for packet drop or not on this TC */ + uint8_t pfc_en; /* Pfc enabled or not for user priority */ +}; + +enum hns3_fc_status { + HNS3_FC_STATUS_NONE, + HNS3_FC_STATUS_MAC_PAUSE, + HNS3_FC_STATUS_PFC, +}; + +struct hns3_tc_queue_info { + uint8_t tqp_offset; /* TQP offset from base TQP */ + uint8_t tqp_count; /* Total TQPs */ + uint8_t tc; /* TC index */ + bool enable; /* If this TC is enable or not */ +}; + +struct hns3_cfg { + uint8_t vmdq_vport_num; + uint8_t tc_num; + uint16_t tqp_desc_num; + uint16_t rx_buf_len; + uint16_t rss_size_max; + uint8_t phy_addr; + uint8_t media_type; + uint8_t mac_addr[RTE_ETHER_ADDR_LEN]; + uint8_t default_speed; + uint32_t numa_node_map; + uint8_t speed_ability; + uint16_t umv_space; +}; + +/* mac media type */ +enum hns3_media_type { + HNS3_MEDIA_TYPE_UNKNOWN, + HNS3_MEDIA_TYPE_FIBER, + HNS3_MEDIA_TYPE_COPPER, + HNS3_MEDIA_TYPE_BACKPLANE, + HNS3_MEDIA_TYPE_NONE, +}; + +struct hns3_mac { + uint8_t mac_addr[RTE_ETHER_ADDR_LEN]; + bool default_addr_setted; /* whether default addr(mac_addr) is setted */ + uint8_t media_type; + uint8_t phy_addr; + uint8_t link_duplex : 1; /* ETH_LINK_[HALF/FULL]_DUPLEX */ + uint8_t link_autoneg : 1; /* ETH_LINK_[AUTONEG/FIXED] */ + uint8_t link_status : 1; /* ETH_LINK_[DOWN/UP] */ + uint32_t link_speed; /* ETH_SPEED_NUM_ */ +}; + +struct hns3_fake_queue_data { + void **rx_queues; /* Array of pointers to fake RX queues. */ + void **tx_queues; /* Array of pointers to fake TX queues. */ + uint16_t nb_fake_rx_queues; /* Number of fake RX queues. */ + uint16_t nb_fake_tx_queues; /* Number of fake TX queues. */ +}; + +/* Primary process maintains driver state in main thread. + * + * +---------------+ + * | UNINITIALIZED |<-----------+ + * +---------------+ | + * |.eth_dev_init |.eth_dev_uninit + * V | + * +---------------+------------+ + * | INITIALIZED | + * +---------------+<-----------<---------------+ + * |.dev_configure | | + * V |failed | + * +---------------+------------+ | + * | CONFIGURING | | + * +---------------+----+ | + * |success | | + * | | +---------------+ + * | | | CLOSING | + * | | +---------------+ + * | | ^ + * V |.dev_configure | + * +---------------+----+ |.dev_close + * | CONFIGURED |----------------------------+ + * +---------------+<-----------+ + * |.dev_start | + * V | + * +---------------+ | + * | STARTING |------------^ + * +---------------+ failed | + * |success | + * | +---------------+ + * | | STOPPING | + * | +---------------+ + * | ^ + * V |.dev_stop + * +---------------+------------+ + * | STARTED | + * +---------------+ + */ +enum hns3_adapter_state { + HNS3_NIC_UNINITIALIZED = 0, + HNS3_NIC_INITIALIZED, + HNS3_NIC_CONFIGURING, + HNS3_NIC_CONFIGURED, + HNS3_NIC_STARTING, + HNS3_NIC_STARTED, + HNS3_NIC_STOPPING, + HNS3_NIC_CLOSING, + HNS3_NIC_CLOSED, + HNS3_NIC_REMOVED, + HNS3_NIC_NSTATES +}; + +/* Reset various stages, execute in order */ +enum hns3_reset_stage { + /* Stop query services, stop transceiver, disable MAC */ + RESET_STAGE_DOWN, + /* Clear reset completion flags, disable send command */ + RESET_STAGE_PREWAIT, + /* Inform IMP to start resetting */ + RESET_STAGE_REQ_HW_RESET, + /* Waiting for hardware reset to complete */ + RESET_STAGE_WAIT, + /* Reinitialize hardware */ + RESET_STAGE_DEV_INIT, + /* Restore user settings and enable MAC */ + RESET_STAGE_RESTORE, + /* Restart query services, start transceiver */ + RESET_STAGE_DONE, + /* Not in reset state */ + RESET_STAGE_NONE, +}; + +enum hns3_reset_level { + HNS3_NONE_RESET, + HNS3_VF_FUNC_RESET, /* A VF function reset */ + /* + * All VFs under a PF perform function reset. + * Kernel PF driver use mailbox to inform DPDK VF to do reset, the value + * of the reset level and the one defined in kernel driver should be + * same. + */ + HNS3_VF_PF_FUNC_RESET = 2, + /* + * All VFs under a PF perform FLR reset. + * Kernel PF driver use mailbox to inform DPDK VF to do reset, the value + * of the reset level and the one defined in kernel driver should be + * same. + */ + HNS3_VF_FULL_RESET = 3, + HNS3_FLR_RESET, /* A VF perform FLR reset */ + /* All VFs under the rootport perform a global or IMP reset */ + HNS3_VF_RESET, + HNS3_FUNC_RESET, /* A PF function reset */ + /* All PFs under the rootport perform a global reset */ + HNS3_GLOBAL_RESET, + HNS3_IMP_RESET, /* All PFs under the rootport perform a IMP reset */ + HNS3_MAX_RESET +}; + +enum hns3_wait_result { + HNS3_WAIT_UNKNOWN, + HNS3_WAIT_REQUEST, + HNS3_WAIT_SUCCESS, + HNS3_WAIT_TIMEOUT +}; + +#define HNS3_RESET_SYNC_US 100000 + +struct hns3_reset_stats { + uint64_t request_cnt; /* Total request reset times */ + uint64_t global_cnt; /* Total GLOBAL reset times */ + uint64_t imp_cnt; /* Total IMP reset times */ + uint64_t exec_cnt; /* Total reset executive times */ + uint64_t success_cnt; /* Total reset successful times */ + uint64_t fail_cnt; /* Total reset failed times */ + uint64_t merge_cnt; /* Total merged in high reset times */ +}; + +typedef bool (*check_completion_func)(struct hns3_hw *hw); + +struct hns3_wait_data { + void *hns; + uint64_t end_ms; + uint64_t interval; + int16_t count; + enum hns3_wait_result result; + check_completion_func check_completion; +}; + +struct hns3_reset_ops { + void (*reset_service)(void *arg); + int (*stop_service)(struct hns3_adapter *hns); + int (*prepare_reset)(struct hns3_adapter *hns); + int (*wait_hardware_ready)(struct hns3_adapter *hns); + int (*reinit_dev)(struct hns3_adapter *hns); + int (*restore_conf)(struct hns3_adapter *hns); + int (*start_service)(struct hns3_adapter *hns); +}; + +enum hns3_schedule { + SCHEDULE_NONE, + SCHEDULE_PENDING, + SCHEDULE_REQUESTED, + SCHEDULE_DEFERRED, +}; + +struct hns3_reset_data { + enum hns3_reset_stage stage; + rte_atomic16_t schedule; + /* Reset flag, covering the entire reset process */ + rte_atomic16_t resetting; + /* Used to disable sending cmds during reset */ + rte_atomic16_t disable_cmd; + /* The reset level being processed */ + enum hns3_reset_level level; + /* Reset level set, each bit represents a reset level */ + uint64_t pending; + /* Request reset level set, from interrupt or mailbox */ + uint64_t request; + int attempts; /* Reset failure retry */ + int retries; /* Timeout failure retry in reset_post */ + /* + * At the time of global or IMP reset, the command cannot be sent to + * stop the tx/rx queues. Tx/Rx queues may be access mbuf during the + * reset process, so the mbuf is required to be released after the reset + * is completed.The mbuf_deferred_free is used to mark whether mbuf + * needs to be released. + */ + bool mbuf_deferred_free; + struct timeval start_time; + struct hns3_reset_stats stats; + const struct hns3_reset_ops *ops; + struct hns3_wait_data *wait_data; +}; + +struct hns3_hw { + struct rte_eth_dev_data *data; + void *io_base; + uint8_t revision; /* PCI revision, low byte of class word */ + struct hns3_cmq cmq; + struct hns3_mbx_resp_status mbx_resp; /* mailbox response */ + struct hns3_mbx_arq_ring arq; /* mailbox async rx queue */ + pthread_t irq_thread_id; + struct hns3_mac mac; + unsigned int secondary_cnt; /* Number of secondary processes init'd. */ + struct hns3_tqp_stats tqp_stats; + /* Include Mac stats | Rx stats | Tx stats */ + struct hns3_mac_stats mac_stats; + uint32_t fw_version; + + uint16_t num_msi; + uint16_t total_tqps_num; /* total task queue pairs of this PF */ + uint16_t tqps_num; /* num task queue pairs of this function */ + uint16_t intr_tqps_num; /* num queue pairs mapping interrupt */ + uint16_t rss_size_max; /* HW defined max RSS task queue */ + uint16_t rx_buf_len; + uint16_t num_tx_desc; /* desc num of per tx queue */ + uint16_t num_rx_desc; /* desc num of per rx queue */ + + struct rte_ether_addr mc_addrs[HNS3_MC_MACADDR_NUM]; + int mc_addrs_num; /* Multicast mac addresses number */ + + /* The configuration info of RSS */ + struct hns3_rss_conf rss_info; + bool rss_dis_flag; /* disable rss flag. true: disable, false: enable */ + + uint8_t num_tc; /* Total number of enabled TCs */ + uint8_t hw_tc_map; + enum hns3_fc_mode current_mode; + enum hns3_fc_mode requested_mode; + struct hns3_dcb_info dcb_info; + enum hns3_fc_status current_fc_status; /* current flow control status */ + struct hns3_tc_queue_info tc_queue[HNS3_MAX_TC_NUM]; + uint16_t used_rx_queues; + uint16_t used_tx_queues; + + /* Config max queue numbers between rx and tx queues from user */ + uint16_t cfg_max_queues; + struct hns3_fake_queue_data fkq_data; /* fake queue data */ + uint16_t alloc_rss_size; /* RX queue number per TC */ + uint16_t tx_qnum_per_tc; /* TX queue number per TC */ + + uint32_t flag; + /* + * PMD setup and configuration is not thread safe. Since it is not + * performance sensitive, it is better to guarantee thread-safety + * and add device level lock. Adapter control operations which + * change its state should acquire the lock. + */ + rte_spinlock_t lock; + enum hns3_adapter_state adapter_state; + struct hns3_reset_data reset; +}; + +#define HNS3_FLAG_TC_BASE_SCH_MODE 1 +#define HNS3_FLAG_VNET_BASE_SCH_MODE 2 + +struct hns3_err_msix_intr_stats { + uint64_t mac_afifo_tnl_intr_cnt; + uint64_t ppu_mpf_abnormal_intr_st2_cnt; + uint64_t ssu_port_based_pf_intr_cnt; + uint64_t ppp_pf_abnormal_intr_cnt; + uint64_t ppu_pf_abnormal_intr_cnt; +}; + +/* vlan entry information. */ +struct hns3_user_vlan_table { + LIST_ENTRY(hns3_user_vlan_table) next; + bool hd_tbl_status; + uint16_t vlan_id; +}; + +struct hns3_port_base_vlan_config { + uint16_t state; + uint16_t pvid; +}; + +/* Vlan tag configuration for RX direction */ +struct hns3_rx_vtag_cfg { + uint8_t rx_vlan_offload_en; /* Whether enable rx vlan offload */ + uint8_t strip_tag1_en; /* Whether strip inner vlan tag */ + uint8_t strip_tag2_en; /* Whether strip outer vlan tag */ + uint8_t vlan1_vlan_prionly; /* Inner VLAN Tag up to descriptor Enable */ + uint8_t vlan2_vlan_prionly; /* Outer VLAN Tag up to descriptor Enable */ +}; + +/* Vlan tag configuration for TX direction */ +struct hns3_tx_vtag_cfg { + bool accept_tag1; /* Whether accept tag1 packet from host */ + bool accept_untag1; /* Whether accept untag1 packet from host */ + bool accept_tag2; + bool accept_untag2; + bool insert_tag1_en; /* Whether insert inner vlan tag */ + bool insert_tag2_en; /* Whether insert outer vlan tag */ + uint16_t default_tag1; /* The default inner vlan tag to insert */ + uint16_t default_tag2; /* The default outer vlan tag to insert */ +}; + +struct hns3_vtag_cfg { + struct hns3_rx_vtag_cfg rx_vcfg; + struct hns3_tx_vtag_cfg tx_vcfg; +}; + +/* Request types for IPC. */ +enum hns3_mp_req_type { + HNS3_MP_REQ_START_RXTX = 1, + HNS3_MP_REQ_STOP_RXTX, + HNS3_MP_REQ_MAX +}; + +/* Pameters for IPC. */ +struct hns3_mp_param { + enum hns3_mp_req_type type; + int port_id; + int result; +}; + +/* Request timeout for IPC. */ +#define HNS3_MP_REQ_TIMEOUT_SEC 5 + +/* Key string for IPC. */ +#define HNS3_MP_NAME "net_hns3_mp" + +struct hns3_pf { + struct hns3_adapter *adapter; + bool is_main_pf; + uint16_t func_num; /* num functions of this pf, include pf and vfs */ + + uint32_t pkt_buf_size; /* Total pf buf size for tx/rx */ + uint32_t tx_buf_size; /* Tx buffer size for each TC */ + uint32_t dv_buf_size; /* Dv buffer size for each TC */ + + uint16_t mps; /* Max packet size */ + + uint8_t tx_sch_mode; + uint8_t tc_max; /* max number of tc driver supported */ + uint8_t local_max_tc; /* max number of local tc */ + uint8_t pfc_max; + uint8_t prio_tc[HNS3_MAX_USER_PRIO]; /* TC indexed by prio */ + uint16_t pause_time; + bool support_fc_autoneg; /* support FC autonegotiate */ + + uint16_t wanted_umv_size; + uint16_t max_umv_size; + uint16_t used_umv_size; + + /* Statistics information for abnormal interrupt */ + struct hns3_err_msix_intr_stats abn_int_stats; + + bool support_sfp_query; + + struct hns3_vtag_cfg vtag_config; + struct hns3_port_base_vlan_config port_base_vlan_cfg; + LIST_HEAD(vlan_tbl, hns3_user_vlan_table) vlan_list; + + struct hns3_fdir_info fdir; /* flow director info */ + LIST_HEAD(counters, hns3_flow_counter) flow_counters; +}; + +struct hns3_vf { + struct hns3_adapter *adapter; +}; + +struct hns3_adapter { + struct hns3_hw hw; + + /* Specific for PF or VF */ + bool is_vf; /* false - PF, true - VF */ + union { + struct hns3_pf pf; + struct hns3_vf vf; + }; +}; + +#define HNS3_DEV_SUPPORT_DCB_B 0x0 + +#define hns3_dev_dcb_supported(hw) \ + hns3_get_bit((hw)->flag, HNS3_DEV_SUPPORT_DCB_B) + +#define HNS3_DEV_PRIVATE_TO_HW(adapter) \ + (&((struct hns3_adapter *)adapter)->hw) +#define HNS3_DEV_PRIVATE_TO_ADAPTER(adapter) \ + ((struct hns3_adapter *)adapter) +#define HNS3_DEV_PRIVATE_TO_PF(adapter) \ + (&((struct hns3_adapter *)adapter)->pf) +#define HNS3VF_DEV_PRIVATE_TO_VF(adapter) \ + (&((struct hns3_adapter *)adapter)->vf) +#define HNS3_DEV_HW_TO_ADAPTER(hw) \ + container_of(hw, struct hns3_adapter, hw) + +#define hns3_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= ((val) << (shift)) & (mask); \ + } while (0) +#define hns3_get_field(origin, mask, shift) \ + (((origin) & (mask)) >> (shift)) +#define hns3_set_bit(origin, shift, val) \ + hns3_set_field((origin), (0x1UL << (shift)), (shift), (val)) +#define hns3_get_bit(origin, shift) \ + hns3_get_field((origin), (0x1UL << (shift)), (shift)) + +/* + * upper_32_bits - return bits 32-63 of a number + * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress + * the "right shift count >= width of type" warning when that quantity is + * 32-bits. + */ +#define upper_32_bits(n) ((uint32_t)(((n) >> 16) >> 16)) + +/* lower_32_bits - return bits 0-31 of a number */ +#define lower_32_bits(n) ((uint32_t)(n)) + +#define BIT(nr) (1UL << (nr)) + +#define BITS_PER_LONG (__SIZEOF_LONG__ * 8) +#define GENMASK(h, l) \ + (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) +#define rounddown(x, y) ((x) - ((x) % (y))) + +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define max_t(type, x, y) ({ \ + type __max1 = (x); \ + type __max2 = (y); \ + __max1 > __max2 ? __max1 : __max2; }) + +static inline void hns3_write_reg(void *base, uint32_t reg, uint32_t value) +{ + rte_write32(value, (volatile void *)((char *)base + reg)); +} + +static inline uint32_t hns3_read_reg(void *base, uint32_t reg) +{ + return rte_read32((volatile void *)((char *)base + reg)); +} + +#define hns3_write_dev(a, reg, value) \ + hns3_write_reg((a)->io_base, (reg), (value)) + +#define hns3_read_dev(a, reg) \ + hns3_read_reg((a)->io_base, (reg)) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define NEXT_ITEM_OF_ACTION(act, actions, index) \ + do { \ + act = (actions) + (index); \ + while (act->type == RTE_FLOW_ACTION_TYPE_VOID) { \ + (index)++; \ + act = actions + index; \ + } \ + } while (0) + +#define MSEC_PER_SEC 1000L +#define USEC_PER_MSEC 1000L + +static inline uint64_t +get_timeofday_ms(void) +{ + struct timeval tv; + + (void)gettimeofday(&tv, NULL); + + return (uint64_t)tv.tv_sec * MSEC_PER_SEC + tv.tv_usec / USEC_PER_MSEC; +} + +static inline uint64_t +hns3_atomic_test_bit(unsigned int nr, volatile uint64_t *addr) +{ + uint64_t res; + + res = (__atomic_load_n(addr, __ATOMIC_RELAXED) & (1UL << nr)) != 0; + return res; +} + +static inline void +hns3_atomic_set_bit(unsigned int nr, volatile uint64_t *addr) +{ + __atomic_fetch_or(addr, (1UL << nr), __ATOMIC_RELAXED); +} + +static inline void +hns3_atomic_clear_bit(unsigned int nr, volatile uint64_t *addr) +{ + __atomic_fetch_and(addr, ~(1UL << nr), __ATOMIC_RELAXED); +} + +static inline int64_t +hns3_test_and_clear_bit(unsigned int nr, volatile uint64_t *addr) +{ + uint64_t mask = (1UL << nr); + + return __atomic_fetch_and(addr, ~mask, __ATOMIC_RELAXED) & mask; +} + +int hns3_buffer_alloc(struct hns3_hw *hw); +int hns3_config_gro(struct hns3_hw *hw, bool en); +int hns3_dev_filter_ctrl(struct rte_eth_dev *dev, + enum rte_filter_type filter_type, + enum rte_filter_op filter_op, void *arg); +bool hns3_is_reset_pending(struct hns3_adapter *hns); +bool hns3vf_is_reset_pending(struct hns3_adapter *hns); +void hns3_update_link_status(struct hns3_hw *hw); + +static inline bool +is_reset_pending(struct hns3_adapter *hns) +{ + bool ret; + if (hns->is_vf) + ret = hns3vf_is_reset_pending(hns); + else + ret = hns3_is_reset_pending(hns); + return ret; +} + +#endif /* _HNS3_ETHDEV_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev_vf.c b/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev_vf.c new file mode 100644 index 000000000..904562e03 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_ethdev_vf.c @@ -0,0 +1,2572 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <inttypes.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <linux/pci_regs.h> + +#include <rte_alarm.h> +#include <rte_atomic.h> +#include <rte_bus_pci.h> +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_eal.h> +#include <rte_ether.h> +#include <rte_ethdev_driver.h> +#include <rte_ethdev_pci.h> +#include <rte_interrupts.h> +#include <rte_io.h> +#include <rte_log.h> +#include <rte_pci.h> +#include <rte_vfio.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" +#include "hns3_rxtx.h" +#include "hns3_regs.h" +#include "hns3_intr.h" +#include "hns3_dcb.h" +#include "hns3_mp.h" + +#define HNS3VF_KEEP_ALIVE_INTERVAL 2000000 /* us */ +#define HNS3VF_SERVICE_INTERVAL 1000000 /* us */ + +#define HNS3VF_RESET_WAIT_MS 20 +#define HNS3VF_RESET_WAIT_CNT 2000 + +/* Reset related Registers */ +#define HNS3_GLOBAL_RESET_BIT 0 +#define HNS3_CORE_RESET_BIT 1 +#define HNS3_IMP_RESET_BIT 2 +#define HNS3_FUN_RST_ING_B 0 + +enum hns3vf_evt_cause { + HNS3VF_VECTOR0_EVENT_RST, + HNS3VF_VECTOR0_EVENT_MBX, + HNS3VF_VECTOR0_EVENT_OTHER, +}; + +static enum hns3_reset_level hns3vf_get_reset_level(struct hns3_hw *hw, + uint64_t *levels); +static int hns3vf_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu); +static int hns3vf_dev_configure_vlan(struct rte_eth_dev *dev); + +static int hns3vf_add_mc_mac_addr(struct hns3_hw *hw, + struct rte_ether_addr *mac_addr); +static int hns3vf_remove_mc_mac_addr(struct hns3_hw *hw, + struct rte_ether_addr *mac_addr); +/* set PCI bus mastering */ +static void +hns3vf_set_bus_master(const struct rte_pci_device *device, bool op) +{ + uint16_t reg; + + rte_pci_read_config(device, ®, sizeof(reg), PCI_COMMAND); + + if (op) + /* set the master bit */ + reg |= PCI_COMMAND_MASTER; + else + reg &= ~(PCI_COMMAND_MASTER); + + rte_pci_write_config(device, ®, sizeof(reg), PCI_COMMAND); +} + +/** + * hns3vf_find_pci_capability - lookup a capability in the PCI capability list + * @cap: the capability + * + * Return the address of the given capability within the PCI capability list. + */ +static int +hns3vf_find_pci_capability(const struct rte_pci_device *device, int cap) +{ +#define MAX_PCIE_CAPABILITY 48 + uint16_t status; + uint8_t pos; + uint8_t id; + int ttl; + + rte_pci_read_config(device, &status, sizeof(status), PCI_STATUS); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + ttl = MAX_PCIE_CAPABILITY; + rte_pci_read_config(device, &pos, sizeof(pos), PCI_CAPABILITY_LIST); + while (ttl-- && pos >= PCI_STD_HEADER_SIZEOF) { + rte_pci_read_config(device, &id, sizeof(id), + (pos + PCI_CAP_LIST_ID)); + + if (id == 0xFF) + break; + + if (id == cap) + return (int)pos; + + rte_pci_read_config(device, &pos, sizeof(pos), + (pos + PCI_CAP_LIST_NEXT)); + } + return 0; +} + +static int +hns3vf_enable_msix(const struct rte_pci_device *device, bool op) +{ + uint16_t control; + int pos; + + pos = hns3vf_find_pci_capability(device, PCI_CAP_ID_MSIX); + if (pos) { + rte_pci_read_config(device, &control, sizeof(control), + (pos + PCI_MSIX_FLAGS)); + if (op) + control |= PCI_MSIX_FLAGS_ENABLE; + else + control &= ~PCI_MSIX_FLAGS_ENABLE; + rte_pci_write_config(device, &control, sizeof(control), + (pos + PCI_MSIX_FLAGS)); + return 0; + } + return -ENXIO; +} + +static int +hns3vf_add_uc_mac_addr(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + /* mac address was checked by upper level interface */ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_UNICAST, + HNS3_MBX_MAC_VLAN_UC_ADD, mac_addr->addr_bytes, + RTE_ETHER_ADDR_LEN, false, NULL, 0); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add uc mac addr(%s), ret = %d", + mac_str, ret); + } + return ret; +} + +static int +hns3vf_remove_uc_mac_addr(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + /* mac address was checked by upper level interface */ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_UNICAST, + HNS3_MBX_MAC_VLAN_UC_REMOVE, + mac_addr->addr_bytes, RTE_ETHER_ADDR_LEN, + false, NULL, 0); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add uc mac addr(%s), ret = %d", + mac_str, ret); + } + return ret; +} + +static int +hns3vf_add_mc_addr_common(struct hns3_hw *hw, struct rte_ether_addr *mac_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct rte_ether_addr *addr; + int ret; + int i; + + for (i = 0; i < hw->mc_addrs_num; i++) { + addr = &hw->mc_addrs[i]; + /* Check if there are duplicate addresses */ + if (rte_is_same_ether_addr(addr, mac_addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to add mc mac addr, same addrs" + "(%s) is added by the set_mc_mac_addr_list " + "API", mac_str); + return -EINVAL; + } + } + + ret = hns3vf_add_mc_mac_addr(hw, mac_addr); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add mc mac addr(%s), ret = %d", + mac_str, ret); + } + return ret; +} + +static int +hns3vf_add_mac_addr(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr, + __rte_unused uint32_t idx, + __rte_unused uint32_t pool) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + rte_spinlock_lock(&hw->lock); + + /* + * In hns3 network engine adding UC and MC mac address with different + * commands with firmware. We need to determine whether the input + * address is a UC or a MC address to call different commands. + * By the way, it is recommended calling the API function named + * rte_eth_dev_set_mc_addr_list to set the MC mac address, because + * using the rte_eth_dev_mac_addr_add API function to set MC mac address + * may affect the specifications of UC mac addresses. + */ + if (rte_is_multicast_ether_addr(mac_addr)) + ret = hns3vf_add_mc_addr_common(hw, mac_addr); + else + ret = hns3vf_add_uc_mac_addr(hw, mac_addr); + + rte_spinlock_unlock(&hw->lock); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to add mac addr(%s), ret = %d", mac_str, + ret); + } + + return ret; +} + +static void +hns3vf_remove_mac_addr(struct rte_eth_dev *dev, uint32_t idx) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + /* index will be checked by upper level rte interface */ + struct rte_ether_addr *mac_addr = &dev->data->mac_addrs[idx]; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + rte_spinlock_lock(&hw->lock); + + if (rte_is_multicast_ether_addr(mac_addr)) + ret = hns3vf_remove_mc_mac_addr(hw, mac_addr); + else + ret = hns3vf_remove_uc_mac_addr(hw, mac_addr); + + rte_spinlock_unlock(&hw->lock); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "failed to remove mac addr(%s), ret = %d", + mac_str, ret); + } +} + +static int +hns3vf_set_default_mac_addr(struct rte_eth_dev *dev, + struct rte_ether_addr *mac_addr) +{ +#define HNS3_TWO_ETHER_ADDR_LEN (RTE_ETHER_ADDR_LEN * 2) + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_ether_addr *old_addr; + uint8_t addr_bytes[HNS3_TWO_ETHER_ADDR_LEN]; /* for 2 MAC addresses */ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + /* + * It has been guaranteed that input parameter named mac_addr is valid + * address in the rte layer of DPDK framework. + */ + old_addr = (struct rte_ether_addr *)hw->mac.mac_addr; + rte_spinlock_lock(&hw->lock); + memcpy(addr_bytes, mac_addr->addr_bytes, RTE_ETHER_ADDR_LEN); + memcpy(&addr_bytes[RTE_ETHER_ADDR_LEN], old_addr->addr_bytes, + RTE_ETHER_ADDR_LEN); + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_UNICAST, + HNS3_MBX_MAC_VLAN_UC_MODIFY, addr_bytes, + HNS3_TWO_ETHER_ADDR_LEN, true, NULL, 0); + if (ret) { + /* + * The hns3 VF PMD driver depends on the hns3 PF kernel ethdev + * driver. When user has configured a MAC address for VF device + * by "ip link set ..." command based on the PF device, the hns3 + * PF kernel ethdev driver does not allow VF driver to request + * reconfiguring a different default MAC address, and return + * -EPREM to VF driver through mailbox. + */ + if (ret == -EPERM) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + old_addr); + hns3_warn(hw, "Has permanet mac addr(%s) for vf", + mac_str); + } else { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Failed to set mac addr(%s) for vf: %d", + mac_str, ret); + } + } + + rte_ether_addr_copy(mac_addr, + (struct rte_ether_addr *)hw->mac.mac_addr); + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +static int +hns3vf_configure_mac_addr(struct hns3_adapter *hns, bool del) +{ + struct hns3_hw *hw = &hns->hw; + struct rte_ether_addr *addr; + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int err = 0; + int ret; + int i; + + for (i = 0; i < HNS3_VF_UC_MACADDR_NUM; i++) { + addr = &hw->data->mac_addrs[i]; + if (rte_is_zero_ether_addr(addr)) + continue; + if (rte_is_multicast_ether_addr(addr)) + ret = del ? hns3vf_remove_mc_mac_addr(hw, addr) : + hns3vf_add_mc_mac_addr(hw, addr); + else + ret = del ? hns3vf_remove_uc_mac_addr(hw, addr) : + hns3vf_add_uc_mac_addr(hw, addr); + + if (ret) { + err = ret; + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to %s mac addr(%s) index:%d " + "ret = %d.", del ? "remove" : "restore", + mac_str, i, ret); + } + } + return err; +} + +static int +hns3vf_add_mc_mac_addr(struct hns3_hw *hw, + struct rte_ether_addr *mac_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_MULTICAST, + HNS3_MBX_MAC_VLAN_MC_ADD, + mac_addr->addr_bytes, RTE_ETHER_ADDR_LEN, false, + NULL, 0); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Failed to add mc mac addr(%s) for vf: %d", + mac_str, ret); + } + + return ret; +} + +static int +hns3vf_remove_mc_mac_addr(struct hns3_hw *hw, + struct rte_ether_addr *mac_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_MULTICAST, + HNS3_MBX_MAC_VLAN_MC_REMOVE, + mac_addr->addr_bytes, RTE_ETHER_ADDR_LEN, false, + NULL, 0); + if (ret) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + mac_addr); + hns3_err(hw, "Failed to remove mc mac addr(%s) for vf: %d", + mac_str, ret); + } + + return ret; +} + +static int +hns3vf_set_mc_addr_chk_param(struct hns3_hw *hw, + struct rte_ether_addr *mc_addr_set, + uint32_t nb_mc_addr) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct rte_ether_addr *addr; + uint32_t i; + uint32_t j; + + if (nb_mc_addr > HNS3_MC_MACADDR_NUM) { + hns3_err(hw, "failed to set mc mac addr, nb_mc_addr(%d) " + "invalid. valid range: 0~%d", + nb_mc_addr, HNS3_MC_MACADDR_NUM); + return -EINVAL; + } + + /* Check if input mac addresses are valid */ + for (i = 0; i < nb_mc_addr; i++) { + addr = &mc_addr_set[i]; + if (!rte_is_multicast_ether_addr(addr)) { + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, + "failed to set mc mac addr, addr(%s) invalid.", + mac_str); + return -EINVAL; + } + + /* Check if there are duplicate addresses */ + for (j = i + 1; j < nb_mc_addr; j++) { + if (rte_is_same_ether_addr(addr, &mc_addr_set[j])) { + rte_ether_format_addr(mac_str, + RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to set mc mac addr, " + "addrs invalid. two same addrs(%s).", + mac_str); + return -EINVAL; + } + } + + /* + * Check if there are duplicate addresses between mac_addrs + * and mc_addr_set + */ + for (j = 0; j < HNS3_VF_UC_MACADDR_NUM; j++) { + if (rte_is_same_ether_addr(addr, + &hw->data->mac_addrs[j])) { + rte_ether_format_addr(mac_str, + RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "failed to set mc mac addr, " + "addrs invalid. addrs(%s) has already " + "configured in mac_addr add API", + mac_str); + return -EINVAL; + } + } + } + + return 0; +} + +static int +hns3vf_set_mc_mac_addr_list(struct rte_eth_dev *dev, + struct rte_ether_addr *mc_addr_set, + uint32_t nb_mc_addr) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_ether_addr *addr; + int cur_addr_num; + int set_addr_num; + int num; + int ret; + int i; + + ret = hns3vf_set_mc_addr_chk_param(hw, mc_addr_set, nb_mc_addr); + if (ret) + return ret; + + rte_spinlock_lock(&hw->lock); + cur_addr_num = hw->mc_addrs_num; + for (i = 0; i < cur_addr_num; i++) { + num = cur_addr_num - i - 1; + addr = &hw->mc_addrs[num]; + ret = hns3vf_remove_mc_mac_addr(hw, addr); + if (ret) { + rte_spinlock_unlock(&hw->lock); + return ret; + } + + hw->mc_addrs_num--; + } + + set_addr_num = (int)nb_mc_addr; + for (i = 0; i < set_addr_num; i++) { + addr = &mc_addr_set[i]; + ret = hns3vf_add_mc_mac_addr(hw, addr); + if (ret) { + rte_spinlock_unlock(&hw->lock); + return ret; + } + + rte_ether_addr_copy(addr, &hw->mc_addrs[hw->mc_addrs_num]); + hw->mc_addrs_num++; + } + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3vf_configure_all_mc_mac_addr(struct hns3_adapter *hns, bool del) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct hns3_hw *hw = &hns->hw; + struct rte_ether_addr *addr; + int err = 0; + int ret; + int i; + + for (i = 0; i < hw->mc_addrs_num; i++) { + addr = &hw->mc_addrs[i]; + if (!rte_is_multicast_ether_addr(addr)) + continue; + if (del) + ret = hns3vf_remove_mc_mac_addr(hw, addr); + else + ret = hns3vf_add_mc_mac_addr(hw, addr); + if (ret) { + err = ret; + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + addr); + hns3_err(hw, "Failed to %s mc mac addr: %s for vf: %d", + del ? "Remove" : "Restore", mac_str, ret); + } + } + return err; +} + +static int +hns3vf_set_promisc_mode(struct hns3_hw *hw, bool en_bc_pmc, + bool en_uc_pmc, bool en_mc_pmc) +{ + struct hns3_mbx_vf_to_pf_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + req = (struct hns3_mbx_vf_to_pf_cmd *)desc.data; + + /* + * The hns3 VF PMD driver depends on the hns3 PF kernel ethdev driver, + * so there are some features for promiscuous/allmulticast mode in hns3 + * VF PMD driver as below: + * 1. The promiscuous/allmulticast mode can be configured successfully + * only based on the trusted VF device. If based on the non trusted + * VF device, configuring promiscuous/allmulticast mode will fail. + * The hns3 VF device can be confiruged as trusted device by hns3 PF + * kernel ethdev driver on the host by the following command: + * "ip link set <eth num> vf <vf id> turst on" + * 2. After the promiscuous mode is configured successfully, hns3 VF PMD + * driver can receive the ingress and outgoing traffic. In the words, + * all the ingress packets, all the packets sent from the PF and + * other VFs on the same physical port. + * 3. Note: Because of the hardware constraints, By default vlan filter + * is enabled and couldn't be turned off based on VF device, so vlan + * filter is still effective even in promiscuous mode. If upper + * applications don't call rte_eth_dev_vlan_filter API function to + * set vlan based on VF device, hns3 VF PMD driver will can't receive + * the packets with vlan tag in promiscuoue mode. + */ + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MBX_VF_TO_PF, false); + req->msg[0] = HNS3_MBX_SET_PROMISC_MODE; + req->msg[1] = en_bc_pmc ? 1 : 0; + req->msg[2] = en_uc_pmc ? 1 : 0; + req->msg[3] = en_mc_pmc ? 1 : 0; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Set promisc mode fail, ret = %d", ret); + + return ret; +} + +static int +hns3vf_dev_promiscuous_enable(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3vf_set_promisc_mode(hw, true, true, true); + if (ret) + hns3_err(hw, "Failed to enable promiscuous mode, ret = %d", + ret); + return ret; +} + +static int +hns3vf_dev_promiscuous_disable(struct rte_eth_dev *dev) +{ + bool allmulti = dev->data->all_multicast ? true : false; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3vf_set_promisc_mode(hw, true, false, allmulti); + if (ret) + hns3_err(hw, "Failed to disable promiscuous mode, ret = %d", + ret); + return ret; +} + +static int +hns3vf_dev_allmulticast_enable(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (dev->data->promiscuous) + return 0; + + ret = hns3vf_set_promisc_mode(hw, true, false, true); + if (ret) + hns3_err(hw, "Failed to enable allmulticast mode, ret = %d", + ret); + return ret; +} + +static int +hns3vf_dev_allmulticast_disable(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (dev->data->promiscuous) + return 0; + + ret = hns3vf_set_promisc_mode(hw, true, false, false); + if (ret) + hns3_err(hw, "Failed to disable allmulticast mode, ret = %d", + ret); + return ret; +} + +static int +hns3vf_restore_promisc(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + bool allmulti = hw->data->all_multicast ? true : false; + + if (hw->data->promiscuous) + return hns3vf_set_promisc_mode(hw, true, true, true); + + return hns3vf_set_promisc_mode(hw, true, false, allmulti); +} + +static int +hns3vf_bind_ring_with_vector(struct hns3_hw *hw, uint8_t vector_id, + bool mmap, enum hns3_ring_type queue_type, + uint16_t queue_id) +{ + struct hns3_vf_bind_vector_msg bind_msg; + const char *op_str; + uint16_t code; + int ret; + + memset(&bind_msg, 0, sizeof(bind_msg)); + code = mmap ? HNS3_MBX_MAP_RING_TO_VECTOR : + HNS3_MBX_UNMAP_RING_TO_VECTOR; + bind_msg.vector_id = vector_id; + + if (queue_type == HNS3_RING_TYPE_RX) + bind_msg.param[0].int_gl_index = HNS3_RING_GL_RX; + else + bind_msg.param[0].int_gl_index = HNS3_RING_GL_TX; + + bind_msg.param[0].ring_type = queue_type; + bind_msg.ring_num = 1; + bind_msg.param[0].tqp_index = queue_id; + op_str = mmap ? "Map" : "Unmap"; + ret = hns3_send_mbx_msg(hw, code, 0, (uint8_t *)&bind_msg, + sizeof(bind_msg), false, NULL, 0); + if (ret) + hns3_err(hw, "%s TQP %d fail, vector_id is %d, ret is %d.", + op_str, queue_id, bind_msg.vector_id, ret); + + return ret; +} + +static int +hns3vf_init_ring_with_vector(struct hns3_hw *hw) +{ + uint8_t vec; + int ret; + int i; + + /* + * In hns3 network engine, vector 0 is always the misc interrupt of this + * function, vector 1~N can be used respectively for the queues of the + * function. Tx and Rx queues with the same number share the interrupt + * vector. In the initialization clearing the all hardware mapping + * relationship configurations between queues and interrupt vectors is + * needed, so some error caused by the residual configurations, such as + * the unexpected Tx interrupt, can be avoid. Because of the hardware + * constraints in hns3 hardware engine, we have to implement clearing + * the mapping relationship configurations by binding all queues to the + * last interrupt vector and reserving the last interrupt vector. This + * method results in a decrease of the maximum queues when upper + * applications call the rte_eth_dev_configure API function to enable + * Rx interrupt. + */ + vec = hw->num_msi - 1; /* vector 0 for misc interrupt, not for queue */ + /* vec - 1: the last interrupt is reserved */ + hw->intr_tqps_num = vec > hw->tqps_num ? hw->tqps_num : vec - 1; + for (i = 0; i < hw->intr_tqps_num; i++) { + /* + * Set gap limiter and rate limiter configuration of queue's + * interrupt. + */ + hns3_set_queue_intr_gl(hw, i, HNS3_RING_GL_RX, + HNS3_TQP_INTR_GL_DEFAULT); + hns3_set_queue_intr_gl(hw, i, HNS3_RING_GL_TX, + HNS3_TQP_INTR_GL_DEFAULT); + hns3_set_queue_intr_rl(hw, i, HNS3_TQP_INTR_RL_DEFAULT); + + ret = hns3vf_bind_ring_with_vector(hw, vec, false, + HNS3_RING_TYPE_TX, i); + if (ret) { + PMD_INIT_LOG(ERR, "VF fail to unbind TX ring(%d) with " + "vector: %d, ret=%d", i, vec, ret); + return ret; + } + + ret = hns3vf_bind_ring_with_vector(hw, vec, false, + HNS3_RING_TYPE_RX, i); + if (ret) { + PMD_INIT_LOG(ERR, "VF fail to unbind RX ring(%d) with " + "vector: %d, ret=%d", i, vec, ret); + return ret; + } + } + + return 0; +} + +static int +hns3vf_dev_configure(struct rte_eth_dev *dev) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + struct rte_eth_conf *conf = &dev->data->dev_conf; + enum rte_eth_rx_mq_mode mq_mode = conf->rxmode.mq_mode; + uint16_t nb_rx_q = dev->data->nb_rx_queues; + uint16_t nb_tx_q = dev->data->nb_tx_queues; + struct rte_eth_rss_conf rss_conf; + uint16_t mtu; + int ret; + + /* + * Hardware does not support individually enable/disable/reset the Tx or + * Rx queue in hns3 network engine. Driver must enable/disable/reset Tx + * and Rx queues at the same time. When the numbers of Tx queues + * allocated by upper applications are not equal to the numbers of Rx + * queues, driver needs to setup fake Tx or Rx queues to adjust numbers + * of Tx/Rx queues. otherwise, network engine can not work as usual. But + * these fake queues are imperceptible, and can not be used by upper + * applications. + */ + ret = hns3_set_fake_rx_or_tx_queues(dev, nb_rx_q, nb_tx_q); + if (ret) { + hns3_err(hw, "Failed to set rx/tx fake queues: %d", ret); + return ret; + } + + hw->adapter_state = HNS3_NIC_CONFIGURING; + if (conf->link_speeds & ETH_LINK_SPEED_FIXED) { + hns3_err(hw, "setting link speed/duplex not supported"); + ret = -EINVAL; + goto cfg_err; + } + + /* When RSS is not configured, redirect the packet queue 0 */ + if ((uint32_t)mq_mode & ETH_MQ_RX_RSS_FLAG) { + rss_conf = conf->rx_adv_conf.rss_conf; + if (rss_conf.rss_key == NULL) { + rss_conf.rss_key = rss_cfg->key; + rss_conf.rss_key_len = HNS3_RSS_KEY_SIZE; + } + + ret = hns3_dev_rss_hash_update(dev, &rss_conf); + if (ret) + goto cfg_err; + } + + /* + * If jumbo frames are enabled, MTU needs to be refreshed + * according to the maximum RX packet length. + */ + if (conf->rxmode.offloads & DEV_RX_OFFLOAD_JUMBO_FRAME) { + /* + * Security of max_rx_pkt_len is guaranteed in dpdk frame. + * Maximum value of max_rx_pkt_len is HNS3_MAX_FRAME_LEN, so it + * can safely assign to "uint16_t" type variable. + */ + mtu = (uint16_t)HNS3_PKTLEN_TO_MTU(conf->rxmode.max_rx_pkt_len); + ret = hns3vf_dev_mtu_set(dev, mtu); + if (ret) + goto cfg_err; + dev->data->mtu = mtu; + } + + ret = hns3vf_dev_configure_vlan(dev); + if (ret) + goto cfg_err; + + hw->adapter_state = HNS3_NIC_CONFIGURED; + return 0; + +cfg_err: + (void)hns3_set_fake_rx_or_tx_queues(dev, 0, 0); + hw->adapter_state = HNS3_NIC_INITIALIZED; + + return ret; +} + +static int +hns3vf_config_mtu(struct hns3_hw *hw, uint16_t mtu) +{ + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_MTU, 0, (const uint8_t *)&mtu, + sizeof(mtu), true, NULL, 0); + if (ret) + hns3_err(hw, "Failed to set mtu (%u) for vf: %d", mtu, ret); + + return ret; +} + +static int +hns3vf_dev_mtu_set(struct rte_eth_dev *dev, uint16_t mtu) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + uint32_t frame_size = mtu + HNS3_ETH_OVERHEAD; + int ret; + + /* + * The hns3 PF/VF devices on the same port share the hardware MTU + * configuration. Currently, we send mailbox to inform hns3 PF kernel + * ethdev driver to finish hardware MTU configuration in hns3 VF PMD + * driver, there is no need to stop the port for hns3 VF device, and the + * MTU value issued by hns3 VF PMD driver must be less than or equal to + * PF's MTU. + */ + if (rte_atomic16_read(&hw->reset.resetting)) { + hns3_err(hw, "Failed to set mtu during resetting"); + return -EIO; + } + + rte_spinlock_lock(&hw->lock); + ret = hns3vf_config_mtu(hw, mtu); + if (ret) { + rte_spinlock_unlock(&hw->lock); + return ret; + } + if (frame_size > RTE_ETHER_MAX_LEN) + dev->data->dev_conf.rxmode.offloads |= + DEV_RX_OFFLOAD_JUMBO_FRAME; + else + dev->data->dev_conf.rxmode.offloads &= + ~DEV_RX_OFFLOAD_JUMBO_FRAME; + dev->data->dev_conf.rxmode.max_rx_pkt_len = frame_size; + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3vf_dev_infos_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *info) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint16_t q_num = hw->tqps_num; + + /* + * In interrupt mode, 'max_rx_queues' is set based on the number of + * MSI-X interrupt resources of the hardware. + */ + if (hw->data->dev_conf.intr_conf.rxq == 1) + q_num = hw->intr_tqps_num; + + info->max_rx_queues = q_num; + info->max_tx_queues = hw->tqps_num; + info->max_rx_pktlen = HNS3_MAX_FRAME_LEN; /* CRC included */ + info->min_rx_bufsize = hw->rx_buf_len; + info->max_mac_addrs = HNS3_VF_UC_MACADDR_NUM; + info->max_mtu = info->max_rx_pktlen - HNS3_ETH_OVERHEAD; + + info->rx_offload_capa = (DEV_RX_OFFLOAD_IPV4_CKSUM | + DEV_RX_OFFLOAD_UDP_CKSUM | + DEV_RX_OFFLOAD_TCP_CKSUM | + DEV_RX_OFFLOAD_SCTP_CKSUM | + DEV_RX_OFFLOAD_OUTER_IPV4_CKSUM | + DEV_RX_OFFLOAD_OUTER_UDP_CKSUM | + DEV_RX_OFFLOAD_KEEP_CRC | + DEV_RX_OFFLOAD_SCATTER | + DEV_RX_OFFLOAD_VLAN_STRIP | + DEV_RX_OFFLOAD_QINQ_STRIP | + DEV_RX_OFFLOAD_VLAN_FILTER | + DEV_RX_OFFLOAD_JUMBO_FRAME | + DEV_RX_OFFLOAD_RSS_HASH); + info->tx_queue_offload_capa = DEV_TX_OFFLOAD_MBUF_FAST_FREE; + info->tx_offload_capa = (DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM | + DEV_TX_OFFLOAD_IPV4_CKSUM | + DEV_TX_OFFLOAD_TCP_CKSUM | + DEV_TX_OFFLOAD_UDP_CKSUM | + DEV_TX_OFFLOAD_SCTP_CKSUM | + DEV_TX_OFFLOAD_VLAN_INSERT | + DEV_TX_OFFLOAD_QINQ_INSERT | + DEV_TX_OFFLOAD_MULTI_SEGS | + DEV_TX_OFFLOAD_TCP_TSO | + DEV_TX_OFFLOAD_VXLAN_TNL_TSO | + DEV_TX_OFFLOAD_GRE_TNL_TSO | + DEV_TX_OFFLOAD_GENEVE_TNL_TSO | + info->tx_queue_offload_capa); + + info->rx_desc_lim = (struct rte_eth_desc_lim) { + .nb_max = HNS3_MAX_RING_DESC, + .nb_min = HNS3_MIN_RING_DESC, + .nb_align = HNS3_ALIGN_RING_DESC, + }; + + info->tx_desc_lim = (struct rte_eth_desc_lim) { + .nb_max = HNS3_MAX_RING_DESC, + .nb_min = HNS3_MIN_RING_DESC, + .nb_align = HNS3_ALIGN_RING_DESC, + }; + + info->vmdq_queue_num = 0; + + info->reta_size = HNS3_RSS_IND_TBL_SIZE; + info->hash_key_size = HNS3_RSS_KEY_SIZE; + info->flow_type_rss_offloads = HNS3_ETH_RSS_SUPPORT; + info->default_rxportconf.ring_size = HNS3_DEFAULT_RING_DESC; + info->default_txportconf.ring_size = HNS3_DEFAULT_RING_DESC; + + return 0; +} + +static void +hns3vf_clear_event_cause(struct hns3_hw *hw, uint32_t regclr) +{ + hns3_write_dev(hw, HNS3_VECTOR0_CMDQ_SRC_REG, regclr); +} + +static void +hns3vf_disable_irq0(struct hns3_hw *hw) +{ + hns3_write_dev(hw, HNS3_MISC_VECTOR_REG_BASE, 0); +} + +static void +hns3vf_enable_irq0(struct hns3_hw *hw) +{ + hns3_write_dev(hw, HNS3_MISC_VECTOR_REG_BASE, 1); +} + +static enum hns3vf_evt_cause +hns3vf_check_event_cause(struct hns3_adapter *hns, uint32_t *clearval) +{ + struct hns3_hw *hw = &hns->hw; + enum hns3vf_evt_cause ret; + uint32_t cmdq_stat_reg; + uint32_t rst_ing_reg; + uint32_t val; + + /* Fetch the events from their corresponding regs */ + cmdq_stat_reg = hns3_read_dev(hw, HNS3_VECTOR0_CMDQ_STAT_REG); + + if (BIT(HNS3_VECTOR0_RST_INT_B) & cmdq_stat_reg) { + rst_ing_reg = hns3_read_dev(hw, HNS3_FUN_RST_ING); + hns3_warn(hw, "resetting reg: 0x%x", rst_ing_reg); + hns3_atomic_set_bit(HNS3_VF_RESET, &hw->reset.pending); + rte_atomic16_set(&hw->reset.disable_cmd, 1); + val = hns3_read_dev(hw, HNS3_VF_RST_ING); + hns3_write_dev(hw, HNS3_VF_RST_ING, val | HNS3_VF_RST_ING_BIT); + val = cmdq_stat_reg & ~BIT(HNS3_VECTOR0_RST_INT_B); + if (clearval) { + hw->reset.stats.global_cnt++; + hns3_warn(hw, "Global reset detected, clear reset status"); + } else { + hns3_schedule_delayed_reset(hns); + hns3_warn(hw, "Global reset detected, don't clear reset status"); + } + + ret = HNS3VF_VECTOR0_EVENT_RST; + goto out; + } + + /* Check for vector0 mailbox(=CMDQ RX) event source */ + if (BIT(HNS3_VECTOR0_RX_CMDQ_INT_B) & cmdq_stat_reg) { + val = cmdq_stat_reg & ~BIT(HNS3_VECTOR0_RX_CMDQ_INT_B); + ret = HNS3VF_VECTOR0_EVENT_MBX; + goto out; + } + + val = 0; + ret = HNS3VF_VECTOR0_EVENT_OTHER; +out: + if (clearval) + *clearval = val; + return ret; +} + +static void +hns3vf_interrupt_handler(void *param) +{ + struct rte_eth_dev *dev = (struct rte_eth_dev *)param; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + enum hns3vf_evt_cause event_cause; + uint32_t clearval; + + if (hw->irq_thread_id == 0) + hw->irq_thread_id = pthread_self(); + + /* Disable interrupt */ + hns3vf_disable_irq0(hw); + + /* Read out interrupt causes */ + event_cause = hns3vf_check_event_cause(hns, &clearval); + + switch (event_cause) { + case HNS3VF_VECTOR0_EVENT_RST: + hns3_schedule_reset(hns); + break; + case HNS3VF_VECTOR0_EVENT_MBX: + hns3_dev_handle_mbx_msg(hw); + break; + default: + break; + } + + /* Clear interrupt causes */ + hns3vf_clear_event_cause(hw, clearval); + + /* Enable interrupt */ + hns3vf_enable_irq0(hw); +} + +static int +hns3vf_check_tqp_info(struct hns3_hw *hw) +{ + uint16_t tqps_num; + + tqps_num = hw->tqps_num; + if (tqps_num > HNS3_MAX_TQP_NUM_PER_FUNC || tqps_num == 0) { + PMD_INIT_LOG(ERR, "Get invalid tqps_num(%u) from PF. valid " + "range: 1~%d", + tqps_num, HNS3_MAX_TQP_NUM_PER_FUNC); + return -EINVAL; + } + + if (hw->rx_buf_len == 0) + hw->rx_buf_len = HNS3_DEFAULT_RX_BUF_LEN; + hw->alloc_rss_size = RTE_MIN(hw->rss_size_max, hw->tqps_num); + + return 0; +} + +static int +hns3vf_get_queue_info(struct hns3_hw *hw) +{ +#define HNS3VF_TQPS_RSS_INFO_LEN 6 + uint8_t resp_msg[HNS3VF_TQPS_RSS_INFO_LEN]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_GET_QINFO, 0, NULL, 0, true, + resp_msg, HNS3VF_TQPS_RSS_INFO_LEN); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to get tqp info from PF: %d", ret); + return ret; + } + + memcpy(&hw->tqps_num, &resp_msg[0], sizeof(uint16_t)); + memcpy(&hw->rss_size_max, &resp_msg[2], sizeof(uint16_t)); + memcpy(&hw->rx_buf_len, &resp_msg[4], sizeof(uint16_t)); + + return hns3vf_check_tqp_info(hw); +} + +static int +hns3vf_get_queue_depth(struct hns3_hw *hw) +{ +#define HNS3VF_TQPS_DEPTH_INFO_LEN 4 + uint8_t resp_msg[HNS3VF_TQPS_DEPTH_INFO_LEN]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_GET_QDEPTH, 0, NULL, 0, true, + resp_msg, HNS3VF_TQPS_DEPTH_INFO_LEN); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to get tqp depth info from PF: %d", + ret); + return ret; + } + + memcpy(&hw->num_tx_desc, &resp_msg[0], sizeof(uint16_t)); + memcpy(&hw->num_rx_desc, &resp_msg[2], sizeof(uint16_t)); + + return 0; +} + +static int +hns3vf_get_tc_info(struct hns3_hw *hw) +{ + uint8_t resp_msg; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_GET_TCINFO, 0, NULL, 0, + true, &resp_msg, sizeof(resp_msg)); + if (ret) { + hns3_err(hw, "VF request to get TC info from PF failed %d", + ret); + return ret; + } + + hw->hw_tc_map = resp_msg; + + return 0; +} + +static int +hns3vf_get_host_mac_addr(struct hns3_hw *hw) +{ + uint8_t host_mac[RTE_ETHER_ADDR_LEN]; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_GET_MAC_ADDR, 0, NULL, 0, + true, host_mac, RTE_ETHER_ADDR_LEN); + if (ret) { + hns3_err(hw, "Failed to get mac addr from PF: %d", ret); + return ret; + } + + memcpy(hw->mac.mac_addr, host_mac, RTE_ETHER_ADDR_LEN); + + return 0; +} + +static int +hns3vf_get_configuration(struct hns3_hw *hw) +{ + int ret; + + hw->mac.media_type = HNS3_MEDIA_TYPE_NONE; + hw->rss_dis_flag = false; + + /* Get queue configuration from PF */ + ret = hns3vf_get_queue_info(hw); + if (ret) + return ret; + + /* Get queue depth info from PF */ + ret = hns3vf_get_queue_depth(hw); + if (ret) + return ret; + + /* Get user defined VF MAC addr from PF */ + ret = hns3vf_get_host_mac_addr(hw); + if (ret) + return ret; + + /* Get tc configuration from PF */ + return hns3vf_get_tc_info(hw); +} + +static int +hns3vf_set_tc_info(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + uint16_t nb_rx_q = hw->data->nb_rx_queues; + uint16_t nb_tx_q = hw->data->nb_tx_queues; + uint8_t i; + + hw->num_tc = 0; + for (i = 0; i < HNS3_MAX_TC_NUM; i++) + if (hw->hw_tc_map & BIT(i)) + hw->num_tc++; + + if (nb_rx_q < hw->num_tc) { + hns3_err(hw, "number of Rx queues(%d) is less than tcs(%d).", + nb_rx_q, hw->num_tc); + return -EINVAL; + } + + if (nb_tx_q < hw->num_tc) { + hns3_err(hw, "number of Tx queues(%d) is less than tcs(%d).", + nb_tx_q, hw->num_tc); + return -EINVAL; + } + + hns3_set_rss_size(hw, nb_rx_q); + hns3_tc_queue_mapping_cfg(hw, nb_tx_q); + + return 0; +} + +static void +hns3vf_request_link_info(struct hns3_hw *hw) +{ + uint8_t resp_msg; + int ret; + + if (rte_atomic16_read(&hw->reset.resetting)) + return; + ret = hns3_send_mbx_msg(hw, HNS3_MBX_GET_LINK_STATUS, 0, NULL, 0, false, + &resp_msg, sizeof(resp_msg)); + if (ret) + hns3_err(hw, "Failed to fetch link status from PF: %d", ret); +} + +static int +hns3vf_vlan_filter_configure(struct hns3_adapter *hns, uint16_t vlan_id, int on) +{ +#define HNS3VF_VLAN_MBX_MSG_LEN 5 + struct hns3_hw *hw = &hns->hw; + uint8_t msg_data[HNS3VF_VLAN_MBX_MSG_LEN]; + uint16_t proto = htons(RTE_ETHER_TYPE_VLAN); + uint8_t is_kill = on ? 0 : 1; + + msg_data[0] = is_kill; + memcpy(&msg_data[1], &vlan_id, sizeof(vlan_id)); + memcpy(&msg_data[3], &proto, sizeof(proto)); + + return hns3_send_mbx_msg(hw, HNS3_MBX_SET_VLAN, HNS3_MBX_VLAN_FILTER, + msg_data, HNS3VF_VLAN_MBX_MSG_LEN, true, NULL, + 0); +} + +static int +hns3vf_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (rte_atomic16_read(&hw->reset.resetting)) { + hns3_err(hw, + "vf set vlan id failed during resetting, vlan_id =%u", + vlan_id); + return -EIO; + } + rte_spinlock_lock(&hw->lock); + ret = hns3vf_vlan_filter_configure(hns, vlan_id, on); + rte_spinlock_unlock(&hw->lock); + if (ret) + hns3_err(hw, "vf set vlan id failed, vlan_id =%u, ret =%d", + vlan_id, ret); + + return ret; +} + +static int +hns3vf_en_hw_strip_rxvtag(struct hns3_hw *hw, bool enable) +{ + uint8_t msg_data; + int ret; + + msg_data = enable ? 1 : 0; + ret = hns3_send_mbx_msg(hw, HNS3_MBX_SET_VLAN, HNS3_MBX_VLAN_RX_OFF_CFG, + &msg_data, sizeof(msg_data), false, NULL, 0); + if (ret) + hns3_err(hw, "vf enable strip failed, ret =%d", ret); + + return ret; +} + +static int +hns3vf_vlan_offload_set(struct rte_eth_dev *dev, int mask) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_eth_conf *dev_conf = &dev->data->dev_conf; + unsigned int tmp_mask; + int ret = 0; + + if (rte_atomic16_read(&hw->reset.resetting)) { + hns3_err(hw, "vf set vlan offload failed during resetting, " + "mask = 0x%x", mask); + return -EIO; + } + + tmp_mask = (unsigned int)mask; + /* Vlan stripping setting */ + if (tmp_mask & ETH_VLAN_STRIP_MASK) { + rte_spinlock_lock(&hw->lock); + /* Enable or disable VLAN stripping */ + if (dev_conf->rxmode.offloads & DEV_RX_OFFLOAD_VLAN_STRIP) + ret = hns3vf_en_hw_strip_rxvtag(hw, true); + else + ret = hns3vf_en_hw_strip_rxvtag(hw, false); + rte_spinlock_unlock(&hw->lock); + } + + return ret; +} + +static int +hns3vf_handle_all_vlan_table(struct hns3_adapter *hns, int on) +{ + struct rte_vlan_filter_conf *vfc; + struct hns3_hw *hw = &hns->hw; + uint16_t vlan_id; + uint64_t vbit; + uint64_t ids; + int ret = 0; + uint32_t i; + + vfc = &hw->data->vlan_filter_conf; + for (i = 0; i < RTE_DIM(vfc->ids); i++) { + if (vfc->ids[i] == 0) + continue; + ids = vfc->ids[i]; + while (ids) { + /* + * 64 means the num bits of ids, one bit corresponds to + * one vlan id + */ + vlan_id = 64 * i; + /* count trailing zeroes */ + vbit = ~ids & (ids - 1); + /* clear least significant bit set */ + ids ^= (ids ^ (ids - 1)) ^ vbit; + for (; vbit;) { + vbit >>= 1; + vlan_id++; + } + ret = hns3vf_vlan_filter_configure(hns, vlan_id, on); + if (ret) { + hns3_err(hw, + "VF handle vlan table failed, ret =%d, on = %d", + ret, on); + return ret; + } + } + } + + return ret; +} + +static int +hns3vf_remove_all_vlan_table(struct hns3_adapter *hns) +{ + return hns3vf_handle_all_vlan_table(hns, 0); +} + +static int +hns3vf_restore_vlan_conf(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct rte_eth_conf *dev_conf; + bool en; + int ret; + + dev_conf = &hw->data->dev_conf; + en = dev_conf->rxmode.offloads & DEV_RX_OFFLOAD_VLAN_STRIP ? true + : false; + ret = hns3vf_en_hw_strip_rxvtag(hw, en); + if (ret) + hns3_err(hw, "VF restore vlan conf fail, en =%d, ret =%d", en, + ret); + return ret; +} + +static int +hns3vf_dev_configure_vlan(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct rte_eth_dev_data *data = dev->data; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (data->dev_conf.txmode.hw_vlan_reject_tagged || + data->dev_conf.txmode.hw_vlan_reject_untagged || + data->dev_conf.txmode.hw_vlan_insert_pvid) { + hns3_warn(hw, "hw_vlan_reject_tagged, hw_vlan_reject_untagged " + "or hw_vlan_insert_pvid is not support!"); + } + + /* Apply vlan offload setting */ + ret = hns3vf_vlan_offload_set(dev, ETH_VLAN_STRIP_MASK); + if (ret) + hns3_err(hw, "dev config vlan offload failed, ret =%d", ret); + + return ret; +} + +static int +hns3vf_set_alive(struct hns3_hw *hw, bool alive) +{ + uint8_t msg_data; + + msg_data = alive ? 1 : 0; + return hns3_send_mbx_msg(hw, HNS3_MBX_SET_ALIVE, 0, &msg_data, + sizeof(msg_data), false, NULL, 0); +} + +static void +hns3vf_keep_alive_handler(void *param) +{ + struct rte_eth_dev *eth_dev = (struct rte_eth_dev *)param; + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint8_t respmsg; + int ret; + + ret = hns3_send_mbx_msg(hw, HNS3_MBX_KEEP_ALIVE, 0, NULL, 0, + false, &respmsg, sizeof(uint8_t)); + if (ret) + hns3_err(hw, "VF sends keeping alive cmd failed(=%d)", + ret); + + rte_eal_alarm_set(HNS3VF_KEEP_ALIVE_INTERVAL, hns3vf_keep_alive_handler, + eth_dev); +} + +static void +hns3vf_service_handler(void *param) +{ + struct rte_eth_dev *eth_dev = (struct rte_eth_dev *)param; + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + /* + * The query link status and reset processing are executed in the + * interrupt thread.When the IMP reset occurs, IMP will not respond, + * and the query operation will time out after 30ms. In the case of + * multiple PF/VFs, each query failure timeout causes the IMP reset + * interrupt to fail to respond within 100ms. + * Before querying the link status, check whether there is a reset + * pending, and if so, abandon the query. + */ + if (!hns3vf_is_reset_pending(hns)) + hns3vf_request_link_info(hw); + else + hns3_warn(hw, "Cancel the query when reset is pending"); + + rte_eal_alarm_set(HNS3VF_SERVICE_INTERVAL, hns3vf_service_handler, + eth_dev); +} + +static int +hns3_query_vf_resource(struct hns3_hw *hw) +{ + struct hns3_vf_res_cmd *req; + struct hns3_cmd_desc desc; + uint16_t num_msi; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_VF_RSRC, true); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "query vf resource failed, ret = %d", ret); + return ret; + } + + req = (struct hns3_vf_res_cmd *)desc.data; + num_msi = hns3_get_field(rte_le_to_cpu_16(req->vf_intr_vector_number), + HNS3_VEC_NUM_M, HNS3_VEC_NUM_S); + if (num_msi < HNS3_MIN_VECTOR_NUM) { + hns3_err(hw, "Just %u msi resources, not enough for vf(min:%d)", + num_msi, HNS3_MIN_VECTOR_NUM); + return -EINVAL; + } + + hw->num_msi = num_msi; + + return 0; +} + +static int +hns3vf_init_hardware(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + uint16_t mtu = hw->data->mtu; + int ret; + + ret = hns3vf_set_promisc_mode(hw, true, false, false); + if (ret) + return ret; + + ret = hns3vf_config_mtu(hw, mtu); + if (ret) + goto err_init_hardware; + + ret = hns3vf_vlan_filter_configure(hns, 0, 1); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to initialize VLAN config: %d", ret); + goto err_init_hardware; + } + + ret = hns3_config_gro(hw, false); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to config gro: %d", ret); + goto err_init_hardware; + } + + /* + * In the initialization clearing the all hardware mapping relationship + * configurations between queues and interrupt vectors is needed, so + * some error caused by the residual configurations, such as the + * unexpected interrupt, can be avoid. + */ + ret = hns3vf_init_ring_with_vector(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init ring intr vector: %d", ret); + goto err_init_hardware; + } + + ret = hns3vf_set_alive(hw, true); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to VF send alive to PF: %d", ret); + goto err_init_hardware; + } + + hns3vf_request_link_info(hw); + return 0; + +err_init_hardware: + (void)hns3vf_set_promisc_mode(hw, false, false, false); + return ret; +} + +static int +hns3vf_clear_vport_list(struct hns3_hw *hw) +{ + return hns3_send_mbx_msg(hw, HNS3_MBX_HANDLE_VF_TBL, + HNS3_MBX_VPORT_LIST_CLEAR, NULL, 0, false, + NULL, 0); +} + +static int +hns3vf_init_vf(struct rte_eth_dev *eth_dev) +{ + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev); + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + PMD_INIT_FUNC_TRACE(); + + /* Get hardware io base address from pcie BAR2 IO space */ + hw->io_base = pci_dev->mem_resource[2].addr; + + /* Firmware command queue initialize */ + ret = hns3_cmd_init_queue(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init cmd queue: %d", ret); + goto err_cmd_init_queue; + } + + /* Firmware command initialize */ + ret = hns3_cmd_init(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init cmd: %d", ret); + goto err_cmd_init; + } + + /* Get VF resource */ + ret = hns3_query_vf_resource(hw); + if (ret) + goto err_cmd_init; + + rte_spinlock_init(&hw->mbx_resp.lock); + + hns3vf_clear_event_cause(hw, 0); + + ret = rte_intr_callback_register(&pci_dev->intr_handle, + hns3vf_interrupt_handler, eth_dev); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to register intr: %d", ret); + goto err_intr_callback_register; + } + + /* Enable interrupt */ + rte_intr_enable(&pci_dev->intr_handle); + hns3vf_enable_irq0(hw); + + /* Get configuration from PF */ + ret = hns3vf_get_configuration(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to fetch configuration: %d", ret); + goto err_get_config; + } + + /* + * The hns3 PF ethdev driver in kernel support setting VF MAC address + * on the host by "ip link set ..." command. To avoid some incorrect + * scenes, for example, hns3 VF PMD driver fails to receive and send + * packets after user configure the MAC address by using the + * "ip link set ..." command, hns3 VF PMD driver keep the same MAC + * address strategy as the hns3 kernel ethdev driver in the + * initialization. If user configure a MAC address by the ip command + * for VF device, then hns3 VF PMD driver will start with it, otherwise + * start with a random MAC address in the initialization. + */ + ret = rte_is_zero_ether_addr((struct rte_ether_addr *)hw->mac.mac_addr); + if (ret) + rte_eth_random_addr(hw->mac.mac_addr); + + ret = hns3vf_clear_vport_list(hw); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to clear tbl list: %d", ret); + goto err_get_config; + } + + ret = hns3vf_init_hardware(hns); + if (ret) + goto err_get_config; + + hns3_set_default_rss_args(hw); + + return 0; + +err_get_config: + hns3vf_disable_irq0(hw); + rte_intr_disable(&pci_dev->intr_handle); + hns3_intr_unregister(&pci_dev->intr_handle, hns3vf_interrupt_handler, + eth_dev); +err_intr_callback_register: +err_cmd_init: + hns3_cmd_uninit(hw); + hns3_cmd_destroy_queue(hw); +err_cmd_init_queue: + hw->io_base = NULL; + + return ret; +} + +static void +hns3vf_uninit_vf(struct rte_eth_dev *eth_dev) +{ + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev); + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + PMD_INIT_FUNC_TRACE(); + + hns3_rss_uninit(hns); + (void)hns3vf_set_alive(hw, false); + (void)hns3vf_set_promisc_mode(hw, false, false, false); + hns3vf_disable_irq0(hw); + rte_intr_disable(&pci_dev->intr_handle); + hns3_intr_unregister(&pci_dev->intr_handle, hns3vf_interrupt_handler, + eth_dev); + hns3_cmd_uninit(hw); + hns3_cmd_destroy_queue(hw); + hw->io_base = NULL; +} + +static int +hns3vf_do_stop(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + bool reset_queue; + + hw->mac.link_status = ETH_LINK_DOWN; + + if (rte_atomic16_read(&hw->reset.disable_cmd) == 0) { + hns3vf_configure_mac_addr(hns, true); + reset_queue = true; + } else + reset_queue = false; + return hns3_stop_queues(hns, reset_queue); +} + +static void +hns3vf_unmap_rx_interrupt(struct rte_eth_dev *dev) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + uint8_t base = RTE_INTR_VEC_ZERO_OFFSET; + uint8_t vec = RTE_INTR_VEC_ZERO_OFFSET; + uint16_t q_id; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return; + + /* unmap the ring with vector */ + if (rte_intr_allow_others(intr_handle)) { + vec = RTE_INTR_VEC_RXTX_OFFSET; + base = RTE_INTR_VEC_RXTX_OFFSET; + } + if (rte_intr_dp_is_en(intr_handle)) { + for (q_id = 0; q_id < hw->used_rx_queues; q_id++) { + (void)hns3vf_bind_ring_with_vector(hw, vec, false, + HNS3_RING_TYPE_RX, + q_id); + if (vec < base + intr_handle->nb_efd - 1) + vec++; + } + } + /* Clean datapath event and queue/vec mapping */ + rte_intr_efd_disable(intr_handle); + if (intr_handle->intr_vec) { + rte_free(intr_handle->intr_vec); + intr_handle->intr_vec = NULL; + } +} + +static void +hns3vf_dev_stop(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + PMD_INIT_FUNC_TRACE(); + + hw->adapter_state = HNS3_NIC_STOPPING; + hns3_set_rxtx_function(dev); + rte_wmb(); + /* Disable datapath on secondary process. */ + hns3_mp_req_stop_rxtx(dev); + /* Prevent crashes when queues are still in use. */ + rte_delay_ms(hw->tqps_num); + + rte_spinlock_lock(&hw->lock); + if (rte_atomic16_read(&hw->reset.resetting) == 0) { + hns3vf_do_stop(hns); + hns3vf_unmap_rx_interrupt(dev); + hns3_dev_release_mbufs(hns); + hw->adapter_state = HNS3_NIC_CONFIGURED; + } + rte_eal_alarm_cancel(hns3vf_service_handler, dev); + rte_spinlock_unlock(&hw->lock); +} + +static void +hns3vf_dev_close(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return; + + if (hw->adapter_state == HNS3_NIC_STARTED) + hns3vf_dev_stop(eth_dev); + + hw->adapter_state = HNS3_NIC_CLOSING; + hns3_reset_abort(hns); + hw->adapter_state = HNS3_NIC_CLOSED; + rte_eal_alarm_cancel(hns3vf_keep_alive_handler, eth_dev); + hns3vf_configure_all_mc_mac_addr(hns, true); + hns3vf_remove_all_vlan_table(hns); + hns3vf_uninit_vf(eth_dev); + hns3_free_all_queues(eth_dev); + rte_free(hw->reset.wait_data); + rte_free(eth_dev->process_private); + eth_dev->process_private = NULL; + hns3_mp_uninit_primary(); + hns3_warn(hw, "Close port %d finished", hw->data->port_id); +} + +static int +hns3vf_fw_version_get(struct rte_eth_dev *eth_dev, char *fw_version, + size_t fw_size) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint32_t version = hw->fw_version; + int ret; + + ret = snprintf(fw_version, fw_size, "%lu.%lu.%lu.%lu", + hns3_get_field(version, HNS3_FW_VERSION_BYTE3_M, + HNS3_FW_VERSION_BYTE3_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE2_M, + HNS3_FW_VERSION_BYTE2_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE1_M, + HNS3_FW_VERSION_BYTE1_S), + hns3_get_field(version, HNS3_FW_VERSION_BYTE0_M, + HNS3_FW_VERSION_BYTE0_S)); + ret += 1; /* add the size of '\0' */ + if (fw_size < (uint32_t)ret) + return ret; + else + return 0; +} + +static int +hns3vf_dev_link_update(struct rte_eth_dev *eth_dev, + __rte_unused int wait_to_complete) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_mac *mac = &hw->mac; + struct rte_eth_link new_link; + + memset(&new_link, 0, sizeof(new_link)); + switch (mac->link_speed) { + case ETH_SPEED_NUM_10M: + case ETH_SPEED_NUM_100M: + case ETH_SPEED_NUM_1G: + case ETH_SPEED_NUM_10G: + case ETH_SPEED_NUM_25G: + case ETH_SPEED_NUM_40G: + case ETH_SPEED_NUM_50G: + case ETH_SPEED_NUM_100G: + new_link.link_speed = mac->link_speed; + break; + default: + new_link.link_speed = ETH_SPEED_NUM_100M; + break; + } + + new_link.link_duplex = mac->link_duplex; + new_link.link_status = mac->link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + new_link.link_autoneg = + !(eth_dev->data->dev_conf.link_speeds & ETH_LINK_SPEED_FIXED); + + return rte_eth_linkstatus_set(eth_dev, &new_link); +} + +static int +hns3vf_do_start(struct hns3_adapter *hns, bool reset_queue) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3vf_set_tc_info(hns); + if (ret) + return ret; + + ret = hns3_start_queues(hns, reset_queue); + if (ret) + hns3_err(hw, "Failed to start queues: %d", ret); + + return ret; +} + +static int +hns3vf_map_rx_interrupt(struct rte_eth_dev *dev) +{ + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + uint8_t base = RTE_INTR_VEC_ZERO_OFFSET; + uint8_t vec = RTE_INTR_VEC_ZERO_OFFSET; + uint32_t intr_vector; + uint16_t q_id; + int ret; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return 0; + + /* disable uio/vfio intr/eventfd mapping */ + rte_intr_disable(intr_handle); + + /* check and configure queue intr-vector mapping */ + if (rte_intr_cap_multiple(intr_handle) || + !RTE_ETH_DEV_SRIOV(dev).active) { + intr_vector = hw->used_rx_queues; + /* It creates event fd for each intr vector when MSIX is used */ + if (rte_intr_efd_enable(intr_handle, intr_vector)) + return -EINVAL; + } + if (rte_intr_dp_is_en(intr_handle) && !intr_handle->intr_vec) { + intr_handle->intr_vec = + rte_zmalloc("intr_vec", + hw->used_rx_queues * sizeof(int), 0); + if (intr_handle->intr_vec == NULL) { + hns3_err(hw, "Failed to allocate %d rx_queues" + " intr_vec", hw->used_rx_queues); + ret = -ENOMEM; + goto vf_alloc_intr_vec_error; + } + } + + if (rte_intr_allow_others(intr_handle)) { + vec = RTE_INTR_VEC_RXTX_OFFSET; + base = RTE_INTR_VEC_RXTX_OFFSET; + } + if (rte_intr_dp_is_en(intr_handle)) { + for (q_id = 0; q_id < hw->used_rx_queues; q_id++) { + ret = hns3vf_bind_ring_with_vector(hw, vec, true, + HNS3_RING_TYPE_RX, + q_id); + if (ret) + goto vf_bind_vector_error; + intr_handle->intr_vec[q_id] = vec; + if (vec < base + intr_handle->nb_efd - 1) + vec++; + } + } + rte_intr_enable(intr_handle); + return 0; + +vf_bind_vector_error: + rte_intr_efd_disable(intr_handle); + if (intr_handle->intr_vec) { + free(intr_handle->intr_vec); + intr_handle->intr_vec = NULL; + } + return ret; +vf_alloc_intr_vec_error: + rte_intr_efd_disable(intr_handle); + return ret; +} + +static int +hns3vf_restore_rx_interrupt(struct hns3_hw *hw) +{ + struct rte_eth_dev *dev = &rte_eth_devices[hw->data->port_id]; + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + uint16_t q_id; + int ret; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return 0; + + if (rte_intr_dp_is_en(intr_handle)) { + for (q_id = 0; q_id < hw->used_rx_queues; q_id++) { + ret = hns3vf_bind_ring_with_vector(hw, + intr_handle->intr_vec[q_id], true, + HNS3_RING_TYPE_RX, q_id); + if (ret) + return ret; + } + } + + return 0; +} + +static void +hns3vf_restore_filter(struct rte_eth_dev *dev) +{ + hns3_restore_rss_filter(dev); +} + +static int +hns3vf_dev_start(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + int ret; + + PMD_INIT_FUNC_TRACE(); + if (rte_atomic16_read(&hw->reset.resetting)) + return -EBUSY; + + rte_spinlock_lock(&hw->lock); + hw->adapter_state = HNS3_NIC_STARTING; + ret = hns3vf_do_start(hns, true); + if (ret) { + hw->adapter_state = HNS3_NIC_CONFIGURED; + rte_spinlock_unlock(&hw->lock); + return ret; + } + ret = hns3vf_map_rx_interrupt(dev); + if (ret) { + hw->adapter_state = HNS3_NIC_CONFIGURED; + rte_spinlock_unlock(&hw->lock); + return ret; + } + hw->adapter_state = HNS3_NIC_STARTED; + rte_spinlock_unlock(&hw->lock); + + hns3_set_rxtx_function(dev); + hns3_mp_req_start_rxtx(dev); + rte_eal_alarm_set(HNS3VF_SERVICE_INTERVAL, hns3vf_service_handler, dev); + + hns3vf_restore_filter(dev); + + /* Enable interrupt of all rx queues before enabling queues */ + hns3_dev_all_rx_queue_intr_enable(hw, true); + /* + * When finished the initialization, enable queues to receive/transmit + * packets. + */ + hns3_enable_all_queues(hw, true); + + return ret; +} + +static bool +is_vf_reset_done(struct hns3_hw *hw) +{ +#define HNS3_FUN_RST_ING_BITS \ + (BIT(HNS3_VECTOR0_GLOBALRESET_INT_B) | \ + BIT(HNS3_VECTOR0_CORERESET_INT_B) | \ + BIT(HNS3_VECTOR0_IMPRESET_INT_B) | \ + BIT(HNS3_VECTOR0_FUNCRESET_INT_B)) + + uint32_t val; + + if (hw->reset.level == HNS3_VF_RESET) { + val = hns3_read_dev(hw, HNS3_VF_RST_ING); + if (val & HNS3_VF_RST_ING_BIT) + return false; + } else { + val = hns3_read_dev(hw, HNS3_FUN_RST_ING); + if (val & HNS3_FUN_RST_ING_BITS) + return false; + } + return true; +} + +bool +hns3vf_is_reset_pending(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + enum hns3_reset_level reset; + + hns3vf_check_event_cause(hns, NULL); + reset = hns3vf_get_reset_level(hw, &hw->reset.pending); + if (hw->reset.level != HNS3_NONE_RESET && hw->reset.level < reset) { + hns3_warn(hw, "High level reset %d is pending", reset); + return true; + } + return false; +} + +static int +hns3vf_wait_hardware_ready(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_wait_data *wait_data = hw->reset.wait_data; + struct timeval tv; + + if (wait_data->result == HNS3_WAIT_SUCCESS) { + /* + * After vf reset is ready, the PF may not have completed + * the reset processing. The vf sending mbox to PF may fail + * during the pf reset, so it is better to add extra delay. + */ + if (hw->reset.level == HNS3_VF_FUNC_RESET || + hw->reset.level == HNS3_FLR_RESET) + return 0; + /* Reset retry process, no need to add extra delay. */ + if (hw->reset.attempts) + return 0; + if (wait_data->check_completion == NULL) + return 0; + + wait_data->check_completion = NULL; + wait_data->interval = 1 * MSEC_PER_SEC * USEC_PER_MSEC; + wait_data->count = 1; + wait_data->result = HNS3_WAIT_REQUEST; + rte_eal_alarm_set(wait_data->interval, hns3_wait_callback, + wait_data); + hns3_warn(hw, "hardware is ready, delay 1 sec for PF reset complete"); + return -EAGAIN; + } else if (wait_data->result == HNS3_WAIT_TIMEOUT) { + gettimeofday(&tv, NULL); + hns3_warn(hw, "Reset step4 hardware not ready after reset time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + return -ETIME; + } else if (wait_data->result == HNS3_WAIT_REQUEST) + return -EAGAIN; + + wait_data->hns = hns; + wait_data->check_completion = is_vf_reset_done; + wait_data->end_ms = (uint64_t)HNS3VF_RESET_WAIT_CNT * + HNS3VF_RESET_WAIT_MS + get_timeofday_ms(); + wait_data->interval = HNS3VF_RESET_WAIT_MS * USEC_PER_MSEC; + wait_data->count = HNS3VF_RESET_WAIT_CNT; + wait_data->result = HNS3_WAIT_REQUEST; + rte_eal_alarm_set(wait_data->interval, hns3_wait_callback, wait_data); + return -EAGAIN; +} + +static int +hns3vf_prepare_reset(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret = 0; + + if (hw->reset.level == HNS3_VF_FUNC_RESET) { + ret = hns3_send_mbx_msg(hw, HNS3_MBX_RESET, 0, NULL, + 0, true, NULL, 0); + } + rte_atomic16_set(&hw->reset.disable_cmd, 1); + + return ret; +} + +static int +hns3vf_stop_service(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct rte_eth_dev *eth_dev; + + eth_dev = &rte_eth_devices[hw->data->port_id]; + if (hw->adapter_state == HNS3_NIC_STARTED) + rte_eal_alarm_cancel(hns3vf_service_handler, eth_dev); + hw->mac.link_status = ETH_LINK_DOWN; + + hns3_set_rxtx_function(eth_dev); + rte_wmb(); + /* Disable datapath on secondary process. */ + hns3_mp_req_stop_rxtx(eth_dev); + rte_delay_ms(hw->tqps_num); + + rte_spinlock_lock(&hw->lock); + if (hw->adapter_state == HNS3_NIC_STARTED || + hw->adapter_state == HNS3_NIC_STOPPING) { + hns3vf_do_stop(hns); + hw->reset.mbuf_deferred_free = true; + } else + hw->reset.mbuf_deferred_free = false; + + /* + * It is cumbersome for hardware to pick-and-choose entries for deletion + * from table space. Hence, for function reset software intervention is + * required to delete the entries. + */ + if (rte_atomic16_read(&hw->reset.disable_cmd) == 0) + hns3vf_configure_all_mc_mac_addr(hns, true); + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3vf_start_service(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct rte_eth_dev *eth_dev; + + eth_dev = &rte_eth_devices[hw->data->port_id]; + hns3_set_rxtx_function(eth_dev); + hns3_mp_req_start_rxtx(eth_dev); + if (hw->adapter_state == HNS3_NIC_STARTED) { + hns3vf_service_handler(eth_dev); + + /* Enable interrupt of all rx queues before enabling queues */ + hns3_dev_all_rx_queue_intr_enable(hw, true); + /* + * When finished the initialization, enable queues to receive + * and transmit packets. + */ + hns3_enable_all_queues(hw, true); + } + + return 0; +} + +static int +hns3vf_check_default_mac_change(struct hns3_hw *hw) +{ + char mac_str[RTE_ETHER_ADDR_FMT_SIZE]; + struct rte_ether_addr *hw_mac; + int ret; + + /* + * The hns3 PF ethdev driver in kernel support setting VF MAC address + * on the host by "ip link set ..." command. If the hns3 PF kernel + * ethdev driver sets the MAC address for VF device after the + * initialization of the related VF device, the PF driver will notify + * VF driver to reset VF device to make the new MAC address effective + * immediately. The hns3 VF PMD driver should check whether the MAC + * address has been changed by the PF kernel ethdev driver, if changed + * VF driver should configure hardware using the new MAC address in the + * recovering hardware configuration stage of the reset process. + */ + ret = hns3vf_get_host_mac_addr(hw); + if (ret) + return ret; + + hw_mac = (struct rte_ether_addr *)hw->mac.mac_addr; + ret = rte_is_zero_ether_addr(hw_mac); + if (ret) { + rte_ether_addr_copy(&hw->data->mac_addrs[0], hw_mac); + } else { + ret = rte_is_same_ether_addr(&hw->data->mac_addrs[0], hw_mac); + if (!ret) { + rte_ether_addr_copy(hw_mac, &hw->data->mac_addrs[0]); + rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE, + &hw->data->mac_addrs[0]); + hns3_warn(hw, "Default MAC address has been changed to:" + " %s by the host PF kernel ethdev driver", + mac_str); + } + } + + return 0; +} + +static int +hns3vf_restore_conf(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = hns3vf_check_default_mac_change(hw); + if (ret) + return ret; + + ret = hns3vf_configure_mac_addr(hns, false); + if (ret) + return ret; + + ret = hns3vf_configure_all_mc_mac_addr(hns, false); + if (ret) + goto err_mc_mac; + + ret = hns3vf_restore_promisc(hns); + if (ret) + goto err_vlan_table; + + ret = hns3vf_restore_vlan_conf(hns); + if (ret) + goto err_vlan_table; + + ret = hns3vf_restore_rx_interrupt(hw); + if (ret) + goto err_vlan_table; + + if (hw->adapter_state == HNS3_NIC_STARTED) { + ret = hns3vf_do_start(hns, false); + if (ret) + goto err_vlan_table; + hns3_info(hw, "hns3vf dev restart successful!"); + } else if (hw->adapter_state == HNS3_NIC_STOPPING) + hw->adapter_state = HNS3_NIC_CONFIGURED; + return 0; + +err_vlan_table: + hns3vf_configure_all_mc_mac_addr(hns, true); +err_mc_mac: + hns3vf_configure_mac_addr(hns, true); + return ret; +} + +static enum hns3_reset_level +hns3vf_get_reset_level(struct hns3_hw *hw, uint64_t *levels) +{ + enum hns3_reset_level reset_level; + + /* return the highest priority reset level amongst all */ + if (hns3_atomic_test_bit(HNS3_VF_RESET, levels)) + reset_level = HNS3_VF_RESET; + else if (hns3_atomic_test_bit(HNS3_VF_FULL_RESET, levels)) + reset_level = HNS3_VF_FULL_RESET; + else if (hns3_atomic_test_bit(HNS3_VF_PF_FUNC_RESET, levels)) + reset_level = HNS3_VF_PF_FUNC_RESET; + else if (hns3_atomic_test_bit(HNS3_VF_FUNC_RESET, levels)) + reset_level = HNS3_VF_FUNC_RESET; + else if (hns3_atomic_test_bit(HNS3_FLR_RESET, levels)) + reset_level = HNS3_FLR_RESET; + else + reset_level = HNS3_NONE_RESET; + + if (hw->reset.level != HNS3_NONE_RESET && reset_level < hw->reset.level) + return HNS3_NONE_RESET; + + return reset_level; +} + +static void +hns3vf_reset_service(void *param) +{ + struct hns3_adapter *hns = (struct hns3_adapter *)param; + struct hns3_hw *hw = &hns->hw; + enum hns3_reset_level reset_level; + struct timeval tv_delta; + struct timeval tv_start; + struct timeval tv; + uint64_t msec; + + /* + * The interrupt is not triggered within the delay time. + * The interrupt may have been lost. It is necessary to handle + * the interrupt to recover from the error. + */ + if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_DEFERRED) { + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_REQUESTED); + hns3_err(hw, "Handling interrupts in delayed tasks"); + hns3vf_interrupt_handler(&rte_eth_devices[hw->data->port_id]); + reset_level = hns3vf_get_reset_level(hw, &hw->reset.pending); + if (reset_level == HNS3_NONE_RESET) { + hns3_err(hw, "No reset level is set, try global reset"); + hns3_atomic_set_bit(HNS3_VF_RESET, &hw->reset.pending); + } + } + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_NONE); + + /* + * Hardware reset has been notified, we now have to poll & check if + * hardware has actually completed the reset sequence. + */ + reset_level = hns3vf_get_reset_level(hw, &hw->reset.pending); + if (reset_level != HNS3_NONE_RESET) { + gettimeofday(&tv_start, NULL); + hns3_reset_process(hns, reset_level); + gettimeofday(&tv, NULL); + timersub(&tv, &tv_start, &tv_delta); + msec = tv_delta.tv_sec * MSEC_PER_SEC + + tv_delta.tv_usec / USEC_PER_MSEC; + if (msec > HNS3_RESET_PROCESS_MS) + hns3_err(hw, "%d handle long time delta %" PRIx64 + " ms time=%ld.%.6ld", + hw->reset.level, msec, tv.tv_sec, tv.tv_usec); + } +} + +static int +hns3vf_reinit_dev(struct hns3_adapter *hns) +{ + struct rte_eth_dev *eth_dev = &rte_eth_devices[hns->hw.data->port_id]; + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev); + struct hns3_hw *hw = &hns->hw; + int ret; + + if (hw->reset.level == HNS3_VF_FULL_RESET) { + rte_intr_disable(&pci_dev->intr_handle); + hns3vf_set_bus_master(pci_dev, true); + } + + /* Firmware command initialize */ + ret = hns3_cmd_init(hw); + if (ret) { + hns3_err(hw, "Failed to init cmd: %d", ret); + return ret; + } + + if (hw->reset.level == HNS3_VF_FULL_RESET) { + /* + * UIO enables msix by writing the pcie configuration space + * vfio_pci enables msix in rte_intr_enable. + */ + if (pci_dev->kdrv == RTE_KDRV_IGB_UIO || + pci_dev->kdrv == RTE_KDRV_UIO_GENERIC) { + if (hns3vf_enable_msix(pci_dev, true)) + hns3_err(hw, "Failed to enable msix"); + } + + rte_intr_enable(&pci_dev->intr_handle); + } + + ret = hns3_reset_all_queues(hns); + if (ret) { + hns3_err(hw, "Failed to reset all queues: %d", ret); + return ret; + } + + ret = hns3vf_init_hardware(hns); + if (ret) { + hns3_err(hw, "Failed to init hardware: %d", ret); + return ret; + } + + return 0; +} + +static const struct eth_dev_ops hns3vf_eth_dev_ops = { + .dev_start = hns3vf_dev_start, + .dev_stop = hns3vf_dev_stop, + .dev_close = hns3vf_dev_close, + .mtu_set = hns3vf_dev_mtu_set, + .promiscuous_enable = hns3vf_dev_promiscuous_enable, + .promiscuous_disable = hns3vf_dev_promiscuous_disable, + .allmulticast_enable = hns3vf_dev_allmulticast_enable, + .allmulticast_disable = hns3vf_dev_allmulticast_disable, + .stats_get = hns3_stats_get, + .stats_reset = hns3_stats_reset, + .xstats_get = hns3_dev_xstats_get, + .xstats_get_names = hns3_dev_xstats_get_names, + .xstats_reset = hns3_dev_xstats_reset, + .xstats_get_by_id = hns3_dev_xstats_get_by_id, + .xstats_get_names_by_id = hns3_dev_xstats_get_names_by_id, + .dev_infos_get = hns3vf_dev_infos_get, + .fw_version_get = hns3vf_fw_version_get, + .rx_queue_setup = hns3_rx_queue_setup, + .tx_queue_setup = hns3_tx_queue_setup, + .rx_queue_release = hns3_dev_rx_queue_release, + .tx_queue_release = hns3_dev_tx_queue_release, + .rx_queue_intr_enable = hns3_dev_rx_queue_intr_enable, + .rx_queue_intr_disable = hns3_dev_rx_queue_intr_disable, + .dev_configure = hns3vf_dev_configure, + .mac_addr_add = hns3vf_add_mac_addr, + .mac_addr_remove = hns3vf_remove_mac_addr, + .mac_addr_set = hns3vf_set_default_mac_addr, + .set_mc_addr_list = hns3vf_set_mc_mac_addr_list, + .link_update = hns3vf_dev_link_update, + .rss_hash_update = hns3_dev_rss_hash_update, + .rss_hash_conf_get = hns3_dev_rss_hash_conf_get, + .reta_update = hns3_dev_rss_reta_update, + .reta_query = hns3_dev_rss_reta_query, + .filter_ctrl = hns3_dev_filter_ctrl, + .vlan_filter_set = hns3vf_vlan_filter_set, + .vlan_offload_set = hns3vf_vlan_offload_set, + .get_reg = hns3_get_regs, + .dev_supported_ptypes_get = hns3_dev_supported_ptypes_get, +}; + +static const struct hns3_reset_ops hns3vf_reset_ops = { + .reset_service = hns3vf_reset_service, + .stop_service = hns3vf_stop_service, + .prepare_reset = hns3vf_prepare_reset, + .wait_hardware_ready = hns3vf_wait_hardware_ready, + .reinit_dev = hns3vf_reinit_dev, + .restore_conf = hns3vf_restore_conf, + .start_service = hns3vf_start_service, +}; + +static int +hns3vf_dev_init(struct rte_eth_dev *eth_dev) +{ + struct rte_device *dev = eth_dev->device; + struct rte_pci_device *pci_dev = RTE_DEV_TO_PCI(dev); + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint8_t revision; + int ret; + + PMD_INIT_FUNC_TRACE(); + + /* Get PCI revision id */ + ret = rte_pci_read_config(pci_dev, &revision, HNS3_PCI_REVISION_ID_LEN, + HNS3_PCI_REVISION_ID); + if (ret != HNS3_PCI_REVISION_ID_LEN) { + PMD_INIT_LOG(ERR, "Failed to read pci revision id, ret = %d", + ret); + return -EIO; + } + hw->revision = revision; + + eth_dev->process_private = (struct hns3_process_private *) + rte_zmalloc_socket("hns3_filter_list", + sizeof(struct hns3_process_private), + RTE_CACHE_LINE_SIZE, eth_dev->device->numa_node); + if (eth_dev->process_private == NULL) { + PMD_INIT_LOG(ERR, "Failed to alloc memory for process private"); + return -ENOMEM; + } + + /* initialize flow filter lists */ + hns3_filterlist_init(eth_dev); + + hns3_set_rxtx_function(eth_dev); + eth_dev->dev_ops = &hns3vf_eth_dev_ops; + if (rte_eal_process_type() != RTE_PROC_PRIMARY) { + hns3_mp_init_secondary(); + hw->secondary_cnt++; + return 0; + } + + hns3_mp_init_primary(); + + hw->adapter_state = HNS3_NIC_UNINITIALIZED; + hns->is_vf = true; + hw->data = eth_dev->data; + + ret = hns3_reset_init(hw); + if (ret) + goto err_init_reset; + hw->reset.ops = &hns3vf_reset_ops; + + ret = hns3vf_init_vf(eth_dev); + if (ret) { + PMD_INIT_LOG(ERR, "Failed to init vf: %d", ret); + goto err_init_vf; + } + + /* Allocate memory for storing MAC addresses */ + eth_dev->data->mac_addrs = rte_zmalloc("hns3vf-mac", + sizeof(struct rte_ether_addr) * + HNS3_VF_UC_MACADDR_NUM, 0); + if (eth_dev->data->mac_addrs == NULL) { + PMD_INIT_LOG(ERR, "Failed to allocate %zx bytes needed " + "to store MAC addresses", + sizeof(struct rte_ether_addr) * + HNS3_VF_UC_MACADDR_NUM); + ret = -ENOMEM; + goto err_rte_zmalloc; + } + + rte_ether_addr_copy((struct rte_ether_addr *)hw->mac.mac_addr, + ð_dev->data->mac_addrs[0]); + hw->adapter_state = HNS3_NIC_INITIALIZED; + /* + * Pass the information to the rte_eth_dev_close() that it should also + * release the private port resources. + */ + eth_dev->data->dev_flags |= RTE_ETH_DEV_CLOSE_REMOVE; + + if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_PENDING) { + hns3_err(hw, "Reschedule reset service after dev_init"); + hns3_schedule_reset(hns); + } else { + /* IMP will wait ready flag before reset */ + hns3_notify_reset_ready(hw, false); + } + rte_eal_alarm_set(HNS3VF_KEEP_ALIVE_INTERVAL, hns3vf_keep_alive_handler, + eth_dev); + return 0; + +err_rte_zmalloc: + hns3vf_uninit_vf(eth_dev); + +err_init_vf: + rte_free(hw->reset.wait_data); + +err_init_reset: + eth_dev->dev_ops = NULL; + eth_dev->rx_pkt_burst = NULL; + eth_dev->tx_pkt_burst = NULL; + eth_dev->tx_pkt_prepare = NULL; + rte_free(eth_dev->process_private); + eth_dev->process_private = NULL; + + return ret; +} + +static int +hns3vf_dev_uninit(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + PMD_INIT_FUNC_TRACE(); + + if (rte_eal_process_type() != RTE_PROC_PRIMARY) + return -EPERM; + + eth_dev->dev_ops = NULL; + eth_dev->rx_pkt_burst = NULL; + eth_dev->tx_pkt_burst = NULL; + eth_dev->tx_pkt_prepare = NULL; + + if (hw->adapter_state < HNS3_NIC_CLOSING) + hns3vf_dev_close(eth_dev); + + hw->adapter_state = HNS3_NIC_REMOVED; + return 0; +} + +static int +eth_hns3vf_pci_probe(struct rte_pci_driver *pci_drv __rte_unused, + struct rte_pci_device *pci_dev) +{ + return rte_eth_dev_pci_generic_probe(pci_dev, + sizeof(struct hns3_adapter), + hns3vf_dev_init); +} + +static int +eth_hns3vf_pci_remove(struct rte_pci_device *pci_dev) +{ + return rte_eth_dev_pci_generic_remove(pci_dev, hns3vf_dev_uninit); +} + +static const struct rte_pci_id pci_id_hns3vf_map[] = { + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_100G_VF) }, + { RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HNS3_DEV_ID_100G_RDMA_PFC_VF) }, + { .vendor_id = 0, /* sentinel */ }, +}; + +static struct rte_pci_driver rte_hns3vf_pmd = { + .id_table = pci_id_hns3vf_map, + .drv_flags = RTE_PCI_DRV_NEED_MAPPING, + .probe = eth_hns3vf_pci_probe, + .remove = eth_hns3vf_pci_remove, +}; + +RTE_PMD_REGISTER_PCI(net_hns3_vf, rte_hns3vf_pmd); +RTE_PMD_REGISTER_PCI_TABLE(net_hns3_vf, pci_id_hns3vf_map); +RTE_PMD_REGISTER_KMOD_DEP(net_hns3_vf, "* igb_uio | vfio-pci"); diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_fdir.c b/src/spdk/dpdk/drivers/net/hns3/hns3_fdir.c new file mode 100644 index 000000000..4c5928ffc --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_fdir.c @@ -0,0 +1,1080 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdbool.h> +#include <rte_ethdev_driver.h> +#include <rte_hash.h> +#include <rte_hash_crc.h> +#include <rte_io.h> +#include <rte_malloc.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" + +#define HNS3_VLAN_TAG_TYPE_NONE 0 +#define HNS3_VLAN_TAG_TYPE_TAG2 1 +#define HNS3_VLAN_TAG_TYPE_TAG1 2 +#define HNS3_VLAN_TAG_TYPE_TAG1_2 3 + +#define HNS3_PF_ID_S 0 +#define HNS3_PF_ID_M GENMASK(2, 0) +#define HNS3_VF_ID_S 3 +#define HNS3_VF_ID_M GENMASK(10, 3) +#define HNS3_PORT_TYPE_B 11 +#define HNS3_NETWORK_PORT_ID_S 0 +#define HNS3_NETWORK_PORT_ID_M GENMASK(3, 0) + +#define HNS3_FD_EPORT_SW_EN_B 0 + +#define HNS3_FD_AD_DATA_S 32 +#define HNS3_FD_AD_DROP_B 0 +#define HNS3_FD_AD_DIRECT_QID_B 1 +#define HNS3_FD_AD_QID_S 2 +#define HNS3_FD_AD_QID_M GENMASK(12, 2) +#define HNS3_FD_AD_USE_COUNTER_B 12 +#define HNS3_FD_AD_COUNTER_NUM_S 13 +#define HNS3_FD_AD_COUNTER_NUM_M GENMASK(20, 13) +#define HNS3_FD_AD_NXT_STEP_B 20 +#define HNS3_FD_AD_NXT_KEY_S 21 +#define HNS3_FD_AD_NXT_KEY_M GENMASK(26, 21) +#define HNS3_FD_AD_WR_RULE_ID_B 0 +#define HNS3_FD_AD_RULE_ID_S 1 +#define HNS3_FD_AD_RULE_ID_M GENMASK(13, 1) + +enum HNS3_PORT_TYPE { + HOST_PORT, + NETWORK_PORT +}; + +enum HNS3_FD_MODE { + HNS3_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1, + HNS3_FD_MODE_DEPTH_1K_WIDTH_400B_STAGE_2, + HNS3_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1, + HNS3_FD_MODE_DEPTH_2K_WIDTH_200B_STAGE_2, +}; + +enum HNS3_FD_KEY_TYPE { + HNS3_FD_KEY_BASE_ON_PTYPE, + HNS3_FD_KEY_BASE_ON_TUPLE, +}; + +enum HNS3_FD_META_DATA { + PACKET_TYPE_ID, + IP_FRAGEMENT, + ROCE_TYPE, + NEXT_KEY, + VLAN_NUMBER, + SRC_VPORT, + DST_VPORT, + TUNNEL_PACKET, + MAX_META_DATA, +}; + +struct key_info { + uint8_t key_type; + uint8_t key_length; +}; + +static const struct key_info meta_data_key_info[] = { + {PACKET_TYPE_ID, 6}, + {IP_FRAGEMENT, 1}, + {ROCE_TYPE, 1}, + {NEXT_KEY, 5}, + {VLAN_NUMBER, 2}, + {SRC_VPORT, 12}, + {DST_VPORT, 12}, + {TUNNEL_PACKET, 1}, +}; + +static const struct key_info tuple_key_info[] = { + {OUTER_DST_MAC, 48}, + {OUTER_SRC_MAC, 48}, + {OUTER_VLAN_TAG_FST, 16}, + {OUTER_VLAN_TAG_SEC, 16}, + {OUTER_ETH_TYPE, 16}, + {OUTER_L2_RSV, 16}, + {OUTER_IP_TOS, 8}, + {OUTER_IP_PROTO, 8}, + {OUTER_SRC_IP, 32}, + {OUTER_DST_IP, 32}, + {OUTER_L3_RSV, 16}, + {OUTER_SRC_PORT, 16}, + {OUTER_DST_PORT, 16}, + {OUTER_L4_RSV, 32}, + {OUTER_TUN_VNI, 24}, + {OUTER_TUN_FLOW_ID, 8}, + {INNER_DST_MAC, 48}, + {INNER_SRC_MAC, 48}, + {INNER_VLAN_TAG1, 16}, + {INNER_VLAN_TAG2, 16}, + {INNER_ETH_TYPE, 16}, + {INNER_L2_RSV, 16}, + {INNER_IP_TOS, 8}, + {INNER_IP_PROTO, 8}, + {INNER_SRC_IP, 32}, + {INNER_DST_IP, 32}, + {INNER_L3_RSV, 16}, + {INNER_SRC_PORT, 16}, + {INNER_DST_PORT, 16}, + {INNER_SCTP_TAG, 32}, +}; + +#define HNS3_BITS_PER_BYTE 8 +#define MAX_KEY_LENGTH 400 +#define MAX_200B_KEY_LENGTH 200 +#define MAX_META_DATA_LENGTH 16 +#define MAX_KEY_DWORDS DIV_ROUND_UP(MAX_KEY_LENGTH / HNS3_BITS_PER_BYTE, 4) +#define MAX_KEY_BYTES (MAX_KEY_DWORDS * 4) + +enum HNS3_FD_PACKET_TYPE { + NIC_PACKET, + ROCE_PACKET, +}; + +/* For each bit of TCAM entry, it uses a pair of 'x' and + * 'y' to indicate which value to match, like below: + * ---------------------------------- + * | bit x | bit y | search value | + * ---------------------------------- + * | 0 | 0 | always hit | + * ---------------------------------- + * | 1 | 0 | match '0' | + * ---------------------------------- + * | 0 | 1 | match '1' | + * ---------------------------------- + * | 1 | 1 | invalid | + * ---------------------------------- + * Then for input key(k) and mask(v), we can calculate the value by + * the formulae: + * x = (~k) & v + * y = k & v + */ +#define calc_x(x, k, v) ((x) = (~(k) & (v))) +#define calc_y(y, k, v) ((y) = ((k) & (v))) + +struct hns3_fd_tcam_config_1_cmd { + uint8_t stage; + uint8_t xy_sel; + uint8_t port_info; + uint8_t rsv1[1]; + rte_le32_t index; + uint8_t entry_vld; + uint8_t rsv2[7]; + uint8_t tcam_data[8]; +}; + +struct hns3_fd_tcam_config_2_cmd { + uint8_t tcam_data[24]; +}; + +struct hns3_fd_tcam_config_3_cmd { + uint8_t tcam_data[20]; + uint8_t rsv[4]; +}; + +struct hns3_get_fd_mode_cmd { + uint8_t mode; + uint8_t enable; + uint8_t rsv[22]; +}; + +struct hns3_get_fd_allocation_cmd { + rte_le32_t stage1_entry_num; + rte_le32_t stage2_entry_num; + rte_le16_t stage1_counter_num; + rte_le16_t stage2_counter_num; + uint8_t rsv[12]; +}; + +struct hns3_set_fd_key_config_cmd { + uint8_t stage; + uint8_t key_select; + uint8_t inner_sipv6_word_en; + uint8_t inner_dipv6_word_en; + uint8_t outer_sipv6_word_en; + uint8_t outer_dipv6_word_en; + uint8_t rsv1[2]; + rte_le32_t tuple_mask; + rte_le32_t meta_data_mask; + uint8_t rsv2[8]; +}; + +struct hns3_fd_ad_config_cmd { + uint8_t stage; + uint8_t rsv1[3]; + rte_le32_t index; + rte_le64_t ad_data; + uint8_t rsv2[8]; +}; + +struct hns3_fd_get_cnt_cmd { + uint8_t stage; + uint8_t rsv1[3]; + rte_le16_t index; + uint8_t rsv2[2]; + rte_le64_t value; + uint8_t rsv3[8]; +}; + +static int hns3_get_fd_mode(struct hns3_hw *hw, uint8_t *fd_mode) +{ + struct hns3_get_fd_mode_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_MODE_CTRL, true); + + req = (struct hns3_get_fd_mode_cmd *)desc.data; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Get fd mode fail, ret=%d", ret); + return ret; + } + + *fd_mode = req->mode; + + return ret; +} + +static int hns3_get_fd_allocation(struct hns3_hw *hw, + uint32_t *stage1_entry_num, + uint32_t *stage2_entry_num, + uint16_t *stage1_counter_num, + uint16_t *stage2_counter_num) +{ + struct hns3_get_fd_allocation_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_GET_ALLOCATION, true); + + req = (struct hns3_get_fd_allocation_cmd *)desc.data; + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Query fd allocation fail, ret=%d", ret); + return ret; + } + + *stage1_entry_num = rte_le_to_cpu_32(req->stage1_entry_num); + *stage2_entry_num = rte_le_to_cpu_32(req->stage2_entry_num); + *stage1_counter_num = rte_le_to_cpu_16(req->stage1_counter_num); + *stage2_counter_num = rte_le_to_cpu_16(req->stage2_counter_num); + + return ret; +} + +static int hns3_set_fd_key_config(struct hns3_adapter *hns) +{ + struct hns3_set_fd_key_config_cmd *req; + struct hns3_fd_key_cfg *key_cfg; + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_KEY_CONFIG, false); + + req = (struct hns3_set_fd_key_config_cmd *)desc.data; + key_cfg = &pf->fdir.fd_cfg.key_cfg[HNS3_FD_STAGE_1]; + req->stage = HNS3_FD_STAGE_1; + req->key_select = key_cfg->key_sel; + req->inner_sipv6_word_en = key_cfg->inner_sipv6_word_en; + req->inner_dipv6_word_en = key_cfg->inner_dipv6_word_en; + req->outer_sipv6_word_en = key_cfg->outer_sipv6_word_en; + req->outer_dipv6_word_en = key_cfg->outer_dipv6_word_en; + req->tuple_mask = rte_cpu_to_le_32(~key_cfg->tuple_active); + req->meta_data_mask = rte_cpu_to_le_32(~key_cfg->meta_data_active); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Set fd key fail, ret=%d", ret); + + return ret; +} + +int hns3_init_fd_config(struct hns3_adapter *hns) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + struct hns3_fd_key_cfg *key_cfg; + int ret; + + ret = hns3_get_fd_mode(hw, &pf->fdir.fd_cfg.fd_mode); + if (ret) + return ret; + + switch (pf->fdir.fd_cfg.fd_mode) { + case HNS3_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1: + pf->fdir.fd_cfg.max_key_length = MAX_KEY_LENGTH; + break; + case HNS3_FD_MODE_DEPTH_4K_WIDTH_200B_STAGE_1: + pf->fdir.fd_cfg.max_key_length = MAX_200B_KEY_LENGTH; + hns3_warn(hw, "Unsupported tunnel filter in 4K*200Bit"); + break; + default: + hns3_err(hw, "Unsupported flow director mode %d", + pf->fdir.fd_cfg.fd_mode); + return -EOPNOTSUPP; + } + + key_cfg = &pf->fdir.fd_cfg.key_cfg[HNS3_FD_STAGE_1]; + key_cfg->key_sel = HNS3_FD_KEY_BASE_ON_TUPLE; + key_cfg->inner_sipv6_word_en = IPV6_ADDR_WORD_MASK; + key_cfg->inner_dipv6_word_en = IPV6_ADDR_WORD_MASK; + key_cfg->outer_sipv6_word_en = 0; + key_cfg->outer_dipv6_word_en = 0; + + key_cfg->tuple_active = BIT(INNER_VLAN_TAG1) | BIT(INNER_ETH_TYPE) | + BIT(INNER_IP_PROTO) | BIT(INNER_IP_TOS) | + BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) | + BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT); + + /* If use max 400bit key, we can support tuples for ether type */ + if (pf->fdir.fd_cfg.max_key_length == MAX_KEY_LENGTH) { + key_cfg->tuple_active |= + BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC) | + BIT(OUTER_SRC_PORT) | BIT(INNER_SCTP_TAG) | + BIT(OUTER_DST_PORT) | BIT(INNER_VLAN_TAG2) | + BIT(OUTER_TUN_VNI) | BIT(OUTER_TUN_FLOW_ID) | + BIT(OUTER_ETH_TYPE) | BIT(OUTER_IP_PROTO); + } + + /* roce_type is used to filter roce frames + * dst_vport is used to specify the rule + */ + key_cfg->meta_data_active = BIT(DST_VPORT) | BIT(TUNNEL_PACKET) | + BIT(VLAN_NUMBER); + + ret = hns3_get_fd_allocation(hw, + &pf->fdir.fd_cfg.rule_num[HNS3_FD_STAGE_1], + &pf->fdir.fd_cfg.rule_num[HNS3_FD_STAGE_2], + &pf->fdir.fd_cfg.cnt_num[HNS3_FD_STAGE_1], + &pf->fdir.fd_cfg.cnt_num[HNS3_FD_STAGE_2]); + if (ret) + return ret; + + return hns3_set_fd_key_config(hns); +} + +static int hns3_fd_tcam_config(struct hns3_hw *hw, bool sel_x, int loc, + uint8_t *key, bool is_add) +{ +#define FD_TCAM_CMD_NUM 3 + struct hns3_fd_tcam_config_1_cmd *req1; + struct hns3_fd_tcam_config_2_cmd *req2; + struct hns3_fd_tcam_config_3_cmd *req3; + struct hns3_cmd_desc desc[FD_TCAM_CMD_NUM]; + int len; + int ret; + + hns3_cmd_setup_basic_desc(&desc[0], HNS3_OPC_FD_TCAM_OP, false); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_setup_basic_desc(&desc[1], HNS3_OPC_FD_TCAM_OP, false); + desc[1].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_setup_basic_desc(&desc[2], HNS3_OPC_FD_TCAM_OP, false); + + req1 = (struct hns3_fd_tcam_config_1_cmd *)desc[0].data; + req2 = (struct hns3_fd_tcam_config_2_cmd *)desc[1].data; + req3 = (struct hns3_fd_tcam_config_3_cmd *)desc[2].data; + + req1->stage = HNS3_FD_STAGE_1; + req1->xy_sel = sel_x ? 1 : 0; + hns3_set_bit(req1->port_info, HNS3_FD_EPORT_SW_EN_B, 0); + req1->index = rte_cpu_to_le_32(loc); + req1->entry_vld = sel_x ? is_add : 0; + + if (key) { + len = sizeof(req1->tcam_data); + memcpy(req1->tcam_data, key, len); + key += len; + + len = sizeof(req2->tcam_data); + memcpy(req2->tcam_data, key, len); + key += len; + + len = sizeof(req3->tcam_data); + memcpy(req3->tcam_data, key, len); + } + + ret = hns3_cmd_send(hw, desc, FD_TCAM_CMD_NUM); + if (ret) + hns3_err(hw, "Config tcam key fail, ret=%d loc=%d add=%d", + ret, loc, is_add); + return ret; +} + +static int hns3_fd_ad_config(struct hns3_hw *hw, int loc, + struct hns3_fd_ad_data *action) +{ + struct hns3_fd_ad_config_cmd *req; + struct hns3_cmd_desc desc; + uint64_t ad_data = 0; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_AD_OP, false); + + req = (struct hns3_fd_ad_config_cmd *)desc.data; + req->index = rte_cpu_to_le_32(loc); + req->stage = HNS3_FD_STAGE_1; + + hns3_set_bit(ad_data, HNS3_FD_AD_WR_RULE_ID_B, + action->write_rule_id_to_bd); + hns3_set_field(ad_data, HNS3_FD_AD_RULE_ID_M, HNS3_FD_AD_RULE_ID_S, + action->rule_id); + ad_data <<= HNS3_FD_AD_DATA_S; + hns3_set_bit(ad_data, HNS3_FD_AD_DROP_B, action->drop_packet); + hns3_set_bit(ad_data, HNS3_FD_AD_DIRECT_QID_B, + action->forward_to_direct_queue); + hns3_set_field(ad_data, HNS3_FD_AD_QID_M, HNS3_FD_AD_QID_S, + action->queue_id); + hns3_set_bit(ad_data, HNS3_FD_AD_USE_COUNTER_B, action->use_counter); + hns3_set_field(ad_data, HNS3_FD_AD_COUNTER_NUM_M, + HNS3_FD_AD_COUNTER_NUM_S, action->counter_id); + hns3_set_bit(ad_data, HNS3_FD_AD_NXT_STEP_B, action->use_next_stage); + hns3_set_field(ad_data, HNS3_FD_AD_NXT_KEY_M, HNS3_FD_AD_NXT_KEY_S, + action->counter_id); + + req->ad_data = rte_cpu_to_le_64(ad_data); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Config fd ad fail, ret=%d loc=%d", ret, loc); + + return ret; +} + +static inline void hns3_fd_convert_mac(uint8_t *key, uint8_t *mask, + uint8_t *mac_x, uint8_t *mac_y) +{ + uint8_t tmp; + int i; + + for (i = 0; i < RTE_ETHER_ADDR_LEN; i++) { + tmp = RTE_ETHER_ADDR_LEN - 1 - i; + calc_x(mac_x[tmp], key[i], mask[i]); + calc_y(mac_y[tmp], key[i], mask[i]); + } +} + +static void hns3_fd_convert_int16(uint32_t tuple, struct hns3_fdir_rule *rule, + uint8_t *val_x, uint8_t *val_y) +{ + uint16_t tmp_x_s; + uint16_t tmp_y_s; + uint16_t mask; + uint16_t key; + + switch (tuple) { + case OUTER_SRC_PORT: + key = rule->key_conf.spec.outer_src_port; + mask = rule->key_conf.mask.outer_src_port; + break; + case OUTER_DST_PORT: + key = rule->key_conf.spec.tunnel_type; + mask = rule->key_conf.mask.tunnel_type; + break; + case OUTER_ETH_TYPE: + key = rule->key_conf.spec.outer_ether_type; + mask = rule->key_conf.mask.outer_ether_type; + break; + case INNER_SRC_PORT: + key = rule->key_conf.spec.src_port; + mask = rule->key_conf.mask.src_port; + break; + case INNER_DST_PORT: + key = rule->key_conf.spec.dst_port; + mask = rule->key_conf.mask.dst_port; + break; + case INNER_VLAN_TAG1: + key = rule->key_conf.spec.vlan_tag1; + mask = rule->key_conf.mask.vlan_tag1; + break; + case INNER_VLAN_TAG2: + key = rule->key_conf.spec.vlan_tag2; + mask = rule->key_conf.mask.vlan_tag2; + break; + default: + /* INNER_ETH_TYPE: */ + key = rule->key_conf.spec.ether_type; + mask = rule->key_conf.mask.ether_type; + break; + } + calc_x(tmp_x_s, key, mask); + calc_y(tmp_y_s, key, mask); + val_x[0] = rte_cpu_to_le_16(tmp_x_s) & 0xFF; + val_x[1] = rte_cpu_to_le_16(tmp_x_s) >> HNS3_BITS_PER_BYTE; + val_y[0] = rte_cpu_to_le_16(tmp_y_s) & 0xFF; + val_y[1] = rte_cpu_to_le_16(tmp_y_s) >> HNS3_BITS_PER_BYTE; +} + +static inline void hns3_fd_convert_int32(uint32_t key, uint32_t mask, + uint8_t *val_x, uint8_t *val_y) +{ + uint32_t tmp_x_l; + uint32_t tmp_y_l; + + calc_x(tmp_x_l, key, mask); + calc_y(tmp_y_l, key, mask); + memcpy(val_x, &tmp_x_l, sizeof(tmp_x_l)); + memcpy(val_y, &tmp_y_l, sizeof(tmp_y_l)); +} + +static bool hns3_fd_convert_tuple(uint32_t tuple, uint8_t *key_x, + uint8_t *key_y, struct hns3_fdir_rule *rule) +{ + struct hns3_fdir_key_conf *key_conf; + int tmp; + int i; + + if ((rule->input_set & BIT(tuple)) == 0) + return true; + + key_conf = &rule->key_conf; + switch (tuple) { + case INNER_DST_MAC: + hns3_fd_convert_mac(key_conf->spec.dst_mac, + key_conf->mask.dst_mac, key_x, key_y); + break; + case INNER_SRC_MAC: + hns3_fd_convert_mac(key_conf->spec.src_mac, + key_conf->mask.src_mac, key_x, key_y); + break; + case OUTER_SRC_PORT: + case OUTER_DST_PORT: + case OUTER_ETH_TYPE: + case INNER_SRC_PORT: + case INNER_DST_PORT: + case INNER_VLAN_TAG1: + case INNER_VLAN_TAG2: + case INNER_ETH_TYPE: + hns3_fd_convert_int16(tuple, rule, key_x, key_y); + break; + case INNER_SRC_IP: + hns3_fd_convert_int32(key_conf->spec.src_ip[IP_ADDR_KEY_ID], + key_conf->mask.src_ip[IP_ADDR_KEY_ID], + key_x, key_y); + break; + case INNER_DST_IP: + hns3_fd_convert_int32(key_conf->spec.dst_ip[IP_ADDR_KEY_ID], + key_conf->mask.dst_ip[IP_ADDR_KEY_ID], + key_x, key_y); + break; + case INNER_SCTP_TAG: + hns3_fd_convert_int32(key_conf->spec.sctp_tag, + key_conf->mask.sctp_tag, key_x, key_y); + break; + case OUTER_TUN_VNI: + for (i = 0; i < VNI_OR_TNI_LEN; i++) { + tmp = VNI_OR_TNI_LEN - 1 - i; + calc_x(key_x[tmp], + key_conf->spec.outer_tun_vni[i], + key_conf->mask.outer_tun_vni[i]); + calc_y(key_y[tmp], + key_conf->spec.outer_tun_vni[i], + key_conf->mask.outer_tun_vni[i]); + } + break; + case OUTER_TUN_FLOW_ID: + calc_x(*key_x, key_conf->spec.outer_tun_flow_id, + key_conf->mask.outer_tun_flow_id); + calc_y(*key_y, key_conf->spec.outer_tun_flow_id, + key_conf->mask.outer_tun_flow_id); + break; + case INNER_IP_TOS: + calc_x(*key_x, key_conf->spec.ip_tos, key_conf->mask.ip_tos); + calc_y(*key_y, key_conf->spec.ip_tos, key_conf->mask.ip_tos); + break; + case OUTER_IP_PROTO: + calc_x(*key_x, key_conf->spec.outer_proto, + key_conf->mask.outer_proto); + calc_y(*key_y, key_conf->spec.outer_proto, + key_conf->mask.outer_proto); + break; + case INNER_IP_PROTO: + calc_x(*key_x, key_conf->spec.ip_proto, + key_conf->mask.ip_proto); + calc_y(*key_y, key_conf->spec.ip_proto, + key_conf->mask.ip_proto); + break; + } + return true; +} + +static uint32_t hns3_get_port_number(uint8_t pf_id, uint8_t vf_id) +{ + uint32_t port_number = 0; + + hns3_set_field(port_number, HNS3_PF_ID_M, HNS3_PF_ID_S, pf_id); + hns3_set_field(port_number, HNS3_VF_ID_M, HNS3_VF_ID_S, vf_id); + hns3_set_bit(port_number, HNS3_PORT_TYPE_B, HOST_PORT); + + return port_number; +} + +static void hns3_fd_convert_meta_data(struct hns3_fd_key_cfg *cfg, + uint8_t vf_id, + struct hns3_fdir_rule *rule, + uint8_t *key_x, uint8_t *key_y) +{ + uint16_t meta_data = 0; + uint16_t port_number; + uint8_t cur_pos = 0; + uint8_t tuple_size; + uint8_t shift_bits; + uint32_t tmp_x; + uint32_t tmp_y; + uint8_t i; + + for (i = 0; i < MAX_META_DATA; i++) { + if ((cfg->meta_data_active & BIT(i)) == 0) + continue; + + tuple_size = meta_data_key_info[i].key_length; + if (i == TUNNEL_PACKET) { + hns3_set_bit(meta_data, cur_pos, + rule->key_conf.spec.tunnel_type ? 1 : 0); + cur_pos += tuple_size; + } else if (i == VLAN_NUMBER) { + uint8_t vlan_tag; + uint8_t vlan_num; + if (rule->key_conf.spec.tunnel_type == 0) + vlan_num = rule->key_conf.vlan_num; + else + vlan_num = rule->key_conf.outer_vlan_num; + if (vlan_num == 1) + vlan_tag = HNS3_VLAN_TAG_TYPE_TAG1; + else if (vlan_num == VLAN_TAG_NUM_MAX) + vlan_tag = HNS3_VLAN_TAG_TYPE_TAG1_2; + else + vlan_tag = HNS3_VLAN_TAG_TYPE_NONE; + hns3_set_field(meta_data, + GENMASK(cur_pos + tuple_size, + cur_pos), cur_pos, vlan_tag); + cur_pos += tuple_size; + } else if (i == DST_VPORT) { + port_number = hns3_get_port_number(0, vf_id); + hns3_set_field(meta_data, + GENMASK(cur_pos + tuple_size, cur_pos), + cur_pos, port_number); + cur_pos += tuple_size; + } + } + + calc_x(tmp_x, meta_data, 0xFFFF); + calc_y(tmp_y, meta_data, 0xFFFF); + shift_bits = sizeof(meta_data) * HNS3_BITS_PER_BYTE - cur_pos; + + tmp_x = rte_cpu_to_le_32(tmp_x << shift_bits); + tmp_y = rte_cpu_to_le_32(tmp_y << shift_bits); + key_x[0] = tmp_x & 0xFF; + key_x[1] = (tmp_x >> HNS3_BITS_PER_BYTE) & 0xFF; + key_y[0] = tmp_y & 0xFF; + key_y[1] = (tmp_y >> HNS3_BITS_PER_BYTE) & 0xFF; +} + +/* A complete key is combined with meta data key and tuple key. + * Meta data key is stored at the MSB region, and tuple key is stored at + * the LSB region, unused bits will be filled 0. + */ +static int hns3_config_key(struct hns3_adapter *hns, + struct hns3_fdir_rule *rule) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + struct hns3_fd_key_cfg *key_cfg; + uint8_t *cur_key_x; + uint8_t *cur_key_y; + uint8_t key_x[MAX_KEY_BYTES] __rte_aligned(4); + uint8_t key_y[MAX_KEY_BYTES] __rte_aligned(4); + uint8_t vf_id = rule->vf_id; + uint8_t meta_data_region; + uint8_t tuple_size; + uint8_t i; + int ret; + + memset(key_x, 0, sizeof(key_x)); + memset(key_y, 0, sizeof(key_y)); + cur_key_x = key_x; + cur_key_y = key_y; + + key_cfg = &pf->fdir.fd_cfg.key_cfg[HNS3_FD_STAGE_1]; + for (i = 0; i < MAX_TUPLE; i++) { + bool tuple_valid; + + tuple_size = tuple_key_info[i].key_length / HNS3_BITS_PER_BYTE; + if (key_cfg->tuple_active & BIT(i)) { + tuple_valid = hns3_fd_convert_tuple(i, cur_key_x, + cur_key_y, rule); + if (tuple_valid) { + cur_key_x += tuple_size; + cur_key_y += tuple_size; + } + } + } + + meta_data_region = pf->fdir.fd_cfg.max_key_length / HNS3_BITS_PER_BYTE - + MAX_META_DATA_LENGTH / HNS3_BITS_PER_BYTE; + + hns3_fd_convert_meta_data(key_cfg, vf_id, rule, + key_x + meta_data_region, + key_y + meta_data_region); + + ret = hns3_fd_tcam_config(hw, false, rule->location, key_y, true); + if (ret) { + hns3_err(hw, "Config fd key_y fail, loc=%d, ret=%d", + rule->queue_id, ret); + return ret; + } + + ret = hns3_fd_tcam_config(hw, true, rule->location, key_x, true); + if (ret) + hns3_err(hw, "Config fd key_x fail, loc=%d, ret=%d", + rule->queue_id, ret); + return ret; +} + +static int hns3_config_action(struct hns3_hw *hw, struct hns3_fdir_rule *rule) +{ + struct hns3_fd_ad_data ad_data; + + ad_data.ad_id = rule->location; + + if (rule->action == HNS3_FD_ACTION_DROP_PACKET) { + ad_data.drop_packet = true; + ad_data.forward_to_direct_queue = false; + ad_data.queue_id = 0; + } else { + ad_data.drop_packet = false; + ad_data.forward_to_direct_queue = true; + ad_data.queue_id = rule->queue_id; + } + + if (unlikely(rule->flags & HNS3_RULE_FLAG_COUNTER)) { + ad_data.use_counter = true; + ad_data.counter_id = rule->act_cnt.id; + } else { + ad_data.use_counter = false; + ad_data.counter_id = 0; + } + + if (unlikely(rule->flags & HNS3_RULE_FLAG_FDID)) + ad_data.rule_id = rule->fd_id; + else + ad_data.rule_id = rule->location; + + ad_data.use_next_stage = false; + ad_data.next_input_key = 0; + + ad_data.write_rule_id_to_bd = true; + + return hns3_fd_ad_config(hw, ad_data.ad_id, &ad_data); +} + +static int hns3_fd_clear_all_rules(struct hns3_hw *hw, uint32_t rule_num) +{ + uint32_t i; + int ret; + + for (i = 0; i < rule_num; i++) { + ret = hns3_fd_tcam_config(hw, true, i, NULL, false); + if (ret) + return ret; + } + + return 0; +} + +int hns3_fdir_filter_init(struct hns3_adapter *hns) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_fdir_info *fdir_info = &pf->fdir; + uint32_t rule_num = fdir_info->fd_cfg.rule_num[HNS3_FD_STAGE_1]; + char fdir_hash_name[RTE_HASH_NAMESIZE]; + struct rte_hash_parameters fdir_hash_params = { + .name = fdir_hash_name, + .entries = rule_num, + .key_len = sizeof(struct hns3_fdir_key_conf), + .hash_func = rte_hash_crc, + .hash_func_init_val = 0, + }; + int ret; + + ret = hns3_fd_clear_all_rules(&hns->hw, rule_num); + if (ret) { + PMD_INIT_LOG(ERR, "Clear all fd rules fail! ret = %d", ret); + return ret; + } + + fdir_hash_params.socket_id = rte_socket_id(); + TAILQ_INIT(&fdir_info->fdir_list); + rte_spinlock_init(&fdir_info->flows_lock); + snprintf(fdir_hash_name, RTE_HASH_NAMESIZE, "%s", hns->hw.data->name); + fdir_info->hash_handle = rte_hash_create(&fdir_hash_params); + if (fdir_info->hash_handle == NULL) { + PMD_INIT_LOG(ERR, "Create FDIR hash handle fail!"); + return -EINVAL; + } + fdir_info->hash_map = rte_zmalloc("hns3 FDIR hash", + rule_num * + sizeof(struct hns3_fdir_rule_ele *), + 0); + if (fdir_info->hash_map == NULL) { + PMD_INIT_LOG(ERR, "Allocate memory for FDIR hash map fail!"); + rte_hash_free(fdir_info->hash_handle); + return -ENOMEM; + } + + return 0; +} + +void hns3_fdir_filter_uninit(struct hns3_adapter *hns) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_fdir_info *fdir_info = &pf->fdir; + struct hns3_fdir_rule_ele *fdir_filter; + + rte_spinlock_lock(&fdir_info->flows_lock); + if (fdir_info->hash_map) { + rte_free(fdir_info->hash_map); + fdir_info->hash_map = NULL; + } + if (fdir_info->hash_handle) { + rte_hash_free(fdir_info->hash_handle); + fdir_info->hash_handle = NULL; + } + rte_spinlock_unlock(&fdir_info->flows_lock); + + fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list); + while (fdir_filter) { + TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries); + hns3_fd_tcam_config(&hns->hw, true, + fdir_filter->fdir_conf.location, NULL, + false); + rte_free(fdir_filter); + fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list); + } +} + +/* + * Find a key in the hash table. + * @return + * - Zero and positive values are key location. + * - -EINVAL if the parameters are invalid. + * - -ENOENT if the key is not found. + */ +static int hns3_fdir_filter_lookup(struct hns3_fdir_info *fdir_info, + struct hns3_fdir_key_conf *key) +{ + hash_sig_t sig; + int ret; + + rte_spinlock_lock(&fdir_info->flows_lock); + sig = rte_hash_crc(key, sizeof(*key), 0); + ret = rte_hash_lookup_with_hash(fdir_info->hash_handle, key, sig); + rte_spinlock_unlock(&fdir_info->flows_lock); + + return ret; +} + +static int hns3_insert_fdir_filter(struct hns3_hw *hw, + struct hns3_fdir_info *fdir_info, + struct hns3_fdir_rule_ele *fdir_filter) +{ + struct hns3_fdir_key_conf *key; + hash_sig_t sig; + int ret; + + key = &fdir_filter->fdir_conf.key_conf; + rte_spinlock_lock(&fdir_info->flows_lock); + sig = rte_hash_crc(key, sizeof(*key), 0); + ret = rte_hash_add_key_with_hash(fdir_info->hash_handle, key, sig); + if (ret < 0) { + rte_spinlock_unlock(&fdir_info->flows_lock); + hns3_err(hw, "Hash table full? err:%d(%s)!", ret, + strerror(ret)); + return ret; + } + + fdir_info->hash_map[ret] = fdir_filter; + TAILQ_INSERT_TAIL(&fdir_info->fdir_list, fdir_filter, entries); + rte_spinlock_unlock(&fdir_info->flows_lock); + + return ret; +} + +static int hns3_remove_fdir_filter(struct hns3_hw *hw, + struct hns3_fdir_info *fdir_info, + struct hns3_fdir_key_conf *key) +{ + struct hns3_fdir_rule_ele *fdir_filter; + hash_sig_t sig; + int ret; + + rte_spinlock_lock(&fdir_info->flows_lock); + sig = rte_hash_crc(key, sizeof(*key), 0); + ret = rte_hash_del_key_with_hash(fdir_info->hash_handle, key, sig); + if (ret < 0) { + rte_spinlock_unlock(&fdir_info->flows_lock); + hns3_err(hw, "Delete hash key fail ret=%d", ret); + return ret; + } + + fdir_filter = fdir_info->hash_map[ret]; + fdir_info->hash_map[ret] = NULL; + TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries); + rte_spinlock_unlock(&fdir_info->flows_lock); + + rte_free(fdir_filter); + + return 0; +} + +int hns3_fdir_filter_program(struct hns3_adapter *hns, + struct hns3_fdir_rule *rule, bool del) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_fdir_info *fdir_info = &pf->fdir; + struct hns3_fdir_rule_ele *node; + struct hns3_hw *hw = &hns->hw; + int ret; + + if (del) { + ret = hns3_fd_tcam_config(hw, true, rule->location, NULL, + false); + if (ret) + hns3_err(hw, "Failed to delete fdir: %d src_ip:%x " + "dst_ip:%x src_port:%d dst_port:%d ret = %d", + rule->location, + rule->key_conf.spec.src_ip[IP_ADDR_KEY_ID], + rule->key_conf.spec.dst_ip[IP_ADDR_KEY_ID], + rule->key_conf.spec.src_port, + rule->key_conf.spec.dst_port, ret); + else + hns3_remove_fdir_filter(hw, fdir_info, &rule->key_conf); + + return ret; + } + + ret = hns3_fdir_filter_lookup(fdir_info, &rule->key_conf); + if (ret >= 0) { + hns3_err(hw, "Conflict with existing fdir loc: %d", ret); + return -EINVAL; + } + + node = rte_zmalloc("hns3 fdir rule", sizeof(struct hns3_fdir_rule_ele), + 0); + if (node == NULL) { + hns3_err(hw, "Failed to allocate fdir_rule memory"); + return -ENOMEM; + } + + rte_memcpy(&node->fdir_conf, rule, sizeof(struct hns3_fdir_rule)); + ret = hns3_insert_fdir_filter(hw, fdir_info, node); + if (ret < 0) { + rte_free(node); + return ret; + } + rule->location = ret; + node->fdir_conf.location = ret; + + rte_spinlock_lock(&fdir_info->flows_lock); + ret = hns3_config_action(hw, rule); + if (!ret) + ret = hns3_config_key(hns, rule); + rte_spinlock_unlock(&fdir_info->flows_lock); + if (ret) { + hns3_err(hw, "Failed to config fdir: %d src_ip:%x dst_ip:%x " + "src_port:%d dst_port:%d ret = %d", + rule->location, + rule->key_conf.spec.src_ip[IP_ADDR_KEY_ID], + rule->key_conf.spec.dst_ip[IP_ADDR_KEY_ID], + rule->key_conf.spec.src_port, + rule->key_conf.spec.dst_port, ret); + (void)hns3_remove_fdir_filter(hw, fdir_info, &rule->key_conf); + } + + return ret; +} + +/* remove all the flow director filters */ +int hns3_clear_all_fdir_filter(struct hns3_adapter *hns) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_fdir_info *fdir_info = &pf->fdir; + struct hns3_fdir_rule_ele *fdir_filter; + struct hns3_hw *hw = &hns->hw; + int ret = 0; + + /* flush flow director */ + rte_spinlock_lock(&fdir_info->flows_lock); + rte_hash_reset(fdir_info->hash_handle); + rte_spinlock_unlock(&fdir_info->flows_lock); + + fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list); + while (fdir_filter) { + TAILQ_REMOVE(&fdir_info->fdir_list, fdir_filter, entries); + ret += hns3_fd_tcam_config(hw, true, + fdir_filter->fdir_conf.location, + NULL, false); + rte_free(fdir_filter); + fdir_filter = TAILQ_FIRST(&fdir_info->fdir_list); + } + + if (ret) { + hns3_err(hw, "Fail to delete FDIR filter, ret = %d", ret); + ret = -EIO; + } + return ret; +} + +int hns3_restore_all_fdir_filter(struct hns3_adapter *hns) +{ + struct hns3_pf *pf = &hns->pf; + struct hns3_fdir_info *fdir_info = &pf->fdir; + struct hns3_fdir_rule_ele *fdir_filter; + struct hns3_hw *hw = &hns->hw; + bool err = false; + int ret; + + TAILQ_FOREACH(fdir_filter, &fdir_info->fdir_list, entries) { + ret = hns3_config_action(hw, &fdir_filter->fdir_conf); + if (!ret) + ret = hns3_config_key(hns, &fdir_filter->fdir_conf); + if (ret) { + err = true; + if (ret == -EBUSY) + break; + } + } + + if (err) { + hns3_err(hw, "Fail to restore FDIR filter, ret = %d", ret); + return -EIO; + } + return 0; +} + +int hns3_get_count(struct hns3_hw *hw, uint32_t id, uint64_t *value) +{ + struct hns3_fd_get_cnt_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_FD_COUNTER_OP, true); + + req = (struct hns3_fd_get_cnt_cmd *)desc.data; + req->stage = HNS3_FD_STAGE_1; + req->index = rte_cpu_to_le_32(id); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Read counter fail, ret=%d", ret); + return ret; + } + + *value = req->value; + + return ret; +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_fdir.h b/src/spdk/dpdk/drivers/net/hns3/hns3_fdir.h new file mode 100644 index 000000000..f7b421613 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_fdir.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_FDIR_H_ +#define _HNS3_FDIR_H_ + +#include <rte_flow.h> + +struct hns3_fd_key_cfg { + uint8_t key_sel; + uint8_t inner_sipv6_word_en; + uint8_t inner_dipv6_word_en; + uint8_t outer_sipv6_word_en; + uint8_t outer_dipv6_word_en; + uint32_t tuple_active; + uint32_t meta_data_active; +}; + +enum HNS3_FD_STAGE { + HNS3_FD_STAGE_1, + HNS3_FD_STAGE_2, + HNS3_FD_STAGE_NUM, +}; + +enum HNS3_FD_ACTION { + HNS3_FD_ACTION_ACCEPT_PACKET, + HNS3_FD_ACTION_DROP_PACKET, +}; + +struct hns3_fd_cfg { + uint8_t fd_mode; + uint16_t max_key_length; + uint32_t rule_num[HNS3_FD_STAGE_NUM]; /* rule entry number */ + uint16_t cnt_num[HNS3_FD_STAGE_NUM]; /* rule hit counter number */ + struct hns3_fd_key_cfg key_cfg[HNS3_FD_STAGE_NUM]; +}; + +/* OUTER_XXX indicates tuples in tunnel header of tunnel packet + * INNER_XXX indicate tuples in tunneled header of tunnel packet or + * tuples of non-tunnel packet + */ +enum HNS3_FD_TUPLE { + OUTER_DST_MAC, + OUTER_SRC_MAC, + OUTER_VLAN_TAG_FST, + OUTER_VLAN_TAG_SEC, + OUTER_ETH_TYPE, + OUTER_L2_RSV, + OUTER_IP_TOS, + OUTER_IP_PROTO, + OUTER_SRC_IP, + OUTER_DST_IP, + OUTER_L3_RSV, + OUTER_SRC_PORT, + OUTER_DST_PORT, + OUTER_L4_RSV, + OUTER_TUN_VNI, + OUTER_TUN_FLOW_ID, + INNER_DST_MAC, + INNER_SRC_MAC, + INNER_VLAN_TAG1, + INNER_VLAN_TAG2, + INNER_ETH_TYPE, + INNER_L2_RSV, + INNER_IP_TOS, + INNER_IP_PROTO, + INNER_SRC_IP, + INNER_DST_IP, + INNER_L3_RSV, + INNER_SRC_PORT, + INNER_DST_PORT, + INNER_SCTP_TAG, + MAX_TUPLE, +}; + +#define VLAN_TAG_NUM_MAX 2 +#define VNI_OR_TNI_LEN 3 +#define IP_ADDR_LEN 4 /* Length of IPv6 address. */ +#define IP_ADDR_KEY_ID 3 /* The last 32bit of IP address as FDIR search key */ +#define IPV6_ADDR_WORD_MASK 3 /* The last two word of IPv6 as FDIR search key */ + +struct hns3_fd_rule_tuples { + uint8_t src_mac[RTE_ETHER_ADDR_LEN]; + uint8_t dst_mac[RTE_ETHER_ADDR_LEN]; + uint32_t src_ip[IP_ADDR_LEN]; + uint32_t dst_ip[IP_ADDR_LEN]; + uint16_t src_port; + uint16_t dst_port; + uint16_t vlan_tag1; + uint16_t vlan_tag2; + uint16_t ether_type; + uint8_t ip_tos; + uint8_t ip_proto; + uint32_t sctp_tag; + uint16_t outer_src_port; + uint16_t tunnel_type; + uint16_t outer_ether_type; + uint8_t outer_proto; + uint8_t outer_tun_vni[VNI_OR_TNI_LEN]; + uint8_t outer_tun_flow_id; +}; + +struct hns3_fd_ad_data { + uint16_t ad_id; + uint8_t drop_packet; + uint8_t forward_to_direct_queue; + uint16_t queue_id; + uint8_t use_counter; + uint8_t counter_id; + uint8_t use_next_stage; + uint8_t write_rule_id_to_bd; + uint8_t next_input_key; + uint16_t rule_id; +}; + +struct hns3_flow_counter { + LIST_ENTRY(hns3_flow_counter) next; /* Pointer to the next counter. */ + uint32_t shared:1; /* Share counter ID with other flow rules. */ + uint32_t ref_cnt:31; /* Reference counter. */ + uint16_t id; /* Counter ID. */ + uint64_t hits; /* Number of packets matched by the rule. */ +}; + +#define HNS3_RULE_FLAG_FDID 0x1 +#define HNS3_RULE_FLAG_VF_ID 0x2 +#define HNS3_RULE_FLAG_COUNTER 0x4 + +struct hns3_fdir_key_conf { + struct hns3_fd_rule_tuples spec; + struct hns3_fd_rule_tuples mask; + uint8_t vlan_num; + uint8_t outer_vlan_num; +}; + +struct hns3_fdir_rule { + struct hns3_fdir_key_conf key_conf; + uint32_t input_set; + uint32_t flags; + uint32_t fd_id; /* APP marked unique value for this rule. */ + uint8_t action; + /* VF id, avaiblable when flags with HNS3_RULE_FLAG_VF_ID. */ + uint8_t vf_id; + uint16_t queue_id; + uint16_t location; + struct rte_flow_action_count act_cnt; +}; + +/* FDIR filter list structure */ +struct hns3_fdir_rule_ele { + TAILQ_ENTRY(hns3_fdir_rule_ele) entries; + struct hns3_fdir_rule fdir_conf; +}; + +/* rss filter list structure */ +struct hns3_rss_conf_ele { + TAILQ_ENTRY(hns3_rss_conf_ele) entries; + struct hns3_rss_conf filter_info; +}; + +/* hns3_flow memory list structure */ +struct hns3_flow_mem { + TAILQ_ENTRY(hns3_flow_mem) entries; + struct rte_flow *flow; +}; + +TAILQ_HEAD(hns3_fdir_rule_list, hns3_fdir_rule_ele); +TAILQ_HEAD(hns3_rss_filter_list, hns3_rss_conf_ele); +TAILQ_HEAD(hns3_flow_mem_list, hns3_flow_mem); + +struct hns3_process_private { + struct hns3_fdir_rule_list fdir_list; + struct hns3_rss_filter_list filter_rss_list; + struct hns3_flow_mem_list flow_list; +}; + +/* + * A structure used to define fields of a FDIR related info. + */ +struct hns3_fdir_info { + rte_spinlock_t flows_lock; + struct hns3_fdir_rule_list fdir_list; + struct hns3_fdir_rule_ele **hash_map; + struct rte_hash *hash_handle; + struct hns3_fd_cfg fd_cfg; +}; + +struct rte_flow { + enum rte_filter_type filter_type; + void *rule; + uint32_t counter_id; +}; +struct hns3_adapter; + +int hns3_init_fd_config(struct hns3_adapter *hns); +int hns3_fdir_filter_init(struct hns3_adapter *hns); +void hns3_fdir_filter_uninit(struct hns3_adapter *hns); +int hns3_fdir_filter_program(struct hns3_adapter *hns, + struct hns3_fdir_rule *rule, bool del); +int hns3_clear_all_fdir_filter(struct hns3_adapter *hns); +int hns3_get_count(struct hns3_hw *hw, uint32_t id, uint64_t *value); +void hns3_filterlist_init(struct rte_eth_dev *dev); +int hns3_restore_all_fdir_filter(struct hns3_adapter *hns); + +#endif /* _HNS3_FDIR_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_flow.c b/src/spdk/dpdk/drivers/net/hns3/hns3_flow.c new file mode 100644 index 000000000..aef301a8a --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_flow.c @@ -0,0 +1,1923 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdbool.h> +#include <sys/queue.h> +#include <rte_flow_driver.h> +#include <rte_io.h> +#include <rte_malloc.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" + +/* Default default keys */ +static uint8_t hns3_hash_key[] = { + 0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2, + 0x41, 0x67, 0x25, 0x3D, 0x43, 0xA3, 0x8F, 0xB0, + 0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4, + 0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C, + 0x6A, 0x42, 0xB7, 0x3B, 0xBE, 0xAC, 0x01, 0xFA +}; + +static const uint8_t full_mask[VNI_OR_TNI_LEN] = { 0xFF, 0xFF, 0xFF }; +static const uint8_t zero_mask[VNI_OR_TNI_LEN] = { 0x00, 0x00, 0x00 }; + +/* Special Filter id for non-specific packet flagging. Don't change value */ +#define HNS3_MAX_FILTER_ID 0x0FFF + +#define ETHER_TYPE_MASK 0xFFFF +#define IPPROTO_MASK 0xFF +#define TUNNEL_TYPE_MASK 0xFFFF + +#define HNS3_TUNNEL_TYPE_VXLAN 0x12B5 +#define HNS3_TUNNEL_TYPE_VXLAN_GPE 0x12B6 +#define HNS3_TUNNEL_TYPE_GENEVE 0x17C1 +#define HNS3_TUNNEL_TYPE_NVGRE 0x6558 + +static enum rte_flow_item_type first_items[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_SCTP, + RTE_FLOW_ITEM_TYPE_ICMP, + RTE_FLOW_ITEM_TYPE_NVGRE, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_GENEVE, + RTE_FLOW_ITEM_TYPE_VXLAN_GPE, + RTE_FLOW_ITEM_TYPE_MPLS +}; + +static enum rte_flow_item_type L2_next_items[] = { + RTE_FLOW_ITEM_TYPE_VLAN, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_IPV6 +}; + +static enum rte_flow_item_type L3_next_items[] = { + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_SCTP, + RTE_FLOW_ITEM_TYPE_NVGRE, + RTE_FLOW_ITEM_TYPE_ICMP +}; + +static enum rte_flow_item_type L4_next_items[] = { + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_GENEVE, + RTE_FLOW_ITEM_TYPE_VXLAN_GPE, + RTE_FLOW_ITEM_TYPE_MPLS +}; + +static enum rte_flow_item_type tunnel_next_items[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_VLAN +}; + +struct items_step_mngr { + enum rte_flow_item_type *items; + int count; +}; + +static inline void +net_addr_to_host(uint32_t *dst, const rte_be32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + dst[i] = rte_be_to_cpu_32(src[i]); +} + +static inline const struct rte_flow_action * +find_rss_action(const struct rte_flow_action actions[]) +{ + const struct rte_flow_action *next = &actions[0]; + + for (; next->type != RTE_FLOW_ACTION_TYPE_END; next++) { + if (next->type == RTE_FLOW_ACTION_TYPE_RSS) + return next; + } + return NULL; +} + +static inline struct hns3_flow_counter * +hns3_counter_lookup(struct rte_eth_dev *dev, uint32_t id) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_pf *pf = &hns->pf; + struct hns3_flow_counter *cnt; + + LIST_FOREACH(cnt, &pf->flow_counters, next) { + if (cnt->id == id) + return cnt; + } + return NULL; +} + +static int +hns3_counter_new(struct rte_eth_dev *dev, uint32_t shared, uint32_t id, + struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_pf *pf = &hns->pf; + struct hns3_flow_counter *cnt; + + cnt = hns3_counter_lookup(dev, id); + if (cnt) { + if (!cnt->shared || cnt->shared != shared) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, + cnt, + "Counter id is used,shared flag not match"); + cnt->ref_cnt++; + return 0; + } + + cnt = rte_zmalloc("hns3 counter", sizeof(*cnt), 0); + if (cnt == NULL) + return rte_flow_error_set(error, ENOMEM, + RTE_FLOW_ERROR_TYPE_ACTION, cnt, + "Alloc mem for counter failed"); + cnt->id = id; + cnt->shared = shared; + cnt->ref_cnt = 1; + cnt->hits = 0; + LIST_INSERT_HEAD(&pf->flow_counters, cnt, next); + return 0; +} + +static int +hns3_counter_query(struct rte_eth_dev *dev, struct rte_flow *flow, + struct rte_flow_query_count *qc, + struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_flow_counter *cnt; + uint64_t value; + int ret; + + /* FDIR is available only in PF driver */ + if (hns->is_vf) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Fdir is not supported in VF"); + cnt = hns3_counter_lookup(dev, flow->counter_id); + if (cnt == NULL) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Can't find counter id"); + + ret = hns3_get_count(&hns->hw, flow->counter_id, &value); + if (ret) { + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "Read counter fail."); + return ret; + } + qc->hits_set = 1; + qc->hits = value; + + return 0; +} + +static int +hns3_counter_release(struct rte_eth_dev *dev, uint32_t id) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_flow_counter *cnt; + + cnt = hns3_counter_lookup(dev, id); + if (cnt == NULL) { + hns3_err(hw, "Can't find available counter to release"); + return -EINVAL; + } + cnt->ref_cnt--; + if (cnt->ref_cnt == 0) { + LIST_REMOVE(cnt, next); + rte_free(cnt); + } + return 0; +} + +static void +hns3_counter_flush(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_pf *pf = &hns->pf; + struct hns3_flow_counter *cnt_ptr; + + cnt_ptr = LIST_FIRST(&pf->flow_counters); + while (cnt_ptr) { + LIST_REMOVE(cnt_ptr, next); + rte_free(cnt_ptr); + cnt_ptr = LIST_FIRST(&pf->flow_counters); + } +} + +static int +hns3_handle_action_queue(struct rte_eth_dev *dev, + const struct rte_flow_action *action, + struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + const struct rte_flow_action_queue *queue; + struct hns3_hw *hw = &hns->hw; + + queue = (const struct rte_flow_action_queue *)action->conf; + if (queue->index >= hw->used_rx_queues) { + hns3_err(hw, "queue ID(%d) is greater than number of " + "available queue (%d) in driver.", + queue->index, hw->used_rx_queues); + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, action, + "Invalid queue ID in PF"); + } + + rule->queue_id = queue->index; + rule->action = HNS3_FD_ACTION_ACCEPT_PACKET; + return 0; +} + +/* + * Parse actions structure from the provided pattern. + * The pattern is validated as the items are copied. + * + * @param actions[in] + * @param rule[out] + * NIC specfilc actions derived from the actions. + * @param error[out] + */ +static int +hns3_handle_actions(struct rte_eth_dev *dev, + const struct rte_flow_action actions[], + struct hns3_fdir_rule *rule, struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + const struct rte_flow_action_count *act_count; + const struct rte_flow_action_mark *mark; + struct hns3_pf *pf = &hns->pf; + uint32_t counter_num; + int ret; + + for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) { + switch (actions->type) { + case RTE_FLOW_ACTION_TYPE_QUEUE: + ret = hns3_handle_action_queue(dev, actions, rule, + error); + if (ret) + return ret; + break; + case RTE_FLOW_ACTION_TYPE_DROP: + rule->action = HNS3_FD_ACTION_DROP_PACKET; + break; + case RTE_FLOW_ACTION_TYPE_MARK: + mark = + (const struct rte_flow_action_mark *)actions->conf; + if (mark->id >= HNS3_MAX_FILTER_ID) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + actions, + "Invalid Mark ID"); + rule->fd_id = mark->id; + rule->flags |= HNS3_RULE_FLAG_FDID; + break; + case RTE_FLOW_ACTION_TYPE_FLAG: + rule->fd_id = HNS3_MAX_FILTER_ID; + rule->flags |= HNS3_RULE_FLAG_FDID; + break; + case RTE_FLOW_ACTION_TYPE_COUNT: + act_count = + (const struct rte_flow_action_count *)actions->conf; + counter_num = pf->fdir.fd_cfg.cnt_num[HNS3_FD_STAGE_1]; + if (act_count->id >= counter_num) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + actions, + "Invalid counter id"); + rule->act_cnt = *act_count; + rule->flags |= HNS3_RULE_FLAG_COUNTER; + break; + case RTE_FLOW_ACTION_TYPE_VOID: + break; + default: + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, + NULL, "Unsupported action"); + } + } + + return 0; +} + +/* Parse to get the attr and action info of flow director rule. */ +static int +hns3_check_attr(const struct rte_flow_attr *attr, struct rte_flow_error *error) +{ + if (!attr->ingress) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, + attr, "Ingress can't be zero"); + if (attr->egress) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, + attr, "Not support egress"); + if (attr->transfer) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER, + attr, "No support for transfer"); + if (attr->priority) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, + attr, "Not support priority"); + if (attr->group) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ATTR_GROUP, + attr, "Not support group"); + return 0; +} + +static int +hns3_parse_eth(const struct rte_flow_item *item, + struct hns3_fdir_rule *rule, struct rte_flow_error *error) +{ + const struct rte_flow_item_eth *eth_spec; + const struct rte_flow_item_eth *eth_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + eth_mask = item->mask; + if (eth_mask->type) { + hns3_set_bit(rule->input_set, INNER_ETH_TYPE, 1); + rule->key_conf.mask.ether_type = + rte_be_to_cpu_16(eth_mask->type); + } + if (!rte_is_zero_ether_addr(ð_mask->src)) { + hns3_set_bit(rule->input_set, INNER_SRC_MAC, 1); + memcpy(rule->key_conf.mask.src_mac, + eth_mask->src.addr_bytes, RTE_ETHER_ADDR_LEN); + } + if (!rte_is_zero_ether_addr(ð_mask->dst)) { + hns3_set_bit(rule->input_set, INNER_DST_MAC, 1); + memcpy(rule->key_conf.mask.dst_mac, + eth_mask->dst.addr_bytes, RTE_ETHER_ADDR_LEN); + } + } + + eth_spec = item->spec; + rule->key_conf.spec.ether_type = rte_be_to_cpu_16(eth_spec->type); + memcpy(rule->key_conf.spec.src_mac, eth_spec->src.addr_bytes, + RTE_ETHER_ADDR_LEN); + memcpy(rule->key_conf.spec.dst_mac, eth_spec->dst.addr_bytes, + RTE_ETHER_ADDR_LEN); + return 0; +} + +static int +hns3_parse_vlan(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_vlan *vlan_spec; + const struct rte_flow_item_vlan *vlan_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + rule->key_conf.vlan_num++; + if (rule->key_conf.vlan_num > VLAN_TAG_NUM_MAX) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Vlan_num is more than 2"); + + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + vlan_mask = item->mask; + if (vlan_mask->tci) { + if (rule->key_conf.vlan_num == 1) { + hns3_set_bit(rule->input_set, INNER_VLAN_TAG1, + 1); + rule->key_conf.mask.vlan_tag1 = + rte_be_to_cpu_16(vlan_mask->tci); + } else { + hns3_set_bit(rule->input_set, INNER_VLAN_TAG2, + 1); + rule->key_conf.mask.vlan_tag2 = + rte_be_to_cpu_16(vlan_mask->tci); + } + } + } + + vlan_spec = item->spec; + if (rule->key_conf.vlan_num == 1) + rule->key_conf.spec.vlan_tag1 = + rte_be_to_cpu_16(vlan_spec->tci); + else + rule->key_conf.spec.vlan_tag2 = + rte_be_to_cpu_16(vlan_spec->tci); + return 0; +} + +static int +hns3_parse_ipv4(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_ipv4 *ipv4_spec; + const struct rte_flow_item_ipv4 *ipv4_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + hns3_set_bit(rule->input_set, INNER_ETH_TYPE, 1); + rule->key_conf.spec.ether_type = RTE_ETHER_TYPE_IPV4; + rule->key_conf.mask.ether_type = ETHER_TYPE_MASK; + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + ipv4_mask = item->mask; + + if (ipv4_mask->hdr.total_length || + ipv4_mask->hdr.packet_id || + ipv4_mask->hdr.fragment_offset || + ipv4_mask->hdr.time_to_live || + ipv4_mask->hdr.hdr_checksum) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Only support src & dst ip,tos,proto in IPV4"); + } + + if (ipv4_mask->hdr.src_addr) { + hns3_set_bit(rule->input_set, INNER_SRC_IP, 1); + rule->key_conf.mask.src_ip[IP_ADDR_KEY_ID] = + rte_be_to_cpu_32(ipv4_mask->hdr.src_addr); + } + + if (ipv4_mask->hdr.dst_addr) { + hns3_set_bit(rule->input_set, INNER_DST_IP, 1); + rule->key_conf.mask.dst_ip[IP_ADDR_KEY_ID] = + rte_be_to_cpu_32(ipv4_mask->hdr.dst_addr); + } + + if (ipv4_mask->hdr.type_of_service) { + hns3_set_bit(rule->input_set, INNER_IP_TOS, 1); + rule->key_conf.mask.ip_tos = + ipv4_mask->hdr.type_of_service; + } + + if (ipv4_mask->hdr.next_proto_id) { + hns3_set_bit(rule->input_set, INNER_IP_PROTO, 1); + rule->key_conf.mask.ip_proto = + ipv4_mask->hdr.next_proto_id; + } + } + + ipv4_spec = item->spec; + rule->key_conf.spec.src_ip[IP_ADDR_KEY_ID] = + rte_be_to_cpu_32(ipv4_spec->hdr.src_addr); + rule->key_conf.spec.dst_ip[IP_ADDR_KEY_ID] = + rte_be_to_cpu_32(ipv4_spec->hdr.dst_addr); + rule->key_conf.spec.ip_tos = ipv4_spec->hdr.type_of_service; + rule->key_conf.spec.ip_proto = ipv4_spec->hdr.next_proto_id; + return 0; +} + +static int +hns3_parse_ipv6(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_ipv6 *ipv6_spec; + const struct rte_flow_item_ipv6 *ipv6_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + hns3_set_bit(rule->input_set, INNER_ETH_TYPE, 1); + rule->key_conf.spec.ether_type = RTE_ETHER_TYPE_IPV6; + rule->key_conf.mask.ether_type = ETHER_TYPE_MASK; + + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + ipv6_mask = item->mask; + if (ipv6_mask->hdr.vtc_flow || + ipv6_mask->hdr.payload_len || ipv6_mask->hdr.hop_limits) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Only support src & dst ip,proto in IPV6"); + } + net_addr_to_host(rule->key_conf.mask.src_ip, + (const rte_be32_t *)ipv6_mask->hdr.src_addr, + IP_ADDR_LEN); + net_addr_to_host(rule->key_conf.mask.dst_ip, + (const rte_be32_t *)ipv6_mask->hdr.dst_addr, + IP_ADDR_LEN); + rule->key_conf.mask.ip_proto = ipv6_mask->hdr.proto; + if (rule->key_conf.mask.src_ip[IP_ADDR_KEY_ID]) + hns3_set_bit(rule->input_set, INNER_SRC_IP, 1); + if (rule->key_conf.mask.dst_ip[IP_ADDR_KEY_ID]) + hns3_set_bit(rule->input_set, INNER_DST_IP, 1); + if (ipv6_mask->hdr.proto) + hns3_set_bit(rule->input_set, INNER_IP_PROTO, 1); + } + + ipv6_spec = item->spec; + net_addr_to_host(rule->key_conf.spec.src_ip, + (const rte_be32_t *)ipv6_spec->hdr.src_addr, + IP_ADDR_LEN); + net_addr_to_host(rule->key_conf.spec.dst_ip, + (const rte_be32_t *)ipv6_spec->hdr.dst_addr, + IP_ADDR_LEN); + rule->key_conf.spec.ip_proto = ipv6_spec->hdr.proto; + + return 0; +} + +static int +hns3_parse_tcp(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_tcp *tcp_spec; + const struct rte_flow_item_tcp *tcp_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + hns3_set_bit(rule->input_set, INNER_IP_PROTO, 1); + rule->key_conf.spec.ip_proto = IPPROTO_TCP; + rule->key_conf.mask.ip_proto = IPPROTO_MASK; + + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + tcp_mask = item->mask; + if (tcp_mask->hdr.sent_seq || + tcp_mask->hdr.recv_ack || + tcp_mask->hdr.data_off || + tcp_mask->hdr.tcp_flags || + tcp_mask->hdr.rx_win || + tcp_mask->hdr.cksum || tcp_mask->hdr.tcp_urp) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Only support src & dst port in TCP"); + } + + if (tcp_mask->hdr.src_port) { + hns3_set_bit(rule->input_set, INNER_SRC_PORT, 1); + rule->key_conf.mask.src_port = + rte_be_to_cpu_16(tcp_mask->hdr.src_port); + } + if (tcp_mask->hdr.dst_port) { + hns3_set_bit(rule->input_set, INNER_DST_PORT, 1); + rule->key_conf.mask.dst_port = + rte_be_to_cpu_16(tcp_mask->hdr.dst_port); + } + } + + tcp_spec = item->spec; + rule->key_conf.spec.src_port = rte_be_to_cpu_16(tcp_spec->hdr.src_port); + rule->key_conf.spec.dst_port = rte_be_to_cpu_16(tcp_spec->hdr.dst_port); + + return 0; +} + +static int +hns3_parse_udp(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_udp *udp_spec; + const struct rte_flow_item_udp *udp_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + hns3_set_bit(rule->input_set, INNER_IP_PROTO, 1); + rule->key_conf.spec.ip_proto = IPPROTO_UDP; + rule->key_conf.mask.ip_proto = IPPROTO_MASK; + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + udp_mask = item->mask; + if (udp_mask->hdr.dgram_len || udp_mask->hdr.dgram_cksum) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Only support src & dst port in UDP"); + } + if (udp_mask->hdr.src_port) { + hns3_set_bit(rule->input_set, INNER_SRC_PORT, 1); + rule->key_conf.mask.src_port = + rte_be_to_cpu_16(udp_mask->hdr.src_port); + } + if (udp_mask->hdr.dst_port) { + hns3_set_bit(rule->input_set, INNER_DST_PORT, 1); + rule->key_conf.mask.dst_port = + rte_be_to_cpu_16(udp_mask->hdr.dst_port); + } + } + + udp_spec = item->spec; + rule->key_conf.spec.src_port = rte_be_to_cpu_16(udp_spec->hdr.src_port); + rule->key_conf.spec.dst_port = rte_be_to_cpu_16(udp_spec->hdr.dst_port); + + return 0; +} + +static int +hns3_parse_sctp(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_sctp *sctp_spec; + const struct rte_flow_item_sctp *sctp_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + + hns3_set_bit(rule->input_set, INNER_IP_PROTO, 1); + rule->key_conf.spec.ip_proto = IPPROTO_SCTP; + rule->key_conf.mask.ip_proto = IPPROTO_MASK; + + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (item->mask) { + sctp_mask = item->mask; + if (sctp_mask->hdr.cksum) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Only support src & dst port in SCTP"); + + if (sctp_mask->hdr.src_port) { + hns3_set_bit(rule->input_set, INNER_SRC_PORT, 1); + rule->key_conf.mask.src_port = + rte_be_to_cpu_16(sctp_mask->hdr.src_port); + } + if (sctp_mask->hdr.dst_port) { + hns3_set_bit(rule->input_set, INNER_DST_PORT, 1); + rule->key_conf.mask.dst_port = + rte_be_to_cpu_16(sctp_mask->hdr.dst_port); + } + if (sctp_mask->hdr.tag) { + hns3_set_bit(rule->input_set, INNER_SCTP_TAG, 1); + rule->key_conf.mask.sctp_tag = + rte_be_to_cpu_32(sctp_mask->hdr.tag); + } + } + + sctp_spec = item->spec; + rule->key_conf.spec.src_port = + rte_be_to_cpu_16(sctp_spec->hdr.src_port); + rule->key_conf.spec.dst_port = + rte_be_to_cpu_16(sctp_spec->hdr.dst_port); + rule->key_conf.spec.sctp_tag = rte_be_to_cpu_32(sctp_spec->hdr.tag); + + return 0; +} + +/* + * Check items before tunnel, save inner configs to outer configs,and clear + * inner configs. + * The key consists of two parts: meta_data and tuple keys. + * Meta data uses 15 bits, including vlan_num(2bit), des_port(12bit) and tunnel + * packet(1bit). + * Tuple keys uses 384bit, including ot_dst-mac(48bit), ot_dst-port(16bit), + * ot_tun_vni(24bit), ot_flow_id(8bit), src-mac(48bit), dst-mac(48bit), + * src-ip(32/128bit), dst-ip(32/128bit), src-port(16bit), dst-port(16bit), + * tos(8bit), ether-proto(16bit), ip-proto(8bit), vlantag1(16bit), + * Vlantag2(16bit) and sctp-tag(32bit). + */ +static int +hns3_handle_tunnel(const struct rte_flow_item *item, + struct hns3_fdir_rule *rule, struct rte_flow_error *error) +{ + /* check eth config */ + if (rule->input_set & (BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC))) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, "Outer eth mac is unsupported"); + if (rule->input_set & BIT(INNER_ETH_TYPE)) { + hns3_set_bit(rule->input_set, OUTER_ETH_TYPE, 1); + rule->key_conf.spec.outer_ether_type = + rule->key_conf.spec.ether_type; + rule->key_conf.mask.outer_ether_type = + rule->key_conf.mask.ether_type; + hns3_set_bit(rule->input_set, INNER_ETH_TYPE, 0); + rule->key_conf.spec.ether_type = 0; + rule->key_conf.mask.ether_type = 0; + } + + /* check vlan config */ + if (rule->input_set & (BIT(INNER_VLAN_TAG1) | BIT(INNER_VLAN_TAG2))) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Outer vlan tags is unsupported"); + + /* clear vlan_num for inner vlan select */ + rule->key_conf.outer_vlan_num = rule->key_conf.vlan_num; + rule->key_conf.vlan_num = 0; + + /* check L3 config */ + if (rule->input_set & + (BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) | BIT(INNER_IP_TOS))) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, "Outer ip is unsupported"); + if (rule->input_set & BIT(INNER_IP_PROTO)) { + hns3_set_bit(rule->input_set, OUTER_IP_PROTO, 1); + rule->key_conf.spec.outer_proto = rule->key_conf.spec.ip_proto; + rule->key_conf.mask.outer_proto = rule->key_conf.mask.ip_proto; + hns3_set_bit(rule->input_set, INNER_IP_PROTO, 0); + rule->key_conf.spec.ip_proto = 0; + rule->key_conf.mask.ip_proto = 0; + } + + /* check L4 config */ + if (rule->input_set & BIT(INNER_SCTP_TAG)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Outer sctp tag is unsupported"); + + if (rule->input_set & BIT(INNER_SRC_PORT)) { + hns3_set_bit(rule->input_set, OUTER_SRC_PORT, 1); + rule->key_conf.spec.outer_src_port = + rule->key_conf.spec.src_port; + rule->key_conf.mask.outer_src_port = + rule->key_conf.mask.src_port; + hns3_set_bit(rule->input_set, INNER_SRC_PORT, 0); + rule->key_conf.spec.src_port = 0; + rule->key_conf.mask.src_port = 0; + } + if (rule->input_set & BIT(INNER_DST_PORT)) { + hns3_set_bit(rule->input_set, INNER_DST_PORT, 0); + rule->key_conf.spec.dst_port = 0; + rule->key_conf.mask.dst_port = 0; + } + return 0; +} + +static int +hns3_parse_vxlan(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_vxlan *vxlan_spec; + const struct rte_flow_item_vxlan *vxlan_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + else if (item->spec && (item->mask == NULL)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Tunnel packets must configure with mask"); + + hns3_set_bit(rule->input_set, OUTER_DST_PORT, 1); + rule->key_conf.mask.tunnel_type = TUNNEL_TYPE_MASK; + if (item->type == RTE_FLOW_ITEM_TYPE_VXLAN) + rule->key_conf.spec.tunnel_type = HNS3_TUNNEL_TYPE_VXLAN; + else + rule->key_conf.spec.tunnel_type = HNS3_TUNNEL_TYPE_VXLAN_GPE; + + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + vxlan_mask = item->mask; + vxlan_spec = item->spec; + + if (vxlan_mask->flags) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Flags is not supported in VxLAN"); + + /* VNI must be totally masked or not. */ + if (memcmp(vxlan_mask->vni, full_mask, VNI_OR_TNI_LEN) && + memcmp(vxlan_mask->vni, zero_mask, VNI_OR_TNI_LEN)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "VNI must be totally masked or not in VxLAN"); + if (vxlan_mask->vni[0]) { + hns3_set_bit(rule->input_set, OUTER_TUN_VNI, 1); + memcpy(rule->key_conf.mask.outer_tun_vni, vxlan_mask->vni, + VNI_OR_TNI_LEN); + } + memcpy(rule->key_conf.spec.outer_tun_vni, vxlan_spec->vni, + VNI_OR_TNI_LEN); + return 0; +} + +static int +hns3_parse_nvgre(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_nvgre *nvgre_spec; + const struct rte_flow_item_nvgre *nvgre_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + else if (item->spec && (item->mask == NULL)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Tunnel packets must configure with mask"); + + hns3_set_bit(rule->input_set, OUTER_IP_PROTO, 1); + rule->key_conf.spec.outer_proto = IPPROTO_GRE; + rule->key_conf.mask.outer_proto = IPPROTO_MASK; + + hns3_set_bit(rule->input_set, OUTER_DST_PORT, 1); + rule->key_conf.spec.tunnel_type = HNS3_TUNNEL_TYPE_NVGRE; + rule->key_conf.mask.tunnel_type = ~HNS3_TUNNEL_TYPE_NVGRE; + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + nvgre_mask = item->mask; + nvgre_spec = item->spec; + + if (nvgre_mask->protocol || nvgre_mask->c_k_s_rsvd0_ver) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Ver/protocal is not supported in NVGRE"); + + /* TNI must be totally masked or not. */ + if (memcmp(nvgre_mask->tni, full_mask, VNI_OR_TNI_LEN) && + memcmp(nvgre_mask->tni, zero_mask, VNI_OR_TNI_LEN)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "TNI must be totally masked or not in NVGRE"); + + if (nvgre_mask->tni[0]) { + hns3_set_bit(rule->input_set, OUTER_TUN_VNI, 1); + memcpy(rule->key_conf.mask.outer_tun_vni, nvgre_mask->tni, + VNI_OR_TNI_LEN); + } + memcpy(rule->key_conf.spec.outer_tun_vni, nvgre_spec->tni, + VNI_OR_TNI_LEN); + + if (nvgre_mask->flow_id) { + hns3_set_bit(rule->input_set, OUTER_TUN_FLOW_ID, 1); + rule->key_conf.mask.outer_tun_flow_id = nvgre_mask->flow_id; + } + rule->key_conf.spec.outer_tun_flow_id = nvgre_spec->flow_id; + return 0; +} + +static int +hns3_parse_geneve(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + const struct rte_flow_item_geneve *geneve_spec; + const struct rte_flow_item_geneve *geneve_mask; + + if (item->spec == NULL && item->mask) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Can't configure FDIR with mask but without spec"); + else if (item->spec && (item->mask == NULL)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Tunnel packets must configure with mask"); + + hns3_set_bit(rule->input_set, OUTER_DST_PORT, 1); + rule->key_conf.spec.tunnel_type = HNS3_TUNNEL_TYPE_GENEVE; + rule->key_conf.mask.tunnel_type = TUNNEL_TYPE_MASK; + /* Only used to describe the protocol stack. */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + geneve_mask = item->mask; + geneve_spec = item->spec; + + if (geneve_mask->ver_opt_len_o_c_rsvd0 || geneve_mask->protocol) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Ver/protocal is not supported in GENEVE"); + /* VNI must be totally masked or not. */ + if (memcmp(geneve_mask->vni, full_mask, VNI_OR_TNI_LEN) && + memcmp(geneve_mask->vni, zero_mask, VNI_OR_TNI_LEN)) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "VNI must be totally masked or not in GENEVE"); + if (geneve_mask->vni[0]) { + hns3_set_bit(rule->input_set, OUTER_TUN_VNI, 1); + memcpy(rule->key_conf.mask.outer_tun_vni, geneve_mask->vni, + VNI_OR_TNI_LEN); + } + memcpy(rule->key_conf.spec.outer_tun_vni, geneve_spec->vni, + VNI_OR_TNI_LEN); + return 0; +} + +static int +hns3_parse_tunnel(const struct rte_flow_item *item, struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + int ret; + + switch (item->type) { + case RTE_FLOW_ITEM_TYPE_VXLAN: + case RTE_FLOW_ITEM_TYPE_VXLAN_GPE: + ret = hns3_parse_vxlan(item, rule, error); + break; + case RTE_FLOW_ITEM_TYPE_NVGRE: + ret = hns3_parse_nvgre(item, rule, error); + break; + case RTE_FLOW_ITEM_TYPE_GENEVE: + ret = hns3_parse_geneve(item, rule, error); + break; + default: + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Unsupported tunnel type!"); + } + if (ret) + return ret; + return hns3_handle_tunnel(item, rule, error); +} + +static int +hns3_parse_normal(const struct rte_flow_item *item, + struct hns3_fdir_rule *rule, + struct items_step_mngr *step_mngr, + struct rte_flow_error *error) +{ + int ret; + + switch (item->type) { + case RTE_FLOW_ITEM_TYPE_ETH: + ret = hns3_parse_eth(item, rule, error); + step_mngr->items = L2_next_items; + step_mngr->count = ARRAY_SIZE(L2_next_items); + break; + case RTE_FLOW_ITEM_TYPE_VLAN: + ret = hns3_parse_vlan(item, rule, error); + step_mngr->items = L2_next_items; + step_mngr->count = ARRAY_SIZE(L2_next_items); + break; + case RTE_FLOW_ITEM_TYPE_IPV4: + ret = hns3_parse_ipv4(item, rule, error); + step_mngr->items = L3_next_items; + step_mngr->count = ARRAY_SIZE(L3_next_items); + break; + case RTE_FLOW_ITEM_TYPE_IPV6: + ret = hns3_parse_ipv6(item, rule, error); + step_mngr->items = L3_next_items; + step_mngr->count = ARRAY_SIZE(L3_next_items); + break; + case RTE_FLOW_ITEM_TYPE_TCP: + ret = hns3_parse_tcp(item, rule, error); + step_mngr->items = L4_next_items; + step_mngr->count = ARRAY_SIZE(L4_next_items); + break; + case RTE_FLOW_ITEM_TYPE_UDP: + ret = hns3_parse_udp(item, rule, error); + step_mngr->items = L4_next_items; + step_mngr->count = ARRAY_SIZE(L4_next_items); + break; + case RTE_FLOW_ITEM_TYPE_SCTP: + ret = hns3_parse_sctp(item, rule, error); + step_mngr->items = L4_next_items; + step_mngr->count = ARRAY_SIZE(L4_next_items); + break; + default: + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Unsupported normal type!"); + } + + return ret; +} + +static int +hns3_validate_item(const struct rte_flow_item *item, + struct items_step_mngr step_mngr, + struct rte_flow_error *error) +{ + int i; + + if (item->last) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, item, + "Not supported last point for range"); + + for (i = 0; i < step_mngr.count; i++) { + if (item->type == step_mngr.items[i]) + break; + } + + if (i == step_mngr.count) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, + item, "Inval or missing item"); + } + return 0; +} + +static inline bool +is_tunnel_packet(enum rte_flow_item_type type) +{ + if (type == RTE_FLOW_ITEM_TYPE_VXLAN_GPE || + type == RTE_FLOW_ITEM_TYPE_VXLAN || + type == RTE_FLOW_ITEM_TYPE_NVGRE || + type == RTE_FLOW_ITEM_TYPE_GENEVE || + type == RTE_FLOW_ITEM_TYPE_MPLS) + return true; + return false; +} + +/* + * Parse the rule to see if it is a IP or MAC VLAN flow director rule. + * And get the flow director filter info BTW. + * UDP/TCP/SCTP PATTERN: + * The first not void item can be ETH or IPV4 or IPV6 + * The second not void item must be IPV4 or IPV6 if the first one is ETH. + * The next not void item could be UDP or TCP or SCTP (optional) + * The next not void item could be RAW (for flexbyte, optional) + * The next not void item must be END. + * A Fuzzy Match pattern can appear at any place before END. + * Fuzzy Match is optional for IPV4 but is required for IPV6 + * MAC VLAN PATTERN: + * The first not void item must be ETH. + * The second not void item must be MAC VLAN. + * The next not void item must be END. + * ACTION: + * The first not void action should be QUEUE or DROP. + * The second not void optional action should be MARK, + * mark_id is a uint32_t number. + * The next not void action should be END. + * UDP/TCP/SCTP pattern example: + * ITEM Spec Mask + * ETH NULL NULL + * IPV4 src_addr 192.168.1.20 0xFFFFFFFF + * dst_addr 192.167.3.50 0xFFFFFFFF + * UDP/TCP/SCTP src_port 80 0xFFFF + * dst_port 80 0xFFFF + * END + * MAC VLAN pattern example: + * ITEM Spec Mask + * ETH dst_addr + {0xAC, 0x7B, 0xA1, {0xFF, 0xFF, 0xFF, + 0x2C, 0x6D, 0x36} 0xFF, 0xFF, 0xFF} + * MAC VLAN tci 0x2016 0xEFFF + * END + * Other members in mask and spec should set to 0x00. + * Item->last should be NULL. + */ +static int +hns3_parse_fdir_filter(struct rte_eth_dev *dev, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct hns3_fdir_rule *rule, + struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + const struct rte_flow_item *item; + struct items_step_mngr step_mngr; + int ret; + + /* FDIR is available only in PF driver */ + if (hns->is_vf) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Fdir not supported in VF"); + + if (dev->data->dev_conf.fdir_conf.mode != RTE_FDIR_MODE_PERFECT) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ITEM_NUM, NULL, + "fdir_conf.mode isn't perfect"); + + step_mngr.items = first_items; + step_mngr.count = ARRAY_SIZE(first_items); + for (item = pattern; item->type != RTE_FLOW_ITEM_TYPE_END; item++) { + if (item->type == RTE_FLOW_ITEM_TYPE_VOID) + continue; + + ret = hns3_validate_item(item, step_mngr, error); + if (ret) + return ret; + + if (is_tunnel_packet(item->type)) { + ret = hns3_parse_tunnel(item, rule, error); + if (ret) + return ret; + step_mngr.items = tunnel_next_items; + step_mngr.count = ARRAY_SIZE(tunnel_next_items); + } else { + ret = hns3_parse_normal(item, rule, &step_mngr, error); + if (ret) + return ret; + } + } + + return hns3_handle_actions(dev, actions, rule, error); +} + +void +hns3_filterlist_init(struct rte_eth_dev *dev) +{ + struct hns3_process_private *process_list = dev->process_private; + + TAILQ_INIT(&process_list->fdir_list); + TAILQ_INIT(&process_list->filter_rss_list); + TAILQ_INIT(&process_list->flow_list); +} + +static void +hns3_filterlist_flush(struct rte_eth_dev *dev) +{ + struct hns3_process_private *process_list = dev->process_private; + struct hns3_fdir_rule_ele *fdir_rule_ptr; + struct hns3_rss_conf_ele *rss_filter_ptr; + struct hns3_flow_mem *flow_node; + + fdir_rule_ptr = TAILQ_FIRST(&process_list->fdir_list); + while (fdir_rule_ptr) { + TAILQ_REMOVE(&process_list->fdir_list, fdir_rule_ptr, entries); + rte_free(fdir_rule_ptr); + fdir_rule_ptr = TAILQ_FIRST(&process_list->fdir_list); + } + + rss_filter_ptr = TAILQ_FIRST(&process_list->filter_rss_list); + while (rss_filter_ptr) { + TAILQ_REMOVE(&process_list->filter_rss_list, rss_filter_ptr, + entries); + rte_free(rss_filter_ptr); + rss_filter_ptr = TAILQ_FIRST(&process_list->filter_rss_list); + } + + flow_node = TAILQ_FIRST(&process_list->flow_list); + while (flow_node) { + TAILQ_REMOVE(&process_list->flow_list, flow_node, entries); + rte_free(flow_node->flow); + rte_free(flow_node); + flow_node = TAILQ_FIRST(&process_list->flow_list); + } +} + +static bool +hns3_action_rss_same(const struct rte_flow_action_rss *comp, + const struct rte_flow_action_rss *with) +{ + return (comp->func == with->func && + comp->level == with->level && + comp->types == with->types && + comp->key_len == with->key_len && + comp->queue_num == with->queue_num && + !memcmp(comp->key, with->key, with->key_len) && + !memcmp(comp->queue, with->queue, + sizeof(*with->queue) * with->queue_num)); +} + +static int +hns3_rss_conf_copy(struct hns3_rss_conf *out, + const struct rte_flow_action_rss *in) +{ + if (in->key_len > RTE_DIM(out->key) || + in->queue_num > RTE_DIM(out->queue)) + return -EINVAL; + if (in->key == NULL && in->key_len) + return -EINVAL; + out->conf = (struct rte_flow_action_rss) { + .func = in->func, + .level = in->level, + .types = in->types, + .key_len = in->key_len, + .queue_num = in->queue_num, + }; + out->conf.queue = + memcpy(out->queue, in->queue, + sizeof(*in->queue) * in->queue_num); + if (in->key) + out->conf.key = memcpy(out->key, in->key, in->key_len); + + return 0; +} + +/* + * This function is used to parse rss action validatation. + */ +static int +hns3_parse_rss_filter(struct rte_eth_dev *dev, + const struct rte_flow_action *actions, + struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_conf = &hw->rss_info; + const struct rte_flow_action_rss *rss; + const struct rte_flow_action *act; + uint32_t act_index = 0; + uint64_t flow_types; + uint16_t n; + + NEXT_ITEM_OF_ACTION(act, actions, act_index); + /* Get configuration args from APP cmdline input */ + rss = act->conf; + + if (rss == NULL || rss->queue_num == 0) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, "no valid queues"); + } + + for (n = 0; n < rss->queue_num; n++) { + if (rss->queue[n] < dev->data->nb_rx_queues) + continue; + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "queue id > max number of queues"); + } + + /* Parse flow types of RSS */ + if (!(rss->types & HNS3_ETH_RSS_SUPPORT) && rss->types) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, + "Flow types is unsupported by " + "hns3's RSS"); + + flow_types = rss->types & HNS3_ETH_RSS_SUPPORT; + if (flow_types != rss->types) + hns3_warn(hw, "RSS flow types(%" PRIx64 ") include unsupported " + "flow types", rss->types); + + /* Parse RSS related parameters from RSS configuration */ + switch (rss->func) { + case RTE_ETH_HASH_FUNCTION_DEFAULT: + case RTE_ETH_HASH_FUNCTION_TOEPLITZ: + case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR: + break; + default: + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, act, + "input RSS hash functions are not supported"); + } + + if (rss->level) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, act, + "a nonzero RSS encapsulation level is not supported"); + if (rss->key_len && rss->key_len != RTE_DIM(rss_conf->key)) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, act, + "RSS hash key must be exactly 40 bytes"); + if (rss->queue_num > RTE_DIM(rss_conf->queue)) + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, act, + "too many queues for RSS context"); + + act_index++; + + /* Check if the next not void action is END */ + NEXT_ITEM_OF_ACTION(act, actions, act_index); + if (act->type != RTE_FLOW_ACTION_TYPE_END) { + memset(rss_conf, 0, sizeof(struct hns3_rss_conf)); + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + act, "Not supported action."); + } + + return 0; +} + +static int +hns3_disable_rss(struct hns3_hw *hw) +{ + int ret; + + /* Redirected the redirection table to queue 0 */ + ret = hns3_rss_reset_indir_table(hw); + if (ret) + return ret; + + /* Disable RSS */ + hw->rss_info.conf.types = 0; + hw->rss_dis_flag = true; + + return 0; +} + +static void +hns3_parse_rss_key(struct hns3_hw *hw, struct rte_flow_action_rss *rss_conf) +{ + if (rss_conf->key == NULL || + rss_conf->key_len < HNS3_RSS_KEY_SIZE) { + hns3_info(hw, "Default RSS hash key to be set"); + rss_conf->key = hns3_hash_key; + rss_conf->key_len = HNS3_RSS_KEY_SIZE; + } +} + +static int +hns3_parse_rss_algorithm(struct hns3_hw *hw, enum rte_eth_hash_function *func, + uint8_t *hash_algo) +{ + enum rte_eth_hash_function algo_func = *func; + switch (algo_func) { + case RTE_ETH_HASH_FUNCTION_DEFAULT: + /* Keep *hash_algo as what it used to be */ + algo_func = hw->rss_info.conf.func; + break; + case RTE_ETH_HASH_FUNCTION_TOEPLITZ: + *hash_algo = HNS3_RSS_HASH_ALGO_TOEPLITZ; + break; + case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR: + *hash_algo = HNS3_RSS_HASH_ALGO_SIMPLE; + break; + default: + hns3_err(hw, "Invalid RSS algorithm configuration(%u)", + algo_func); + return -EINVAL; + } + *func = algo_func; + + return 0; +} + +static int +hns3_hw_rss_hash_set(struct hns3_hw *hw, struct rte_flow_action_rss *rss_config) +{ + uint8_t hash_algo = + (hw->rss_info.conf.func == RTE_ETH_HASH_FUNCTION_TOEPLITZ ? + HNS3_RSS_HASH_ALGO_TOEPLITZ : HNS3_RSS_HASH_ALGO_SIMPLE); + struct hns3_rss_tuple_cfg *tuple; + int ret; + + /* Parse hash key */ + hns3_parse_rss_key(hw, rss_config); + + /* Parse hash algorithm */ + ret = hns3_parse_rss_algorithm(hw, &rss_config->func, &hash_algo); + if (ret) + return ret; + + ret = hns3_set_rss_algo_key(hw, hash_algo, rss_config->key); + if (ret) + return ret; + + /* Update algorithm of hw */ + hw->rss_info.conf.func = rss_config->func; + + /* Set flow type supported */ + tuple = &hw->rss_info.rss_tuple_sets; + ret = hns3_set_rss_tuple_by_rss_hf(hw, tuple, rss_config->types); + if (ret) + hns3_err(hw, "Update RSS tuples by rss hf failed %d", ret); + + return ret; +} + +static int +hns3_update_indir_table(struct rte_eth_dev *dev, + const struct rte_flow_action_rss *conf, uint16_t num) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint8_t indir_tbl[HNS3_RSS_IND_TBL_SIZE]; + uint16_t j, allow_rss_queues; + uint8_t queue_id; + uint32_t i; + + if (num == 0) { + hns3_err(hw, "No PF queues are configured to enable RSS"); + return -ENOTSUP; + } + + allow_rss_queues = RTE_MIN(dev->data->nb_rx_queues, hw->rss_size_max); + /* Fill in redirection table */ + memcpy(indir_tbl, hw->rss_info.rss_indirection_tbl, + HNS3_RSS_IND_TBL_SIZE); + for (i = 0, j = 0; i < HNS3_RSS_IND_TBL_SIZE; i++, j++) { + j %= num; + if (conf->queue[j] >= allow_rss_queues) { + hns3_err(hw, "Invalid queue id(%u) to be set in " + "redirection table, max number of rss " + "queues: %u", conf->queue[j], + allow_rss_queues); + return -EINVAL; + } + queue_id = conf->queue[j]; + indir_tbl[i] = queue_id; + } + + return hns3_set_rss_indir_table(hw, indir_tbl, HNS3_RSS_IND_TBL_SIZE); +} + +static int +hns3_config_rss_filter(struct rte_eth_dev *dev, + const struct hns3_rss_conf *conf, bool add) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_info; + uint64_t flow_types; + uint16_t num; + int ret; + + struct rte_flow_action_rss rss_flow_conf = { + .func = conf->conf.func, + .level = conf->conf.level, + .types = conf->conf.types, + .key_len = conf->conf.key_len, + .queue_num = conf->conf.queue_num, + .key = conf->conf.key_len ? + (void *)(uintptr_t)conf->conf.key : NULL, + .queue = conf->conf.queue, + }; + + /* The types is Unsupported by hns3' RSS */ + if (!(rss_flow_conf.types & HNS3_ETH_RSS_SUPPORT) && + rss_flow_conf.types) { + hns3_err(hw, + "Flow types(%" PRIx64 ") is unsupported by hns3's RSS", + rss_flow_conf.types); + return -EINVAL; + } + + /* Filter the unsupported flow types */ + flow_types = rss_flow_conf.types & HNS3_ETH_RSS_SUPPORT; + if (flow_types != rss_flow_conf.types) + hns3_warn(hw, "modified RSS types based on hardware support, " + "requested:%" PRIx64 " configured:%" PRIx64, + rss_flow_conf.types, flow_types); + /* Update the useful flow types */ + rss_flow_conf.types = flow_types; + + if ((rss_flow_conf.types & ETH_RSS_PROTO_MASK) == 0) + return hns3_disable_rss(hw); + + rss_info = &hw->rss_info; + if (!add) { + if (hns3_action_rss_same(&rss_info->conf, &rss_flow_conf)) { + ret = hns3_disable_rss(hw); + if (ret) { + hns3_err(hw, "RSS disable failed(%d)", ret); + return ret; + } + memset(rss_info, 0, sizeof(struct hns3_rss_conf)); + return 0; + } + return -EINVAL; + } + + /* Get rx queues num */ + num = dev->data->nb_rx_queues; + + /* Set rx queues to use */ + num = RTE_MIN(num, rss_flow_conf.queue_num); + if (rss_flow_conf.queue_num > num) + hns3_warn(hw, "Config queue numbers %u are beyond the scope of truncated", + rss_flow_conf.queue_num); + hns3_info(hw, "Max of contiguous %u PF queues are configured", num); + + rte_spinlock_lock(&hw->lock); + /* Update redirection talbe of rss */ + ret = hns3_update_indir_table(dev, &rss_flow_conf, num); + if (ret) + goto rss_config_err; + + /* Set hash algorithm and flow types by the user's config */ + ret = hns3_hw_rss_hash_set(hw, &rss_flow_conf); + if (ret) + goto rss_config_err; + + ret = hns3_rss_conf_copy(rss_info, &rss_flow_conf); + if (ret) { + hns3_err(hw, "RSS config init fail(%d)", ret); + goto rss_config_err; + } + +rss_config_err: + rte_spinlock_unlock(&hw->lock); + + return ret; +} + +/* Remove the rss filter */ +static int +hns3_clear_rss_filter(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + if (hw->rss_info.conf.queue_num == 0) + return 0; + + return hns3_config_rss_filter(dev, &hw->rss_info, false); +} + +/* Restore the rss filter */ +int +hns3_restore_rss_filter(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + + if (hw->rss_info.conf.queue_num == 0) + return 0; + + return hns3_config_rss_filter(dev, &hw->rss_info, true); +} + +static int +hns3_flow_parse_rss(struct rte_eth_dev *dev, + const struct hns3_rss_conf *conf, bool add) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + bool ret; + + /* Action rss same */ + ret = hns3_action_rss_same(&hw->rss_info.conf, &conf->conf); + if (ret) { + hns3_err(hw, "Enter duplicate RSS configuration : %d", ret); + return -EINVAL; + } + + return hns3_config_rss_filter(dev, conf, add); +} + +static int +hns3_flow_args_check(const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error) +{ + if (pattern == NULL) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM_NUM, + NULL, "NULL pattern."); + + if (actions == NULL) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION_NUM, + NULL, "NULL action."); + + if (attr == NULL) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ATTR, + NULL, "NULL attribute."); + + return hns3_check_attr(attr, error); +} + +/* + * Check if the flow rule is supported by hns3. + * It only checkes the format. Don't guarantee the rule can be programmed into + * the HW. Because there can be no enough room for the rule. + */ +static int +hns3_flow_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error) +{ + struct hns3_fdir_rule fdir_rule; + int ret; + + ret = hns3_flow_args_check(attr, pattern, actions, error); + if (ret) + return ret; + + if (find_rss_action(actions)) + return hns3_parse_rss_filter(dev, actions, error); + + memset(&fdir_rule, 0, sizeof(struct hns3_fdir_rule)); + return hns3_parse_fdir_filter(dev, pattern, actions, &fdir_rule, error); +} + +/* + * Create or destroy a flow rule. + * Theorically one rule can match more than one filters. + * We will let it use the filter which it hitt first. + * So, the sequence matters. + */ +static struct rte_flow * +hns3_flow_create(struct rte_eth_dev *dev, const struct rte_flow_attr *attr, + const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], + struct rte_flow_error *error) +{ + struct hns3_process_private *process_list = dev->process_private; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + const struct hns3_rss_conf *rss_conf; + struct hns3_fdir_rule_ele *fdir_rule_ptr; + struct hns3_rss_conf_ele *rss_filter_ptr; + struct hns3_flow_mem *flow_node; + const struct rte_flow_action *act; + struct rte_flow *flow; + struct hns3_fdir_rule fdir_rule; + int ret; + + ret = hns3_flow_args_check(attr, pattern, actions, error); + if (ret) + return NULL; + + flow = rte_zmalloc("hns3 flow", sizeof(struct rte_flow), 0); + if (flow == NULL) { + rte_flow_error_set(error, ENOMEM, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to allocate flow memory"); + return NULL; + } + flow_node = rte_zmalloc("hns3 flow node", + sizeof(struct hns3_flow_mem), 0); + if (flow_node == NULL) { + rte_flow_error_set(error, ENOMEM, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to allocate flow list memory"); + rte_free(flow); + return NULL; + } + + flow_node->flow = flow; + TAILQ_INSERT_TAIL(&process_list->flow_list, flow_node, entries); + + act = find_rss_action(actions); + if (act) { + rss_conf = act->conf; + + ret = hns3_flow_parse_rss(dev, rss_conf, true); + if (ret) + goto err; + + rss_filter_ptr = rte_zmalloc("hns3 rss filter", + sizeof(struct hns3_rss_conf_ele), + 0); + if (rss_filter_ptr == NULL) { + hns3_err(hw, + "Failed to allocate hns3_rss_filter memory"); + ret = -ENOMEM; + goto err; + } + memcpy(&rss_filter_ptr->filter_info, rss_conf, + sizeof(struct hns3_rss_conf)); + TAILQ_INSERT_TAIL(&process_list->filter_rss_list, + rss_filter_ptr, entries); + + flow->rule = rss_filter_ptr; + flow->filter_type = RTE_ETH_FILTER_HASH; + return flow; + } + + memset(&fdir_rule, 0, sizeof(struct hns3_fdir_rule)); + ret = hns3_parse_fdir_filter(dev, pattern, actions, &fdir_rule, error); + if (ret) + goto out; + + if (fdir_rule.flags & HNS3_RULE_FLAG_COUNTER) { + ret = hns3_counter_new(dev, fdir_rule.act_cnt.shared, + fdir_rule.act_cnt.id, error); + if (ret) + goto out; + + flow->counter_id = fdir_rule.act_cnt.id; + } + ret = hns3_fdir_filter_program(hns, &fdir_rule, false); + if (!ret) { + fdir_rule_ptr = rte_zmalloc("hns3 fdir rule", + sizeof(struct hns3_fdir_rule_ele), + 0); + if (fdir_rule_ptr == NULL) { + hns3_err(hw, "Failed to allocate fdir_rule memory"); + ret = -ENOMEM; + goto err_fdir; + } + memcpy(&fdir_rule_ptr->fdir_conf, &fdir_rule, + sizeof(struct hns3_fdir_rule)); + TAILQ_INSERT_TAIL(&process_list->fdir_list, + fdir_rule_ptr, entries); + flow->rule = fdir_rule_ptr; + flow->filter_type = RTE_ETH_FILTER_FDIR; + + return flow; + } + +err_fdir: + if (fdir_rule.flags & HNS3_RULE_FLAG_COUNTER) + hns3_counter_release(dev, fdir_rule.act_cnt.id); + +err: + rte_flow_error_set(error, -ret, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to create flow"); +out: + TAILQ_REMOVE(&process_list->flow_list, flow_node, entries); + rte_free(flow_node); + rte_free(flow); + return NULL; +} + +/* Destroy a flow rule on hns3. */ +static int +hns3_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow, + struct rte_flow_error *error) +{ + struct hns3_process_private *process_list = dev->process_private; + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_fdir_rule_ele *fdir_rule_ptr; + struct hns3_rss_conf_ele *rss_filter_ptr; + struct hns3_flow_mem *flow_node; + struct hns3_hw *hw = &hns->hw; + enum rte_filter_type filter_type; + struct hns3_fdir_rule fdir_rule; + int ret; + + if (flow == NULL) + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_HANDLE, + flow, "Flow is NULL"); + filter_type = flow->filter_type; + switch (filter_type) { + case RTE_ETH_FILTER_FDIR: + fdir_rule_ptr = (struct hns3_fdir_rule_ele *)flow->rule; + memcpy(&fdir_rule, &fdir_rule_ptr->fdir_conf, + sizeof(struct hns3_fdir_rule)); + + ret = hns3_fdir_filter_program(hns, &fdir_rule, true); + if (ret) + return rte_flow_error_set(error, EIO, + RTE_FLOW_ERROR_TYPE_HANDLE, + flow, + "Destroy FDIR fail.Try again"); + if (fdir_rule.flags & HNS3_RULE_FLAG_COUNTER) + hns3_counter_release(dev, fdir_rule.act_cnt.id); + TAILQ_REMOVE(&process_list->fdir_list, fdir_rule_ptr, entries); + rte_free(fdir_rule_ptr); + fdir_rule_ptr = NULL; + break; + case RTE_ETH_FILTER_HASH: + rss_filter_ptr = (struct hns3_rss_conf_ele *)flow->rule; + ret = hns3_config_rss_filter(dev, &hw->rss_info, false); + if (ret) + return rte_flow_error_set(error, EIO, + RTE_FLOW_ERROR_TYPE_HANDLE, + flow, + "Destroy RSS fail.Try again"); + TAILQ_REMOVE(&process_list->filter_rss_list, rss_filter_ptr, + entries); + rte_free(rss_filter_ptr); + rss_filter_ptr = NULL; + break; + default: + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_HANDLE, flow, + "Unsupported filter type"); + } + + TAILQ_FOREACH(flow_node, &process_list->flow_list, entries) { + if (flow_node->flow == flow) { + TAILQ_REMOVE(&process_list->flow_list, flow_node, + entries); + rte_free(flow_node); + flow_node = NULL; + break; + } + } + rte_free(flow); + flow = NULL; + + return 0; +} + +/* Destroy all flow rules associated with a port on hns3. */ +static int +hns3_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error) +{ + struct hns3_adapter *hns = dev->data->dev_private; + int ret; + + /* FDIR is available only in PF driver */ + if (!hns->is_vf) { + ret = hns3_clear_all_fdir_filter(hns); + if (ret) { + rte_flow_error_set(error, ret, + RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Failed to flush rule"); + return ret; + } + hns3_counter_flush(dev); + } + + ret = hns3_clear_rss_filter(dev); + if (ret) { + rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Failed to flush rss filter"); + return ret; + } + + hns3_filterlist_flush(dev); + + return 0; +} + +/* Query an existing flow rule. */ +static int +hns3_flow_query(struct rte_eth_dev *dev, struct rte_flow *flow, + const struct rte_flow_action *actions, void *data, + struct rte_flow_error *error) +{ + struct rte_flow_query_count *qc; + int ret; + + for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) { + switch (actions->type) { + case RTE_FLOW_ACTION_TYPE_VOID: + break; + case RTE_FLOW_ACTION_TYPE_COUNT: + qc = (struct rte_flow_query_count *)data; + ret = hns3_counter_query(dev, flow, qc, error); + if (ret) + return ret; + break; + default: + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, + actions, + "Query action only support count"); + } + } + return 0; +} + +static const struct rte_flow_ops hns3_flow_ops = { + .validate = hns3_flow_validate, + .create = hns3_flow_create, + .destroy = hns3_flow_destroy, + .flush = hns3_flow_flush, + .query = hns3_flow_query, + .isolate = NULL, +}; + +/* + * The entry of flow API. + * @param dev + * Pointer to Ethernet device. + * @return + * 0 on success, a negative errno value otherwise is set. + */ +int +hns3_dev_filter_ctrl(struct rte_eth_dev *dev, enum rte_filter_type filter_type, + enum rte_filter_op filter_op, void *arg) +{ + struct hns3_hw *hw; + int ret = 0; + + hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + switch (filter_type) { + case RTE_ETH_FILTER_GENERIC: + if (filter_op != RTE_ETH_FILTER_GET) + return -EINVAL; + if (hw->adapter_state >= HNS3_NIC_CLOSED) + return -ENODEV; + *(const void **)arg = &hns3_flow_ops; + break; + default: + hns3_err(hw, "Filter type (%d) not supported", filter_type); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_intr.c b/src/spdk/dpdk/drivers/net/hns3/hns3_intr.c new file mode 100644 index 000000000..9953a1d98 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_intr.c @@ -0,0 +1,1169 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdbool.h> +#include <rte_atomic.h> +#include <rte_alarm.h> +#include <rte_cycles.h> +#include <rte_ethdev.h> +#include <rte_io.h> +#include <rte_malloc.h> +#include <rte_pci.h> +#include <rte_bus_pci.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" +#include "hns3_intr.h" +#include "hns3_regs.h" +#include "hns3_rxtx.h" + +#define SWITCH_CONTEXT_US 10 + +/* offset in MSIX bd */ +#define MAC_ERROR_OFFSET 1 +#define PPP_PF_ERROR_OFFSET 2 +#define PPU_PF_ERROR_OFFSET 3 +#define RCB_ERROR_OFFSET 5 +#define RCB_ERROR_STATUS_OFFSET 2 + +#define HNS3_CHECK_MERGE_CNT(val) \ + do { \ + if (val) \ + hw->reset.stats.merge_cnt++; \ + } while (0) + +static const char *reset_string[HNS3_MAX_RESET] = { + "none", "vf_func", "vf_pf_func", "vf_full", "flr", + "vf_global", "pf_func", "global", "IMP", +}; + +const struct hns3_hw_error mac_afifo_tnl_int[] = { + { .int_msk = BIT(0), .msg = "egu_cge_afifo_ecc_1bit_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(1), .msg = "egu_cge_afifo_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(2), .msg = "egu_lge_afifo_ecc_1bit_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(3), .msg = "egu_lge_afifo_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(4), .msg = "cge_igu_afifo_ecc_1bit_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(5), .msg = "cge_igu_afifo_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(6), .msg = "lge_igu_afifo_ecc_1bit_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(7), .msg = "lge_igu_afifo_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(8), .msg = "cge_igu_afifo_overflow_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(9), .msg = "lge_igu_afifo_overflow_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(10), .msg = "egu_cge_afifo_underrun_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(11), .msg = "egu_lge_afifo_underrun_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(12), .msg = "egu_ge_afifo_underrun_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(13), .msg = "ge_igu_afifo_overflow_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = 0, .msg = NULL, + .reset_level = HNS3_NONE_RESET} +}; + +const struct hns3_hw_error ppu_mpf_abnormal_int_st2[] = { + { .int_msk = BIT(13), .msg = "rpu_rx_pkt_bit32_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(14), .msg = "rpu_rx_pkt_bit33_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(15), .msg = "rpu_rx_pkt_bit34_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(16), .msg = "rpu_rx_pkt_bit35_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(17), .msg = "rcb_tx_ring_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(18), .msg = "rcb_rx_ring_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(19), .msg = "rcb_tx_fbd_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(20), .msg = "rcb_rx_ebd_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(21), .msg = "rcb_tso_info_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(22), .msg = "rcb_tx_int_info_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(23), .msg = "rcb_rx_int_info_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(24), .msg = "tpu_tx_pkt_0_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(25), .msg = "tpu_tx_pkt_1_ecc_mbit_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(26), .msg = "rd_bus_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(27), .msg = "wr_bus_err", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(28), .msg = "reg_search_miss", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(29), .msg = "rx_q_search_miss", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(30), .msg = "ooo_ecc_err_detect", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(31), .msg = "ooo_ecc_err_multpl", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = 0, .msg = NULL, + .reset_level = HNS3_NONE_RESET} +}; + +const struct hns3_hw_error ssu_port_based_pf_int[] = { + { .int_msk = BIT(0), .msg = "roc_pkt_without_key_port", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = BIT(9), .msg = "low_water_line_err_port", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(10), .msg = "hi_water_line_err_port", + .reset_level = HNS3_GLOBAL_RESET }, + { .int_msk = 0, .msg = NULL, + .reset_level = HNS3_NONE_RESET} +}; + +const struct hns3_hw_error ppp_pf_abnormal_int[] = { + { .int_msk = BIT(0), .msg = "tx_vlan_tag_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(1), .msg = "rss_list_tc_unassigned_queue_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = 0, .msg = NULL, + .reset_level = HNS3_NONE_RESET} +}; + +const struct hns3_hw_error ppu_pf_abnormal_int[] = { + { .int_msk = BIT(0), .msg = "over_8bd_no_fe", + .reset_level = HNS3_FUNC_RESET }, + { .int_msk = BIT(1), .msg = "tso_mss_cmp_min_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(2), .msg = "tso_mss_cmp_max_err", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = BIT(3), .msg = "tx_rd_fbd_poison", + .reset_level = HNS3_FUNC_RESET }, + { .int_msk = BIT(4), .msg = "rx_rd_ebd_poison", + .reset_level = HNS3_FUNC_RESET }, + { .int_msk = BIT(5), .msg = "buf_wait_timeout", + .reset_level = HNS3_NONE_RESET }, + { .int_msk = 0, .msg = NULL, + .reset_level = HNS3_NONE_RESET} +}; + +static int +config_ppp_err_intr(struct hns3_adapter *hns, uint32_t cmd, bool en) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc[2]; + int ret; + + /* configure PPP error interrupts */ + hns3_cmd_setup_basic_desc(&desc[0], cmd, false); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_setup_basic_desc(&desc[1], cmd, false); + + if (cmd == HNS3_PPP_CMD0_INT_CMD) { + if (en) { + desc[0].data[0] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT0_EN); + desc[0].data[1] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT1_EN); + desc[0].data[4] = + rte_cpu_to_le_32(HNS3_PPP_PF_ERR_INT_EN); + } + + desc[1].data[0] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT0_EN_MASK); + desc[1].data[1] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT1_EN_MASK); + desc[1].data[2] = + rte_cpu_to_le_32(HNS3_PPP_PF_ERR_INT_EN_MASK); + } else if (cmd == HNS3_PPP_CMD1_INT_CMD) { + if (en) { + desc[0].data[0] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT2_EN); + desc[0].data[1] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT3_EN); + } + + desc[1].data[0] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT2_EN_MASK); + desc[1].data[1] = + rte_cpu_to_le_32(HNS3_PPP_MPF_ECC_ERR_INT3_EN_MASK); + } + + ret = hns3_cmd_send(hw, &desc[0], 2); + if (ret) + hns3_err(hw, "fail to configure PPP error int: %d", ret); + + return ret; +} + +static int +enable_ppp_err_intr(struct hns3_adapter *hns, bool en) +{ + int ret; + + ret = config_ppp_err_intr(hns, HNS3_PPP_CMD0_INT_CMD, en); + if (ret) + return ret; + + return config_ppp_err_intr(hns, HNS3_PPP_CMD1_INT_CMD, en); +} + +static int +enable_ssu_err_intr(struct hns3_adapter *hns, bool en) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc[2]; + int ret; + + /* configure SSU ecc error interrupts */ + hns3_cmd_setup_basic_desc(&desc[0], HNS3_SSU_ECC_INT_CMD, false); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_setup_basic_desc(&desc[1], HNS3_SSU_ECC_INT_CMD, false); + if (en) { + desc[0].data[0] = + rte_cpu_to_le_32(HNS3_SSU_1BIT_ECC_ERR_INT_EN); + desc[0].data[1] = + rte_cpu_to_le_32(HNS3_SSU_MULTI_BIT_ECC_ERR_INT_EN); + desc[0].data[4] = + rte_cpu_to_le_32(HNS3_SSU_BIT32_ECC_ERR_INT_EN); + } + + desc[1].data[0] = rte_cpu_to_le_32(HNS3_SSU_1BIT_ECC_ERR_INT_EN_MASK); + desc[1].data[1] = + rte_cpu_to_le_32(HNS3_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK); + desc[1].data[2] = rte_cpu_to_le_32(HNS3_SSU_BIT32_ECC_ERR_INT_EN_MASK); + + ret = hns3_cmd_send(hw, &desc[0], 2); + if (ret) { + hns3_err(hw, "fail to configure SSU ECC error interrupt: %d", + ret); + return ret; + } + + /* configure SSU common error interrupts */ + hns3_cmd_setup_basic_desc(&desc[0], HNS3_SSU_COMMON_INT_CMD, false); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + hns3_cmd_setup_basic_desc(&desc[1], HNS3_SSU_COMMON_INT_CMD, false); + + if (en) { + desc[0].data[0] = rte_cpu_to_le_32(HNS3_SSU_COMMON_INT_EN); + desc[0].data[1] = + rte_cpu_to_le_32(HNS3_SSU_PORT_BASED_ERR_INT_EN); + desc[0].data[2] = + rte_cpu_to_le_32(HNS3_SSU_FIFO_OVERFLOW_ERR_INT_EN); + } + + desc[1].data[0] = rte_cpu_to_le_32(HNS3_SSU_COMMON_INT_EN_MASK | + HNS3_SSU_PORT_BASED_ERR_INT_EN_MASK); + desc[1].data[1] = + rte_cpu_to_le_32(HNS3_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK); + + ret = hns3_cmd_send(hw, &desc[0], 2); + if (ret) + hns3_err(hw, "fail to configure SSU COMMON error intr: %d", + ret); + + return ret; +} + +static int +config_ppu_err_intrs(struct hns3_adapter *hns, uint32_t cmd, bool en) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc[2]; + int num = 1; + + /* configure PPU error interrupts */ + switch (cmd) { + case HNS3_PPU_MPF_ECC_INT_CMD: + hns3_cmd_setup_basic_desc(&desc[0], cmd, false); + desc[0].flag |= HNS3_CMD_FLAG_NEXT; + hns3_cmd_setup_basic_desc(&desc[1], cmd, false); + if (en) { + desc[0].data[0] = HNS3_PPU_MPF_ABNORMAL_INT0_EN; + desc[0].data[1] = HNS3_PPU_MPF_ABNORMAL_INT1_EN; + desc[1].data[3] = HNS3_PPU_MPF_ABNORMAL_INT3_EN; + desc[1].data[4] = HNS3_PPU_MPF_ABNORMAL_INT2_EN; + } + + desc[1].data[0] = HNS3_PPU_MPF_ABNORMAL_INT0_EN_MASK; + desc[1].data[1] = HNS3_PPU_MPF_ABNORMAL_INT1_EN_MASK; + desc[1].data[2] = HNS3_PPU_MPF_ABNORMAL_INT2_EN_MASK; + desc[1].data[3] |= HNS3_PPU_MPF_ABNORMAL_INT3_EN_MASK; + num = 2; + break; + case HNS3_PPU_MPF_OTHER_INT_CMD: + hns3_cmd_setup_basic_desc(&desc[0], cmd, false); + if (en) + desc[0].data[0] = HNS3_PPU_MPF_ABNORMAL_INT2_EN2; + + desc[0].data[2] = HNS3_PPU_MPF_ABNORMAL_INT2_EN2_MASK; + break; + case HNS3_PPU_PF_OTHER_INT_CMD: + hns3_cmd_setup_basic_desc(&desc[0], cmd, false); + if (en) + desc[0].data[0] = HNS3_PPU_PF_ABNORMAL_INT_EN; + + desc[0].data[2] = HNS3_PPU_PF_ABNORMAL_INT_EN_MASK; + break; + default: + hns3_err(hw, + "Invalid cmd(%u) to configure PPU error interrupts.", + cmd); + return -EINVAL; + } + + return hns3_cmd_send(hw, &desc[0], num); +} + +static int +enable_ppu_err_intr(struct hns3_adapter *hns, bool en) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + ret = config_ppu_err_intrs(hns, HNS3_PPU_MPF_ECC_INT_CMD, en); + if (ret) { + hns3_err(hw, "fail to configure PPU MPF ECC error intr: %d", + ret); + return ret; + } + + ret = config_ppu_err_intrs(hns, HNS3_PPU_MPF_OTHER_INT_CMD, en); + if (ret) { + hns3_err(hw, "fail to configure PPU MPF other intr: %d", + ret); + return ret; + } + + ret = config_ppu_err_intrs(hns, HNS3_PPU_PF_OTHER_INT_CMD, en); + if (ret) + hns3_err(hw, "fail to configure PPU PF error interrupts: %d", + ret); + return ret; +} + +static int +enable_mac_err_intr(struct hns3_adapter *hns, bool en) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc; + int ret; + + /* configure MAC common error interrupts */ + hns3_cmd_setup_basic_desc(&desc, HNS3_MAC_COMMON_INT_EN, false); + if (en) + desc.data[0] = rte_cpu_to_le_32(HNS3_MAC_COMMON_ERR_INT_EN); + + desc.data[1] = rte_cpu_to_le_32(HNS3_MAC_COMMON_ERR_INT_EN_MASK); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "fail to configure MAC COMMON error intr: %d", + ret); + + return ret; +} + +static const struct hns3_hw_blk hw_blk[] = { + { + .name = "PPP", + .enable_err_intr = enable_ppp_err_intr, + }, + { + .name = "SSU", + .enable_err_intr = enable_ssu_err_intr, + }, + { + .name = "PPU", + .enable_err_intr = enable_ppu_err_intr, + }, + { + .name = "MAC", + .enable_err_intr = enable_mac_err_intr, + }, + { + .name = NULL, + .enable_err_intr = NULL, + } +}; + +int +hns3_enable_hw_error_intr(struct hns3_adapter *hns, bool en) +{ + const struct hns3_hw_blk *module = hw_blk; + int ret = 0; + + while (module->enable_err_intr) { + ret = module->enable_err_intr(hns, en); + if (ret) + return ret; + + module++; + } + + return ret; +} + +static enum hns3_reset_level +hns3_find_highest_level(struct hns3_adapter *hns, const char *reg, + const struct hns3_hw_error *err, uint32_t err_sts) +{ + enum hns3_reset_level reset_level = HNS3_FUNC_RESET; + struct hns3_hw *hw = &hns->hw; + bool need_reset = false; + + while (err->msg) { + if (err->int_msk & err_sts) { + hns3_warn(hw, "%s %s found [error status=0x%x]", + reg, err->msg, err_sts); + if (err->reset_level != HNS3_NONE_RESET && + err->reset_level >= reset_level) { + reset_level = err->reset_level; + need_reset = true; + } + } + err++; + } + if (need_reset) + return reset_level; + else + return HNS3_NONE_RESET; +} + +static int +query_num_bds_in_msix(struct hns3_hw *hw, struct hns3_cmd_desc *desc_bd) +{ + int ret; + + hns3_cmd_setup_basic_desc(desc_bd, HNS3_QUERY_MSIX_INT_STS_BD_NUM, + true); + ret = hns3_cmd_send(hw, desc_bd, 1); + if (ret) + hns3_err(hw, "query num bds in msix failed: %d", ret); + + return ret; +} + +static int +query_all_mpf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc, + uint32_t mpf_bd_num) +{ + int ret; + + hns3_cmd_setup_basic_desc(desc, HNS3_QUERY_CLEAR_ALL_MPF_MSIX_INT, + true); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + + ret = hns3_cmd_send(hw, &desc[0], mpf_bd_num); + if (ret) + hns3_err(hw, "query all mpf msix err failed: %d", ret); + + return ret; +} + +static int +clear_all_mpf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc, + uint32_t mpf_bd_num) +{ + int ret; + + hns3_cmd_reuse_desc(desc, false); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + + ret = hns3_cmd_send(hw, desc, mpf_bd_num); + if (ret) + hns3_err(hw, "clear all mpf msix err failed: %d", ret); + + return ret; +} + +static int +query_all_pf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc, + uint32_t pf_bd_num) +{ + int ret; + + hns3_cmd_setup_basic_desc(desc, HNS3_QUERY_CLEAR_ALL_PF_MSIX_INT, true); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + + ret = hns3_cmd_send(hw, desc, pf_bd_num); + if (ret) + hns3_err(hw, "query all pf msix int cmd failed: %d", ret); + + return ret; +} + +static int +clear_all_pf_msix_err(struct hns3_hw *hw, struct hns3_cmd_desc *desc, + uint32_t pf_bd_num) +{ + int ret; + + hns3_cmd_reuse_desc(desc, false); + desc[0].flag |= rte_cpu_to_le_16(HNS3_CMD_FLAG_NEXT); + + ret = hns3_cmd_send(hw, desc, pf_bd_num); + if (ret) + hns3_err(hw, "clear all pf msix err failed: %d", ret); + + return ret; +} + +void +hns3_intr_unregister(const struct rte_intr_handle *hdl, + rte_intr_callback_fn cb_fn, void *cb_arg) +{ + int retry_cnt = 0; + int ret; + + do { + ret = rte_intr_callback_unregister(hdl, cb_fn, cb_arg); + if (ret >= 0) { + break; + } else if (ret != -EAGAIN) { + PMD_INIT_LOG(ERR, "Failed to unregister intr: %d", ret); + break; + } + rte_delay_ms(HNS3_INTR_UNREG_FAIL_DELAY_MS); + } while (retry_cnt++ < HNS3_INTR_UNREG_FAIL_RETRY_CNT); +} + +void +hns3_handle_msix_error(struct hns3_adapter *hns, uint64_t *levels) +{ + uint32_t mpf_bd_num, pf_bd_num, bd_num; + enum hns3_reset_level req_level; + struct hns3_hw *hw = &hns->hw; + struct hns3_pf *pf = &hns->pf; + struct hns3_cmd_desc desc_bd; + struct hns3_cmd_desc *desc; + uint32_t *desc_data; + uint32_t status; + int ret; + + /* query the number of bds for the MSIx int status */ + ret = query_num_bds_in_msix(hw, &desc_bd); + if (ret) { + hns3_err(hw, "fail to query msix int status bd num: %d", ret); + return; + } + + mpf_bd_num = rte_le_to_cpu_32(desc_bd.data[0]); + pf_bd_num = rte_le_to_cpu_32(desc_bd.data[1]); + bd_num = max_t(uint32_t, mpf_bd_num, pf_bd_num); + if (bd_num < RCB_ERROR_OFFSET) { + hns3_err(hw, "bd_num is less than RCB_ERROR_OFFSET: %u", + bd_num); + return; + } + + desc = rte_zmalloc(NULL, bd_num * sizeof(struct hns3_cmd_desc), 0); + if (desc == NULL) { + hns3_err(hw, "fail to zmalloc desc"); + return; + } + + /* query all main PF MSIx errors */ + ret = query_all_mpf_msix_err(hw, &desc[0], mpf_bd_num); + if (ret) { + hns3_err(hw, "query all mpf msix int cmd failed: %d", ret); + goto out; + } + + /* log MAC errors */ + desc_data = (uint32_t *)&desc[MAC_ERROR_OFFSET]; + status = rte_le_to_cpu_32(*desc_data); + if (status) { + req_level = hns3_find_highest_level(hns, "MAC_AFIFO_TNL_INT_R", + mac_afifo_tnl_int, + status); + hns3_atomic_set_bit(req_level, levels); + pf->abn_int_stats.mac_afifo_tnl_intr_cnt++; + } + + /* log PPU(RCB) errors */ + desc_data = (uint32_t *)&desc[RCB_ERROR_OFFSET]; + status = rte_le_to_cpu_32(*(desc_data + RCB_ERROR_STATUS_OFFSET)) & + HNS3_PPU_MPF_INT_ST2_MSIX_MASK; + if (status) { + req_level = hns3_find_highest_level(hns, + "PPU_MPF_ABNORMAL_INT_ST2", + ppu_mpf_abnormal_int_st2, + status); + hns3_atomic_set_bit(req_level, levels); + pf->abn_int_stats.ppu_mpf_abnormal_intr_st2_cnt++; + } + + /* clear all main PF MSIx errors */ + ret = clear_all_mpf_msix_err(hw, desc, mpf_bd_num); + if (ret) { + hns3_err(hw, "clear all mpf msix int cmd failed: %d", ret); + goto out; + } + + /* query all PF MSIx errors */ + memset(desc, 0, bd_num * sizeof(struct hns3_cmd_desc)); + ret = query_all_pf_msix_err(hw, &desc[0], pf_bd_num); + if (ret) { + hns3_err(hw, "query all pf msix int cmd failed (%d)", ret); + goto out; + } + + /* log SSU PF errors */ + status = rte_le_to_cpu_32(desc[0].data[0]) & + HNS3_SSU_PORT_INT_MSIX_MASK; + if (status) { + req_level = hns3_find_highest_level(hns, + "SSU_PORT_BASED_ERR_INT", + ssu_port_based_pf_int, + status); + hns3_atomic_set_bit(req_level, levels); + pf->abn_int_stats.ssu_port_based_pf_intr_cnt++; + } + + /* log PPP PF errors */ + desc_data = (uint32_t *)&desc[PPP_PF_ERROR_OFFSET]; + status = rte_le_to_cpu_32(*desc_data); + if (status) { + req_level = hns3_find_highest_level(hns, + "PPP_PF_ABNORMAL_INT_ST0", + ppp_pf_abnormal_int, + status); + hns3_atomic_set_bit(req_level, levels); + pf->abn_int_stats.ppp_pf_abnormal_intr_cnt++; + } + + /* log PPU(RCB) PF errors */ + desc_data = (uint32_t *)&desc[PPU_PF_ERROR_OFFSET]; + status = rte_le_to_cpu_32(*desc_data) & HNS3_PPU_PF_INT_MSIX_MASK; + if (status) { + req_level = hns3_find_highest_level(hns, + "PPU_PF_ABNORMAL_INT_ST", + ppu_pf_abnormal_int, + status); + hns3_atomic_set_bit(req_level, levels); + pf->abn_int_stats.ppu_pf_abnormal_intr_cnt++; + } + + /* clear all PF MSIx errors */ + ret = clear_all_pf_msix_err(hw, desc, pf_bd_num); + if (ret) + hns3_err(hw, "clear all pf msix int cmd failed: %d", ret); +out: + rte_free(desc); +} + +int +hns3_reset_init(struct hns3_hw *hw) +{ + rte_spinlock_init(&hw->lock); + hw->reset.level = HNS3_NONE_RESET; + hw->reset.stage = RESET_STAGE_NONE; + hw->reset.request = 0; + hw->reset.pending = 0; + rte_atomic16_init(&hw->reset.resetting); + rte_atomic16_init(&hw->reset.disable_cmd); + hw->reset.wait_data = rte_zmalloc("wait_data", + sizeof(struct hns3_wait_data), 0); + if (!hw->reset.wait_data) { + PMD_INIT_LOG(ERR, "Failed to allocate memory for wait_data"); + return -ENOMEM; + } + return 0; +} + +void +hns3_schedule_reset(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + + /* Reschedule the reset process after successful initialization */ + if (hw->adapter_state == HNS3_NIC_UNINITIALIZED) { + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_PENDING); + return; + } + + if (hw->adapter_state >= HNS3_NIC_CLOSED) + return; + + /* Schedule restart alarm if it is not scheduled yet */ + if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_REQUESTED) + return; + if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_DEFERRED) + rte_eal_alarm_cancel(hw->reset.ops->reset_service, hns); + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_REQUESTED); + + rte_eal_alarm_set(SWITCH_CONTEXT_US, hw->reset.ops->reset_service, hns); +} + +void +hns3_schedule_delayed_reset(struct hns3_adapter *hns) +{ +#define DEFERRED_SCHED_US (3 * MSEC_PER_SEC * USEC_PER_MSEC) + struct hns3_hw *hw = &hns->hw; + + /* Do nothing if it is uninited or closed */ + if (hw->adapter_state == HNS3_NIC_UNINITIALIZED || + hw->adapter_state >= HNS3_NIC_CLOSED) { + return; + } + + if (rte_atomic16_read(&hns->hw.reset.schedule) != SCHEDULE_NONE) + return; + rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_DEFERRED); + rte_eal_alarm_set(DEFERRED_SCHED_US, hw->reset.ops->reset_service, hns); +} + +void +hns3_wait_callback(void *param) +{ + struct hns3_wait_data *data = (struct hns3_wait_data *)param; + struct hns3_adapter *hns = data->hns; + struct hns3_hw *hw = &hns->hw; + uint64_t msec; + bool done; + + data->count--; + if (data->check_completion) { + /* + * Check if the current time exceeds the deadline + * or a pending reset coming, or reset during close. + */ + msec = get_timeofday_ms(); + if (msec > data->end_ms || is_reset_pending(hns) || + hw->adapter_state == HNS3_NIC_CLOSING) { + done = false; + data->count = 0; + } else + done = data->check_completion(hw); + } else + done = true; + + if (!done && data->count > 0) { + rte_eal_alarm_set(data->interval, hns3_wait_callback, data); + return; + } + if (done) + data->result = HNS3_WAIT_SUCCESS; + else { + hns3_err(hw, "%s wait timeout at stage %d", + reset_string[hw->reset.level], hw->reset.stage); + data->result = HNS3_WAIT_TIMEOUT; + } + hns3_schedule_reset(hns); +} + +void +hns3_notify_reset_ready(struct hns3_hw *hw, bool enable) +{ + uint32_t reg_val; + + reg_val = hns3_read_dev(hw, HNS3_CMDQ_TX_DEPTH_REG); + if (enable) + reg_val |= HNS3_NIC_SW_RST_RDY; + else + reg_val &= ~HNS3_NIC_SW_RST_RDY; + + hns3_write_dev(hw, HNS3_CMDQ_TX_DEPTH_REG, reg_val); +} + +int +hns3_reset_req_hw_reset(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + + if (hw->reset.wait_data->result == HNS3_WAIT_UNKNOWN) { + hw->reset.wait_data->hns = hns; + hw->reset.wait_data->check_completion = NULL; + hw->reset.wait_data->interval = HNS3_RESET_SYNC_US; + hw->reset.wait_data->count = 1; + hw->reset.wait_data->result = HNS3_WAIT_REQUEST; + rte_eal_alarm_set(hw->reset.wait_data->interval, + hns3_wait_callback, hw->reset.wait_data); + return -EAGAIN; + } else if (hw->reset.wait_data->result == HNS3_WAIT_REQUEST) + return -EAGAIN; + + /* inform hardware that preparatory work is done */ + hns3_notify_reset_ready(hw, true); + return 0; +} + +static void +hns3_clear_reset_level(struct hns3_hw *hw, uint64_t *levels) +{ + uint64_t merge_cnt = hw->reset.stats.merge_cnt; + int64_t tmp; + + switch (hw->reset.level) { + case HNS3_IMP_RESET: + hns3_atomic_clear_bit(HNS3_IMP_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_GLOBAL_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + tmp = hns3_test_and_clear_bit(HNS3_FUNC_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + break; + case HNS3_GLOBAL_RESET: + hns3_atomic_clear_bit(HNS3_GLOBAL_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_FUNC_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + break; + case HNS3_FUNC_RESET: + hns3_atomic_clear_bit(HNS3_FUNC_RESET, levels); + break; + case HNS3_VF_RESET: + hns3_atomic_clear_bit(HNS3_VF_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_VF_PF_FUNC_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + break; + case HNS3_VF_FULL_RESET: + hns3_atomic_clear_bit(HNS3_VF_FULL_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + break; + case HNS3_VF_PF_FUNC_RESET: + hns3_atomic_clear_bit(HNS3_VF_PF_FUNC_RESET, levels); + tmp = hns3_test_and_clear_bit(HNS3_VF_FUNC_RESET, levels); + HNS3_CHECK_MERGE_CNT(tmp); + break; + case HNS3_VF_FUNC_RESET: + hns3_atomic_clear_bit(HNS3_VF_FUNC_RESET, levels); + break; + case HNS3_FLR_RESET: + hns3_atomic_clear_bit(HNS3_FLR_RESET, levels); + break; + case HNS3_NONE_RESET: + default: + return; + }; + if (merge_cnt != hw->reset.stats.merge_cnt) + hns3_warn(hw, + "No need to do low-level reset after %s reset. " + "merge cnt: %" PRIx64 " total merge cnt: %" PRIx64, + reset_string[hw->reset.level], + hw->reset.stats.merge_cnt - merge_cnt, + hw->reset.stats.merge_cnt); +} + +static bool +hns3_reset_err_handle(struct hns3_adapter *hns) +{ +#define MAX_RESET_FAIL_CNT 5 + + struct hns3_hw *hw = &hns->hw; + + if (hw->adapter_state == HNS3_NIC_CLOSING) + goto reset_fail; + + if (is_reset_pending(hns)) { + hw->reset.attempts = 0; + hw->reset.stats.fail_cnt++; + hns3_warn(hw, "%s reset fail because new Reset is pending " + "attempts:%" PRIx64, + reset_string[hw->reset.level], + hw->reset.stats.fail_cnt); + hw->reset.level = HNS3_NONE_RESET; + return true; + } + + hw->reset.attempts++; + if (hw->reset.attempts < MAX_RESET_FAIL_CNT) { + hns3_atomic_set_bit(hw->reset.level, &hw->reset.pending); + hns3_warn(hw, "%s retry to reset attempts: %d", + reset_string[hw->reset.level], + hw->reset.attempts); + return true; + } + + if (rte_atomic16_read(&hw->reset.disable_cmd)) + hns3_cmd_init(hw); +reset_fail: + hw->reset.attempts = 0; + hw->reset.stats.fail_cnt++; + hns3_warn(hw, "%s reset fail fail_cnt:%" PRIx64 " success_cnt:%" PRIx64 + " global_cnt:%" PRIx64 " imp_cnt:%" PRIx64 + " request_cnt:%" PRIx64 " exec_cnt:%" PRIx64 + " merge_cnt:%" PRIx64 "adapter_state:%d", + reset_string[hw->reset.level], hw->reset.stats.fail_cnt, + hw->reset.stats.success_cnt, hw->reset.stats.global_cnt, + hw->reset.stats.imp_cnt, hw->reset.stats.request_cnt, + hw->reset.stats.exec_cnt, hw->reset.stats.merge_cnt, + hw->adapter_state); + + /* IMP no longer waiting the ready flag */ + hns3_notify_reset_ready(hw, true); + return false; +} + +static int +hns3_reset_pre(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct timeval tv; + int ret; + + if (hw->reset.stage == RESET_STAGE_NONE) { + rte_atomic16_set(&hns->hw.reset.resetting, 1); + hw->reset.stage = RESET_STAGE_DOWN; + ret = hw->reset.ops->stop_service(hns); + gettimeofday(&tv, NULL); + if (ret) { + hns3_warn(hw, "Reset step1 down fail=%d time=%ld.%.6ld", + ret, tv.tv_sec, tv.tv_usec); + return ret; + } + hns3_warn(hw, "Reset step1 down success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + hw->reset.stage = RESET_STAGE_PREWAIT; + } + if (hw->reset.stage == RESET_STAGE_PREWAIT) { + ret = hw->reset.ops->prepare_reset(hns); + gettimeofday(&tv, NULL); + if (ret) { + hns3_warn(hw, + "Reset step2 prepare wait fail=%d time=%ld.%.6ld", + ret, tv.tv_sec, tv.tv_usec); + return ret; + } + hns3_warn(hw, "Reset step2 prepare wait success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + hw->reset.stage = RESET_STAGE_REQ_HW_RESET; + hw->reset.wait_data->result = HNS3_WAIT_UNKNOWN; + } + return 0; +} + +static int +hns3_reset_post(struct hns3_adapter *hns) +{ +#define TIMEOUT_RETRIES_CNT 5 + struct hns3_hw *hw = &hns->hw; + struct timeval tv_delta; + struct timeval tv; + int ret = 0; + + if (hw->adapter_state == HNS3_NIC_CLOSING) { + hns3_warn(hw, "Don't do reset_post during closing, just uninit cmd"); + hns3_cmd_uninit(hw); + return -EPERM; + } + + if (hw->reset.stage == RESET_STAGE_DEV_INIT) { + rte_spinlock_lock(&hw->lock); + if (hw->reset.mbuf_deferred_free) { + hns3_dev_release_mbufs(hns); + hw->reset.mbuf_deferred_free = false; + } + ret = hw->reset.ops->reinit_dev(hns); + rte_spinlock_unlock(&hw->lock); + gettimeofday(&tv, NULL); + if (ret) { + hns3_warn(hw, "Reset step5 devinit fail=%d retries=%d", + ret, hw->reset.retries); + goto err; + } + hns3_warn(hw, "Reset step5 devinit success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + hw->reset.retries = 0; + hw->reset.stage = RESET_STAGE_RESTORE; + rte_eal_alarm_set(SWITCH_CONTEXT_US, + hw->reset.ops->reset_service, hns); + return -EAGAIN; + } + if (hw->reset.stage == RESET_STAGE_RESTORE) { + rte_spinlock_lock(&hw->lock); + ret = hw->reset.ops->restore_conf(hns); + rte_spinlock_unlock(&hw->lock); + gettimeofday(&tv, NULL); + if (ret) { + hns3_warn(hw, + "Reset step6 restore fail=%d retries=%d", + ret, hw->reset.retries); + goto err; + } + hns3_warn(hw, "Reset step6 restore success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + hw->reset.retries = 0; + hw->reset.stage = RESET_STAGE_DONE; + } + if (hw->reset.stage == RESET_STAGE_DONE) { + /* IMP will wait ready flag before reset */ + hns3_notify_reset_ready(hw, false); + hns3_clear_reset_level(hw, &hw->reset.pending); + rte_atomic16_clear(&hns->hw.reset.resetting); + hw->reset.attempts = 0; + hw->reset.stats.success_cnt++; + hw->reset.stage = RESET_STAGE_NONE; + rte_spinlock_lock(&hw->lock); + hw->reset.ops->start_service(hns); + rte_spinlock_unlock(&hw->lock); + gettimeofday(&tv, NULL); + timersub(&tv, &hw->reset.start_time, &tv_delta); + hns3_warn(hw, "%s reset done fail_cnt:%" PRIx64 + " success_cnt:%" PRIx64 " global_cnt:%" PRIx64 + " imp_cnt:%" PRIx64 " request_cnt:%" PRIx64 + " exec_cnt:%" PRIx64 " merge_cnt:%" PRIx64, + reset_string[hw->reset.level], + hw->reset.stats.fail_cnt, hw->reset.stats.success_cnt, + hw->reset.stats.global_cnt, hw->reset.stats.imp_cnt, + hw->reset.stats.request_cnt, hw->reset.stats.exec_cnt, + hw->reset.stats.merge_cnt); + hns3_warn(hw, + "%s reset done delta %ld ms time=%ld.%.6ld", + reset_string[hw->reset.level], + tv_delta.tv_sec * MSEC_PER_SEC + + tv_delta.tv_usec / USEC_PER_MSEC, + tv.tv_sec, tv.tv_usec); + hw->reset.level = HNS3_NONE_RESET; + } + return 0; + +err: + if (ret == -ETIME) { + hw->reset.retries++; + if (hw->reset.retries < TIMEOUT_RETRIES_CNT) { + rte_eal_alarm_set(HNS3_RESET_SYNC_US, + hw->reset.ops->reset_service, hns); + return -EAGAIN; + } + } + hw->reset.retries = 0; + return -EIO; +} + +/* + * There are three scenarios as follows: + * When the reset is not in progress, the reset process starts. + * During the reset process, if the reset level has not changed, + * the reset process continues; otherwise, the reset process is aborted. + * hw->reset.level new_level action + * HNS3_NONE_RESET HNS3_XXXX_RESET start reset + * HNS3_XXXX_RESET HNS3_XXXX_RESET continue reset + * HNS3_LOW_RESET HNS3_HIGH_RESET abort + */ +int +hns3_reset_process(struct hns3_adapter *hns, enum hns3_reset_level new_level) +{ + struct hns3_hw *hw = &hns->hw; + struct timeval tv_delta; + struct timeval tv; + int ret; + + if (hw->reset.level == HNS3_NONE_RESET) { + hw->reset.level = new_level; + hw->reset.stats.exec_cnt++; + gettimeofday(&hw->reset.start_time, NULL); + hns3_warn(hw, "Start %s reset time=%ld.%.6ld", + reset_string[hw->reset.level], + hw->reset.start_time.tv_sec, + hw->reset.start_time.tv_usec); + } + + if (is_reset_pending(hns)) { + gettimeofday(&tv, NULL); + hns3_warn(hw, + "%s reset is aborted by high level time=%ld.%.6ld", + reset_string[hw->reset.level], tv.tv_sec, tv.tv_usec); + if (hw->reset.wait_data->result == HNS3_WAIT_REQUEST) + rte_eal_alarm_cancel(hns3_wait_callback, + hw->reset.wait_data); + ret = -EBUSY; + goto err; + } + + ret = hns3_reset_pre(hns); + if (ret) + goto err; + + if (hw->reset.stage == RESET_STAGE_REQ_HW_RESET) { + ret = hns3_reset_req_hw_reset(hns); + if (ret == -EAGAIN) + return ret; + gettimeofday(&tv, NULL); + hns3_warn(hw, + "Reset step3 request IMP reset success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + hw->reset.stage = RESET_STAGE_WAIT; + hw->reset.wait_data->result = HNS3_WAIT_UNKNOWN; + } + if (hw->reset.stage == RESET_STAGE_WAIT) { + ret = hw->reset.ops->wait_hardware_ready(hns); + if (ret) + goto retry; + gettimeofday(&tv, NULL); + hns3_warn(hw, "Reset step4 reset wait success time=%ld.%.6ld", + tv.tv_sec, tv.tv_usec); + hw->reset.stage = RESET_STAGE_DEV_INIT; + } + + ret = hns3_reset_post(hns); + if (ret) + goto retry; + + return 0; +retry: + if (ret == -EAGAIN) + return ret; +err: + hns3_clear_reset_level(hw, &hw->reset.pending); + if (hns3_reset_err_handle(hns)) { + hw->reset.stage = RESET_STAGE_PREWAIT; + hns3_schedule_reset(hns); + } else { + rte_spinlock_lock(&hw->lock); + if (hw->reset.mbuf_deferred_free) { + hns3_dev_release_mbufs(hns); + hw->reset.mbuf_deferred_free = false; + } + rte_spinlock_unlock(&hw->lock); + rte_atomic16_clear(&hns->hw.reset.resetting); + hw->reset.stage = RESET_STAGE_NONE; + gettimeofday(&tv, NULL); + timersub(&tv, &hw->reset.start_time, &tv_delta); + hns3_warn(hw, "%s reset fail delta %ld ms time=%ld.%.6ld", + reset_string[hw->reset.level], + tv_delta.tv_sec * MSEC_PER_SEC + + tv_delta.tv_usec / USEC_PER_MSEC, + tv.tv_sec, tv.tv_usec); + hw->reset.level = HNS3_NONE_RESET; + } + + return -EIO; +} + +/* + * The reset process can only be terminated after handshake with IMP(step3), + * so that IMP can complete the reset process normally. + */ +void +hns3_reset_abort(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct timeval tv; + int i; + + for (i = 0; i < HNS3_QUIT_RESET_CNT; i++) { + if (hw->reset.level == HNS3_NONE_RESET) + break; + rte_delay_ms(HNS3_QUIT_RESET_DELAY_MS); + } + + /* IMP no longer waiting the ready flag */ + hns3_notify_reset_ready(hw, true); + + rte_eal_alarm_cancel(hw->reset.ops->reset_service, hns); + rte_eal_alarm_cancel(hns3_wait_callback, hw->reset.wait_data); + + if (hw->reset.level != HNS3_NONE_RESET) { + gettimeofday(&tv, NULL); + hns3_err(hw, "Failed to terminate reset: %s time=%ld.%.6ld", + reset_string[hw->reset.level], tv.tv_sec, tv.tv_usec); + } +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_intr.h b/src/spdk/dpdk/drivers/net/hns3/hns3_intr.h new file mode 100644 index 000000000..d0af16c50 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_intr.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_INTR_H_ +#define _HNS3_INTR_H_ + +#define HNS3_PPP_MPF_ECC_ERR_INT0_EN 0xFFFFFFFF +#define HNS3_PPP_MPF_ECC_ERR_INT0_EN_MASK 0xFFFFFFFF +#define HNS3_PPP_MPF_ECC_ERR_INT1_EN 0xFFFFFFFF +#define HNS3_PPP_MPF_ECC_ERR_INT1_EN_MASK 0xFFFFFFFF +#define HNS3_PPP_PF_ERR_INT_EN 0x0003 +#define HNS3_PPP_PF_ERR_INT_EN_MASK 0x0003 +#define HNS3_PPP_MPF_ECC_ERR_INT2_EN 0x003F +#define HNS3_PPP_MPF_ECC_ERR_INT2_EN_MASK 0x003F +#define HNS3_PPP_MPF_ECC_ERR_INT3_EN 0x003F +#define HNS3_PPP_MPF_ECC_ERR_INT3_EN_MASK 0x003F + +#define HNS3_MAC_COMMON_ERR_INT_EN 0x107FF +#define HNS3_MAC_COMMON_ERR_INT_EN_MASK 0x107FF + +#define HNS3_PPU_MPF_ABNORMAL_INT0_EN GENMASK(31, 0) +#define HNS3_PPU_MPF_ABNORMAL_INT0_EN_MASK GENMASK(31, 0) +#define HNS3_PPU_MPF_ABNORMAL_INT1_EN GENMASK(31, 0) +#define HNS3_PPU_MPF_ABNORMAL_INT1_EN_MASK GENMASK(31, 0) +#define HNS3_PPU_MPF_ABNORMAL_INT2_EN 0x3FFF3FFF +#define HNS3_PPU_MPF_ABNORMAL_INT2_EN_MASK 0x3FFF3FFF +#define HNS3_PPU_MPF_ABNORMAL_INT2_EN2 0xB +#define HNS3_PPU_MPF_ABNORMAL_INT2_EN2_MASK 0xB +#define HNS3_PPU_MPF_ABNORMAL_INT3_EN GENMASK(7, 0) +#define HNS3_PPU_MPF_ABNORMAL_INT3_EN_MASK GENMASK(23, 16) +#define HNS3_PPU_PF_ABNORMAL_INT_EN GENMASK(5, 0) +#define HNS3_PPU_PF_ABNORMAL_INT_EN_MASK GENMASK(5, 0) +#define HNS3_PPU_PF_INT_MSIX_MASK 0x27 +#define HNS3_PPU_MPF_INT_ST2_MSIX_MASK GENMASK(29, 28) + +#define HNS3_SSU_1BIT_ECC_ERR_INT_EN GENMASK(31, 0) +#define HNS3_SSU_1BIT_ECC_ERR_INT_EN_MASK GENMASK(31, 0) +#define HNS3_SSU_MULTI_BIT_ECC_ERR_INT_EN GENMASK(31, 0) +#define HNS3_SSU_MULTI_BIT_ECC_ERR_INT_EN_MASK GENMASK(31, 0) +#define HNS3_SSU_BIT32_ECC_ERR_INT_EN 0x0101 +#define HNS3_SSU_BIT32_ECC_ERR_INT_EN_MASK 0x0101 +#define HNS3_SSU_COMMON_INT_EN GENMASK(9, 0) +#define HNS3_SSU_COMMON_INT_EN_MASK GENMASK(9, 0) +#define HNS3_SSU_PORT_BASED_ERR_INT_EN 0x0BFF +#define HNS3_SSU_PORT_BASED_ERR_INT_EN_MASK 0x0BFF0000 +#define HNS3_SSU_FIFO_OVERFLOW_ERR_INT_EN GENMASK(23, 0) +#define HNS3_SSU_FIFO_OVERFLOW_ERR_INT_EN_MASK GENMASK(23, 0) +#define HNS3_SSU_COMMON_ERR_INT_MASK GENMASK(9, 0) +#define HNS3_SSU_PORT_INT_MSIX_MASK 0x7BFF + +#define HNS3_RESET_PROCESS_MS 200 + +struct hns3_hw_blk { + const char *name; + int (*enable_err_intr)(struct hns3_adapter *hns, bool en); +}; + +struct hns3_hw_error { + uint32_t int_msk; + const char *msg; + enum hns3_reset_level reset_level; +}; + +int hns3_enable_hw_error_intr(struct hns3_adapter *hns, bool state); +void hns3_handle_msix_error(struct hns3_adapter *hns, uint64_t *levels); +void hns3_intr_unregister(const struct rte_intr_handle *hdl, + rte_intr_callback_fn cb_fn, void *cb_arg); +void hns3_notify_reset_ready(struct hns3_hw *hw, bool enable); +int hns3_reset_init(struct hns3_hw *hw); +void hns3_wait_callback(void *param); +void hns3_schedule_reset(struct hns3_adapter *hns); +void hns3_schedule_delayed_reset(struct hns3_adapter *hns); +int hns3_reset_req_hw_reset(struct hns3_adapter *hns); +int hns3_reset_process(struct hns3_adapter *hns, + enum hns3_reset_level reset_level); +void hns3_reset_abort(struct hns3_adapter *hns); + +#endif /* _HNS3_INTR_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_logs.h b/src/spdk/dpdk/drivers/net/hns3/hns3_logs.h new file mode 100644 index 000000000..f3fc7b51d --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_logs.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_LOGS_H_ +#define _HNS3_LOGS_H_ + +extern int hns3_logtype_init; +#define PMD_INIT_LOG(level, fmt, args...) \ + rte_log(RTE_LOG_ ## level, hns3_logtype_init, "%s(): " fmt "\n", \ + __func__, ##args) +#define PMD_INIT_FUNC_TRACE() PMD_INIT_LOG(DEBUG, " >>") + +extern int hns3_logtype_driver; +#define PMD_DRV_LOG_RAW(hw, level, fmt, args...) \ + rte_log(level, hns3_logtype_driver, "%s %s(): " fmt, \ + (hw)->data->name, __func__, ## args) + +#define hns3_err(hw, fmt, args...) \ + PMD_DRV_LOG_RAW(hw, RTE_LOG_ERR, fmt "\n", ## args) + +#define hns3_warn(hw, fmt, args...) \ + PMD_DRV_LOG_RAW(hw, RTE_LOG_WARNING, fmt "\n", ## args) + +#define hns3_notice(hw, fmt, args...) \ + PMD_DRV_LOG_RAW(hw, RTE_LOG_NOTICE, fmt "\n", ## args) + +#define hns3_info(hw, fmt, args...) \ + PMD_DRV_LOG_RAW(hw, RTE_LOG_INFO, fmt "\n", ## args) + +#define hns3_dbg(hw, fmt, args...) \ + PMD_DRV_LOG_RAW(hw, RTE_LOG_DEBUG, fmt "\n", ## args) + +#endif /* _HNS3_LOGS_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_mbx.c b/src/spdk/dpdk/drivers/net/hns3/hns3_mbx.c new file mode 100644 index 000000000..34c8c688f --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_mbx.c @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <unistd.h> +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_ethdev_driver.h> +#include <rte_io.h> +#include <rte_spinlock.h> +#include <rte_pci.h> +#include <rte_bus_pci.h> + +#include "hns3_ethdev.h" +#include "hns3_regs.h" +#include "hns3_logs.h" +#include "hns3_intr.h" + +#define HNS3_CMD_CODE_OFFSET 2 + +static const struct errno_respcode_map err_code_map[] = { + {0, 0}, + {1, -EPERM}, + {2, -ENOENT}, + {5, -EIO}, + {11, -EAGAIN}, + {12, -ENOMEM}, + {16, -EBUSY}, + {22, -EINVAL}, + {28, -ENOSPC}, + {95, -EOPNOTSUPP}, +}; + +static int +hns3_resp_to_errno(uint16_t resp_code) +{ + uint32_t i, num; + + num = sizeof(err_code_map) / sizeof(struct errno_respcode_map); + for (i = 0; i < num; i++) { + if (err_code_map[i].resp_code == resp_code) + return err_code_map[i].err_no; + } + + return -EIO; +} + +static void +hns3_poll_all_sync_msg(void) +{ + struct rte_eth_dev *eth_dev; + struct hns3_adapter *adapter; + const char *name; + uint16_t port_id; + + RTE_ETH_FOREACH_DEV(port_id) { + eth_dev = &rte_eth_devices[port_id]; + name = eth_dev->device->driver->name; + if (strcmp(name, "net_hns3") && strcmp(name, "net_hns3_vf")) + continue; + adapter = eth_dev->data->dev_private; + if (!adapter || adapter->hw.adapter_state == HNS3_NIC_CLOSED) + continue; + /* Synchronous msg, the mbx_resp.req_msg_data is non-zero */ + if (adapter->hw.mbx_resp.req_msg_data) + hns3_dev_handle_mbx_msg(&adapter->hw); + } +} + +static int +hns3_get_mbx_resp(struct hns3_hw *hw, uint16_t code0, uint16_t code1, + uint8_t *resp_data, uint16_t resp_len) +{ +#define HNS3_MAX_RETRY_MS 500 + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + struct hns3_mbx_resp_status *mbx_resp; + bool in_irq = false; + uint64_t now; + uint64_t end; + + if (resp_len > HNS3_MBX_MAX_RESP_DATA_SIZE) { + hns3_err(hw, "VF mbx response len(=%d) exceeds maximum(=%d)", + resp_len, HNS3_MBX_MAX_RESP_DATA_SIZE); + return -EINVAL; + } + + now = get_timeofday_ms(); + end = now + HNS3_MAX_RETRY_MS; + while ((hw->mbx_resp.head != hw->mbx_resp.tail + hw->mbx_resp.lost) && + (now < end)) { + if (rte_atomic16_read(&hw->reset.disable_cmd)) { + hns3_err(hw, "Don't wait for mbx respone because of " + "disable_cmd"); + return -EBUSY; + } + + if (is_reset_pending(hns)) { + hw->mbx_resp.req_msg_data = 0; + hns3_err(hw, "Don't wait for mbx respone because of " + "reset pending"); + return -EIO; + } + + /* + * The mbox response is running on the interrupt thread. + * Sending mbox in the interrupt thread cannot wait for the + * response, so polling the mbox response on the irq thread. + */ + if (pthread_equal(hw->irq_thread_id, pthread_self())) { + in_irq = true; + hns3_poll_all_sync_msg(); + } else { + rte_delay_ms(HNS3_POLL_RESPONE_MS); + } + now = get_timeofday_ms(); + } + hw->mbx_resp.req_msg_data = 0; + if (now >= end) { + hw->mbx_resp.lost++; + hns3_err(hw, + "VF could not get mbx(%d,%d) head(%d) tail(%d) lost(%d) from PF in_irq:%d", + code0, code1, hw->mbx_resp.head, hw->mbx_resp.tail, + hw->mbx_resp.lost, in_irq); + return -ETIME; + } + rte_io_rmb(); + mbx_resp = &hw->mbx_resp; + + if (mbx_resp->resp_status) + return mbx_resp->resp_status; + + if (resp_data) + memcpy(resp_data, &mbx_resp->additional_info[0], resp_len); + + return 0; +} + +int +hns3_send_mbx_msg(struct hns3_hw *hw, uint16_t code, uint16_t subcode, + const uint8_t *msg_data, uint8_t msg_len, bool need_resp, + uint8_t *resp_data, uint16_t resp_len) +{ + struct hns3_mbx_vf_to_pf_cmd *req; + struct hns3_cmd_desc desc; + bool is_ring_vector_msg; + int offset; + int ret; + + req = (struct hns3_mbx_vf_to_pf_cmd *)desc.data; + + /* first two bytes are reserved for code & subcode */ + if (msg_len > (HNS3_MBX_MAX_MSG_SIZE - HNS3_CMD_CODE_OFFSET)) { + hns3_err(hw, + "VF send mbx msg fail, msg len %d exceeds max payload len %d", + msg_len, HNS3_MBX_MAX_MSG_SIZE - HNS3_CMD_CODE_OFFSET); + return -EINVAL; + } + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_MBX_VF_TO_PF, false); + req->msg[0] = code; + is_ring_vector_msg = (code == HNS3_MBX_MAP_RING_TO_VECTOR) || + (code == HNS3_MBX_UNMAP_RING_TO_VECTOR) || + (code == HNS3_MBX_GET_RING_VECTOR_MAP); + if (!is_ring_vector_msg) + req->msg[1] = subcode; + if (msg_data) { + offset = is_ring_vector_msg ? 1 : HNS3_CMD_CODE_OFFSET; + memcpy(&req->msg[offset], msg_data, msg_len); + } + + /* synchronous send */ + if (need_resp) { + req->mbx_need_resp |= HNS3_MBX_NEED_RESP_BIT; + rte_spinlock_lock(&hw->mbx_resp.lock); + hw->mbx_resp.req_msg_data = (uint32_t)code << 16 | subcode; + hw->mbx_resp.head++; + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + rte_spinlock_unlock(&hw->mbx_resp.lock); + hns3_err(hw, "VF failed(=%d) to send mbx message to PF", + ret); + return ret; + } + + ret = hns3_get_mbx_resp(hw, code, subcode, resp_data, resp_len); + rte_spinlock_unlock(&hw->mbx_resp.lock); + } else { + /* asynchronous send */ + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "VF failed(=%d) to send mbx message to PF", + ret); + return ret; + } + } + + return ret; +} + +static bool +hns3_cmd_crq_empty(struct hns3_hw *hw) +{ + uint32_t tail = hns3_read_dev(hw, HNS3_CMDQ_RX_TAIL_REG); + + return tail == hw->cmq.crq.next_to_use; +} + +static void +hns3_mbx_handler(struct hns3_hw *hw) +{ + struct hns3_mac *mac = &hw->mac; + enum hns3_reset_level reset_level; + uint16_t *msg_q; + uint8_t opcode; + uint32_t tail; + + tail = hw->arq.tail; + + /* process all the async queue messages */ + while (tail != hw->arq.head) { + msg_q = hw->arq.msg_q[hw->arq.head]; + + opcode = msg_q[0] & 0xff; + switch (opcode) { + case HNS3_MBX_LINK_STAT_CHANGE: + memcpy(&mac->link_speed, &msg_q[2], + sizeof(mac->link_speed)); + mac->link_status = rte_le_to_cpu_16(msg_q[1]); + mac->link_duplex = (uint8_t)rte_le_to_cpu_16(msg_q[4]); + break; + case HNS3_MBX_ASSERTING_RESET: + /* PF has asserted reset hence VF should go in pending + * state and poll for the hardware reset status till it + * has been completely reset. After this stack should + * eventually be re-initialized. + */ + reset_level = rte_le_to_cpu_16(msg_q[1]); + hns3_atomic_set_bit(reset_level, &hw->reset.pending); + + hns3_warn(hw, "PF inform reset level %d", reset_level); + hw->reset.stats.request_cnt++; + hns3_schedule_reset(HNS3_DEV_HW_TO_ADAPTER(hw)); + break; + default: + hns3_err(hw, "Fetched unsupported(%d) message from arq", + opcode); + break; + } + + hns3_mbx_head_ptr_move_arq(hw->arq); + msg_q = hw->arq.msg_q[hw->arq.head]; + } +} + +/* + * Case1: receive response after timeout, req_msg_data + * is 0, not equal resp_msg, do lost-- + * Case2: receive last response during new send_mbx_msg, + * req_msg_data is different with resp_msg, let + * lost--, continue to wait for response. + */ +static void +hns3_update_resp_position(struct hns3_hw *hw, uint32_t resp_msg) +{ + struct hns3_mbx_resp_status *resp = &hw->mbx_resp; + uint32_t tail = resp->tail + 1; + + if (tail > resp->head) + tail = resp->head; + if (resp->req_msg_data != resp_msg) { + if (resp->lost) + resp->lost--; + hns3_warn(hw, "Received a mismatched response req_msg(%x) " + "resp_msg(%x) head(%d) tail(%d) lost(%d)", + resp->req_msg_data, resp_msg, resp->head, tail, + resp->lost); + } else if (tail + resp->lost > resp->head) { + resp->lost--; + hns3_warn(hw, "Received a new response again resp_msg(%x) " + "head(%d) tail(%d) lost(%d)", resp_msg, + resp->head, tail, resp->lost); + } + rte_io_wmb(); + resp->tail = tail; +} + +static void +hns3_link_fail_parse(struct hns3_hw *hw, uint8_t link_fail_code) +{ + switch (link_fail_code) { + case HNS3_MBX_LF_NORMAL: + break; + case HNS3_MBX_LF_REF_CLOCK_LOST: + hns3_warn(hw, "Reference clock lost!"); + break; + case HNS3_MBX_LF_XSFP_TX_DISABLE: + hns3_warn(hw, "SFP tx is disabled!"); + break; + case HNS3_MBX_LF_XSFP_ABSENT: + hns3_warn(hw, "SFP is absent!"); + break; + default: + hns3_warn(hw, "Unknown fail code:%u!", link_fail_code); + break; + } +} + +static void +hns3_handle_link_change_event(struct hns3_hw *hw, + struct hns3_mbx_pf_to_vf_cmd *req) +{ +#define LINK_STATUS_OFFSET 1 +#define LINK_FAIL_CODE_OFFSET 2 + + if (!req->msg[LINK_STATUS_OFFSET]) + hns3_link_fail_parse(hw, req->msg[LINK_FAIL_CODE_OFFSET]); + + hns3_update_link_status(hw); +} + +static void +hns3_handle_promisc_info(struct hns3_hw *hw, uint16_t promisc_en) +{ + if (!promisc_en) { + /* + * When promisc/allmulti mode is closed by the hns3 PF kernel + * ethdev driver for untrusted, modify VF's related status. + */ + hns3_warn(hw, "Promisc mode will be closed by host for being " + "untrusted."); + hw->data->promiscuous = 0; + hw->data->all_multicast = 0; + } +} + +void +hns3_dev_handle_mbx_msg(struct hns3_hw *hw) +{ + struct hns3_mbx_resp_status *resp = &hw->mbx_resp; + struct hns3_cmq_ring *crq = &hw->cmq.crq; + struct hns3_mbx_pf_to_vf_cmd *req; + struct hns3_cmd_desc *desc; + uint32_t msg_data; + uint16_t *msg_q; + uint8_t opcode; + uint16_t flag; + uint8_t *temp; + int i; + + while (!hns3_cmd_crq_empty(hw)) { + if (rte_atomic16_read(&hw->reset.disable_cmd)) + return; + + desc = &crq->desc[crq->next_to_use]; + req = (struct hns3_mbx_pf_to_vf_cmd *)desc->data; + opcode = req->msg[0] & 0xff; + + flag = rte_le_to_cpu_16(crq->desc[crq->next_to_use].flag); + if (unlikely(!hns3_get_bit(flag, HNS3_CMDQ_RX_OUTVLD_B))) { + hns3_warn(hw, + "dropped invalid mailbox message, code = %d", + opcode); + + /* dropping/not processing this invalid message */ + crq->desc[crq->next_to_use].flag = 0; + hns3_mbx_ring_ptr_move_crq(crq); + continue; + } + + switch (opcode) { + case HNS3_MBX_PF_VF_RESP: + resp->resp_status = hns3_resp_to_errno(req->msg[3]); + + temp = (uint8_t *)&req->msg[4]; + for (i = 0; i < HNS3_MBX_MAX_RESP_DATA_SIZE; i++) { + resp->additional_info[i] = *temp; + temp++; + } + msg_data = (uint32_t)req->msg[1] << 16 | req->msg[2]; + hns3_update_resp_position(hw, msg_data); + break; + case HNS3_MBX_LINK_STAT_CHANGE: + case HNS3_MBX_ASSERTING_RESET: + msg_q = hw->arq.msg_q[hw->arq.tail]; + memcpy(&msg_q[0], req->msg, + HNS3_MBX_MAX_ARQ_MSG_SIZE * sizeof(uint16_t)); + hns3_mbx_tail_ptr_move_arq(hw->arq); + + hns3_mbx_handler(hw); + break; + case HNS3_MBX_PUSH_LINK_STATUS: + hns3_handle_link_change_event(hw, req); + break; + case HNS3_MBX_PUSH_PROMISC_INFO: + /* + * When the trust status of VF device changed by the + * hns3 PF kernel driver, VF driver will receive this + * mailbox message from PF driver. + */ + hns3_handle_promisc_info(hw, req->msg[1]); + break; + default: + hns3_err(hw, + "VF received unsupported(%d) mbx msg from PF", + req->msg[0]); + break; + } + + crq->desc[crq->next_to_use].flag = 0; + hns3_mbx_ring_ptr_move_crq(crq); + } + + /* Write back CMDQ_RQ header pointer, IMP need this pointer */ + hns3_write_dev(hw, HNS3_CMDQ_RX_HEAD_REG, crq->next_to_use); +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_mbx.h b/src/spdk/dpdk/drivers/net/hns3/hns3_mbx.h new file mode 100644 index 000000000..d6d70f686 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_mbx.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_MBX_H_ +#define _HNS3_MBX_H_ + +#define HNS3_MBX_VF_MSG_DATA_NUM 16 + +enum HNS3_MBX_OPCODE { + HNS3_MBX_RESET = 0x01, /* (VF -> PF) assert reset */ + HNS3_MBX_ASSERTING_RESET, /* (PF -> VF) PF is asserting reset */ + HNS3_MBX_SET_UNICAST, /* (VF -> PF) set UC addr */ + HNS3_MBX_SET_MULTICAST, /* (VF -> PF) set MC addr */ + HNS3_MBX_SET_VLAN, /* (VF -> PF) set VLAN */ + HNS3_MBX_MAP_RING_TO_VECTOR, /* (VF -> PF) map ring-to-vector */ + HNS3_MBX_UNMAP_RING_TO_VECTOR, /* (VF -> PF) unamp ring-to-vector */ + HNS3_MBX_SET_PROMISC_MODE, /* (VF -> PF) set promiscuous mode */ + HNS3_MBX_SET_MACVLAN, /* (VF -> PF) set unicast filter */ + HNS3_MBX_API_NEGOTIATE, /* (VF -> PF) negotiate API version */ + HNS3_MBX_GET_QINFO, /* (VF -> PF) get queue config */ + HNS3_MBX_GET_QDEPTH, /* (VF -> PF) get queue depth */ + HNS3_MBX_GET_TCINFO, /* (VF -> PF) get TC config */ + HNS3_MBX_GET_RETA, /* (VF -> PF) get RETA */ + HNS3_MBX_GET_RSS_KEY, /* (VF -> PF) get RSS key */ + HNS3_MBX_GET_MAC_ADDR, /* (VF -> PF) get MAC addr */ + HNS3_MBX_PF_VF_RESP, /* (PF -> VF) generate respone to VF */ + HNS3_MBX_GET_BDNUM, /* (VF -> PF) get BD num */ + HNS3_MBX_GET_BUFSIZE, /* (VF -> PF) get buffer size */ + HNS3_MBX_GET_STREAMID, /* (VF -> PF) get stream id */ + HNS3_MBX_SET_AESTART, /* (VF -> PF) start ae */ + HNS3_MBX_SET_TSOSTATS, /* (VF -> PF) get tso stats */ + HNS3_MBX_LINK_STAT_CHANGE, /* (PF -> VF) link status has changed */ + HNS3_MBX_GET_BASE_CONFIG, /* (VF -> PF) get config */ + HNS3_MBX_BIND_FUNC_QUEUE, /* (VF -> PF) bind function and queue */ + HNS3_MBX_GET_LINK_STATUS, /* (VF -> PF) get link status */ + HNS3_MBX_QUEUE_RESET, /* (VF -> PF) reset queue */ + HNS3_MBX_KEEP_ALIVE, /* (VF -> PF) send keep alive cmd */ + HNS3_MBX_SET_ALIVE, /* (VF -> PF) set alive state */ + HNS3_MBX_SET_MTU, /* (VF -> PF) set mtu */ + HNS3_MBX_GET_QID_IN_PF, /* (VF -> PF) get queue id in pf */ + + HNS3_MBX_PUSH_PROMISC_INFO = 36, /* (PF -> VF) push vf promisc info */ + + HNS3_MBX_HANDLE_VF_TBL = 38, /* (VF -> PF) store/clear hw cfg tbl */ + HNS3_MBX_GET_RING_VECTOR_MAP, /* (VF -> PF) get ring-to-vector map */ + HNS3_MBX_PUSH_LINK_STATUS = 201, /* (IMP -> PF) get port link status */ +}; + +/* below are per-VF mac-vlan subcodes */ +enum hns3_mbx_mac_vlan_subcode { + HNS3_MBX_MAC_VLAN_UC_MODIFY = 0, /* modify UC mac addr */ + HNS3_MBX_MAC_VLAN_UC_ADD, /* add a new UC mac addr */ + HNS3_MBX_MAC_VLAN_UC_REMOVE, /* remove a new UC mac addr */ + HNS3_MBX_MAC_VLAN_MC_MODIFY, /* modify MC mac addr */ + HNS3_MBX_MAC_VLAN_MC_ADD, /* add new MC mac addr */ + HNS3_MBX_MAC_VLAN_MC_REMOVE, /* remove MC mac addr */ +}; + +/* below are per-VF vlan cfg subcodes */ +enum hns3_mbx_vlan_cfg_subcode { + HNS3_MBX_VLAN_FILTER = 0, /* set vlan filter */ + HNS3_MBX_VLAN_TX_OFF_CFG, /* set tx side vlan offload */ + HNS3_MBX_VLAN_RX_OFF_CFG, /* set rx side vlan offload */ +}; + +enum hns3_mbx_tbl_cfg_subcode { + HNS3_MBX_VPORT_LIST_CLEAR = 0, +}; + +enum hns3_mbx_link_fail_subcode { + HNS3_MBX_LF_NORMAL = 0, + HNS3_MBX_LF_REF_CLOCK_LOST, + HNS3_MBX_LF_XSFP_TX_DISABLE, + HNS3_MBX_LF_XSFP_ABSENT, +}; + +#define HNS3_MBX_MAX_MSG_SIZE 16 +#define HNS3_MBX_MAX_RESP_DATA_SIZE 8 +#define HNS3_MBX_RING_MAP_BASIC_MSG_NUM 3 +#define HNS3_MBX_RING_NODE_VARIABLE_NUM 3 + +struct hns3_mbx_resp_status { + rte_spinlock_t lock; /* protects against contending sync cmd resp */ + uint32_t req_msg_data; + uint32_t head; + uint32_t tail; + uint32_t lost; + int resp_status; + uint8_t additional_info[HNS3_MBX_MAX_RESP_DATA_SIZE]; +}; + +struct errno_respcode_map { + uint16_t resp_code; + int err_no; +}; + +#define HNS3_MBX_NEED_RESP_BIT BIT(0) + +struct hns3_mbx_vf_to_pf_cmd { + uint8_t rsv; + uint8_t mbx_src_vfid; /* Auto filled by IMP */ + uint8_t mbx_need_resp; + uint8_t rsv1; + uint8_t msg_len; + uint8_t rsv2[3]; + uint8_t msg[HNS3_MBX_MAX_MSG_SIZE]; +}; + +struct hns3_mbx_pf_to_vf_cmd { + uint8_t dest_vfid; + uint8_t rsv[3]; + uint8_t msg_len; + uint8_t rsv1[3]; + uint16_t msg[8]; +}; + +struct hns3_ring_chain_param { + uint8_t ring_type; + uint8_t tqp_index; + uint8_t int_gl_index; +}; + +#define HNS3_MBX_MAX_RING_CHAIN_PARAM_NUM 4 +struct hns3_vf_bind_vector_msg { + uint8_t vector_id; + uint8_t ring_num; + struct hns3_ring_chain_param param[HNS3_MBX_MAX_RING_CHAIN_PARAM_NUM]; +}; + +struct hns3_vf_rst_cmd { + uint8_t dest_vfid; + uint8_t vf_rst; + uint8_t rsv[22]; +}; + +struct hns3_pf_rst_done_cmd { + uint8_t pf_rst_done; + uint8_t rsv[23]; +}; + +#define HNS3_PF_RESET_DONE_BIT BIT(0) + +/* used by VF to store the received Async responses from PF */ +struct hns3_mbx_arq_ring { +#define HNS3_MBX_MAX_ARQ_MSG_SIZE 8 +#define HNS3_MBX_MAX_ARQ_MSG_NUM 1024 + uint32_t head; + uint32_t tail; + uint32_t count; + uint16_t msg_q[HNS3_MBX_MAX_ARQ_MSG_NUM][HNS3_MBX_MAX_ARQ_MSG_SIZE]; +}; + +#define hns3_mbx_ring_ptr_move_crq(crq) \ + ((crq)->next_to_use = ((crq)->next_to_use + 1) % (crq)->desc_num) +#define hns3_mbx_tail_ptr_move_arq(arq) \ + ((arq).tail = ((arq).tail + 1) % HNS3_MBX_MAX_ARQ_MSG_SIZE) +#define hns3_mbx_head_ptr_move_arq(arq) \ + ((arq).head = ((arq).head + 1) % HNS3_MBX_MAX_ARQ_MSG_SIZE) + +struct hns3_hw; +void hns3_dev_handle_mbx_msg(struct hns3_hw *hw); +int hns3_send_mbx_msg(struct hns3_hw *hw, uint16_t code, uint16_t subcode, + const uint8_t *msg_data, uint8_t msg_len, bool need_resp, + uint8_t *resp_data, uint16_t resp_len); +#endif /* _HNS3_MBX_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_mp.c b/src/spdk/dpdk/drivers/net/hns3/hns3_mp.c new file mode 100644 index 000000000..596c31064 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_mp.c @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdbool.h> + +#include <rte_eal.h> +#include <rte_ethdev_driver.h> +#include <rte_string_fns.h> +#include <rte_io.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" +#include "hns3_rxtx.h" +#include "hns3_mp.h" + +/* + * Initialize IPC message. + * + * @param[in] dev + * Pointer to Ethernet structure. + * @param[out] msg + * Pointer to message to fill in. + * @param[in] type + * Message type. + */ +static inline void +mp_init_msg(struct rte_eth_dev *dev, struct rte_mp_msg *msg, + enum hns3_mp_req_type type) +{ + struct hns3_mp_param *param = (struct hns3_mp_param *)msg->param; + + memset(msg, 0, sizeof(*msg)); + strlcpy(msg->name, HNS3_MP_NAME, sizeof(msg->name)); + msg->len_param = sizeof(*param); + param->type = type; + param->port_id = dev->data->port_id; +} + +/* + * IPC message handler of primary process. + * + * @param[in] dev + * Pointer to Ethernet structure. + * @param[in] peer + * Pointer to the peer socket path. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +static int +mp_primary_handle(const struct rte_mp_msg *mp_msg __rte_unused, + const void *peer __rte_unused) +{ + return 0; +} + +/* + * IPC message handler of a secondary process. + * + * @param[in] dev + * Pointer to Ethernet structure. + * @param[in] peer + * Pointer to the peer socket path. + * + * @return + * 0 on success, a negative errno value otherwise and rte_errno is set. + */ +static int +mp_secondary_handle(const struct rte_mp_msg *mp_msg, const void *peer) +{ + struct rte_mp_msg mp_res; + struct hns3_mp_param *res = (struct hns3_mp_param *)mp_res.param; + const struct hns3_mp_param *param = + (const struct hns3_mp_param *)mp_msg->param; + struct rte_eth_dev *dev; + int ret; + + if (!rte_eth_dev_is_valid_port(param->port_id)) { + rte_errno = ENODEV; + PMD_INIT_LOG(ERR, "port %u invalid port ID", param->port_id); + return -rte_errno; + } + dev = &rte_eth_devices[param->port_id]; + switch (param->type) { + case HNS3_MP_REQ_START_RXTX: + PMD_INIT_LOG(INFO, "port %u starting datapath", + dev->data->port_id); + rte_mb(); + hns3_set_rxtx_function(dev); + mp_init_msg(dev, &mp_res, param->type); + res->result = 0; + ret = rte_mp_reply(&mp_res, peer); + break; + case HNS3_MP_REQ_STOP_RXTX: + PMD_INIT_LOG(INFO, "port %u stopping datapath", + dev->data->port_id); + hns3_set_rxtx_function(dev); + rte_mb(); + mp_init_msg(dev, &mp_res, param->type); + res->result = 0; + ret = rte_mp_reply(&mp_res, peer); + break; + default: + rte_errno = EINVAL; + PMD_INIT_LOG(ERR, "port %u invalid mp request type", + dev->data->port_id); + return -rte_errno; + } + return ret; +} + +/* + * Broadcast request of stopping/starting data-path to secondary processes. + * + * @param[in] dev + * Pointer to Ethernet structure. + * @param[in] type + * Request type. + */ +static void +mp_req_on_rxtx(struct rte_eth_dev *dev, enum hns3_mp_req_type type) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_mp_msg mp_req; + struct rte_mp_msg *mp_res; + struct rte_mp_reply mp_rep; + struct hns3_mp_param *res; + struct timespec ts; + int ret; + int i; + + if (!hw->secondary_cnt) + return; + if (type != HNS3_MP_REQ_START_RXTX && type != HNS3_MP_REQ_STOP_RXTX) { + hns3_err(hw, "port %u unknown request (req_type %d)", + dev->data->port_id, type); + return; + } + mp_init_msg(dev, &mp_req, type); + ts.tv_sec = HNS3_MP_REQ_TIMEOUT_SEC; + ts.tv_nsec = 0; + ret = rte_mp_request_sync(&mp_req, &mp_rep, &ts); + if (ret) { + hns3_err(hw, "port %u failed to request stop/start Rx/Tx (%d)", + dev->data->port_id, type); + goto exit; + } + if (mp_rep.nb_sent != mp_rep.nb_received) { + PMD_INIT_LOG(ERR, + "port %u not all secondaries responded (req_type %d)", + dev->data->port_id, type); + goto exit; + } + for (i = 0; i < mp_rep.nb_received; i++) { + mp_res = &mp_rep.msgs[i]; + res = (struct hns3_mp_param *)mp_res->param; + if (res->result) { + hns3_err(hw, "port %u request failed on secondary #%d", + dev->data->port_id, i); + goto exit; + } + } +exit: + free(mp_rep.msgs); +} + +/* + * Broadcast request of starting data-path to secondary processes. The request + * is synchronous. + * + * @param[in] dev + * Pointer to Ethernet structure. + */ +void hns3_mp_req_start_rxtx(struct rte_eth_dev *dev) +{ + mp_req_on_rxtx(dev, HNS3_MP_REQ_START_RXTX); +} + +/* + * Broadcast request of stopping data-path to secondary processes. The request + * is synchronous. + * + * @param[in] dev + * Pointer to Ethernet structure. + */ +void hns3_mp_req_stop_rxtx(struct rte_eth_dev *dev) +{ + mp_req_on_rxtx(dev, HNS3_MP_REQ_STOP_RXTX); +} + +/* + * Initialize by primary process. + */ +void hns3_mp_init_primary(void) +{ + rte_mp_action_register(HNS3_MP_NAME, mp_primary_handle); +} + +/* + * Un-initialize by primary process. + */ +void hns3_mp_uninit_primary(void) +{ + rte_mp_action_unregister(HNS3_MP_NAME); +} + +/* + * Initialize by secondary process. + */ +void hns3_mp_init_secondary(void) +{ + rte_mp_action_register(HNS3_MP_NAME, mp_secondary_handle); +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_mp.h b/src/spdk/dpdk/drivers/net/hns3/hns3_mp.h new file mode 100644 index 000000000..aefbeb140 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_mp.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_MP_H_ +#define _HNS3_MP_H_ + +void hns3_mp_req_start_rxtx(struct rte_eth_dev *dev); +void hns3_mp_req_stop_rxtx(struct rte_eth_dev *dev); +void hns3_mp_init_primary(void); +void hns3_mp_uninit_primary(void); +void hns3_mp_init_secondary(void); + +#endif /* _HNS3_MP_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_regs.c b/src/spdk/dpdk/drivers/net/hns3/hns3_regs.c new file mode 100644 index 000000000..a3f2a51f9 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_regs.c @@ -0,0 +1,375 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <stdio.h> +#include <inttypes.h> +#include <unistd.h> +#include <rte_bus_pci.h> +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_dev.h> +#include <rte_eal.h> +#include <rte_ether.h> +#include <rte_ethdev_driver.h> +#include <rte_ethdev_pci.h> +#include <rte_io.h> +#include <rte_pci.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" +#include "hns3_rxtx.h" +#include "hns3_regs.h" + +#define MAX_SEPARATE_NUM 4 +#define SEPARATOR_VALUE 0xFFFFFFFF +#define REG_NUM_PER_LINE 4 +#define REG_LEN_PER_LINE (REG_NUM_PER_LINE * sizeof(uint32_t)) + +static const uint32_t cmdq_reg_addrs[] = {HNS3_CMDQ_TX_ADDR_L_REG, + HNS3_CMDQ_TX_ADDR_H_REG, + HNS3_CMDQ_TX_DEPTH_REG, + HNS3_CMDQ_TX_TAIL_REG, + HNS3_CMDQ_TX_HEAD_REG, + HNS3_CMDQ_RX_ADDR_L_REG, + HNS3_CMDQ_RX_ADDR_H_REG, + HNS3_CMDQ_RX_DEPTH_REG, + HNS3_CMDQ_RX_TAIL_REG, + HNS3_CMDQ_RX_HEAD_REG, + HNS3_VECTOR0_CMDQ_SRC_REG, + HNS3_CMDQ_INTR_STS_REG, + HNS3_CMDQ_INTR_EN_REG, + HNS3_CMDQ_INTR_GEN_REG}; + +static const uint32_t common_reg_addrs[] = {HNS3_MISC_VECTOR_REG_BASE, + HNS3_VECTOR0_OTER_EN_REG, + HNS3_MISC_RESET_STS_REG, + HNS3_VECTOR0_OTHER_INT_STS_REG, + HNS3_GLOBAL_RESET_REG, + HNS3_FUN_RST_ING, + HNS3_GRO_EN_REG}; + +static const uint32_t common_vf_reg_addrs[] = {HNS3_MISC_VECTOR_REG_BASE, + HNS3_FUN_RST_ING, + HNS3_GRO_EN_REG}; + +static const uint32_t ring_reg_addrs[] = {HNS3_RING_RX_BASEADDR_L_REG, + HNS3_RING_RX_BASEADDR_H_REG, + HNS3_RING_RX_BD_NUM_REG, + HNS3_RING_RX_BD_LEN_REG, + HNS3_RING_RX_MERGE_EN_REG, + HNS3_RING_RX_TAIL_REG, + HNS3_RING_RX_HEAD_REG, + HNS3_RING_RX_FBDNUM_REG, + HNS3_RING_RX_OFFSET_REG, + HNS3_RING_RX_FBD_OFFSET_REG, + HNS3_RING_RX_STASH_REG, + HNS3_RING_RX_BD_ERR_REG, + HNS3_RING_TX_BASEADDR_L_REG, + HNS3_RING_TX_BASEADDR_H_REG, + HNS3_RING_TX_BD_NUM_REG, + HNS3_RING_TX_PRIORITY_REG, + HNS3_RING_TX_TC_REG, + HNS3_RING_TX_MERGE_EN_REG, + HNS3_RING_TX_TAIL_REG, + HNS3_RING_TX_HEAD_REG, + HNS3_RING_TX_FBDNUM_REG, + HNS3_RING_TX_OFFSET_REG, + HNS3_RING_TX_EBD_NUM_REG, + HNS3_RING_TX_EBD_OFFSET_REG, + HNS3_RING_TX_BD_ERR_REG, + HNS3_RING_EN_REG}; + +static const uint32_t tqp_intr_reg_addrs[] = {HNS3_TQP_INTR_CTRL_REG, + HNS3_TQP_INTR_GL0_REG, + HNS3_TQP_INTR_GL1_REG, + HNS3_TQP_INTR_GL2_REG, + HNS3_TQP_INTR_RL_REG}; + +static int +hns3_get_regs_num(struct hns3_hw *hw, uint32_t *regs_num_32_bit, + uint32_t *regs_num_64_bit) +{ + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_REG_NUM, true); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Query register number cmd failed, ret = %d", + ret); + return ret; + } + + *regs_num_32_bit = rte_le_to_cpu_32(desc.data[0]); + *regs_num_64_bit = rte_le_to_cpu_32(desc.data[1]); + + return 0; +} + +static int +hns3_get_regs_length(struct hns3_hw *hw, uint32_t *length) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + int cmdq_lines, common_lines, ring_lines, tqp_intr_lines; + uint32_t regs_num_32_bit, regs_num_64_bit; + uint32_t len; + int ret; + + cmdq_lines = sizeof(cmdq_reg_addrs) / REG_LEN_PER_LINE + 1; + if (hns->is_vf) + common_lines = + sizeof(common_vf_reg_addrs) / REG_LEN_PER_LINE + 1; + else + common_lines = sizeof(common_reg_addrs) / REG_LEN_PER_LINE + 1; + ring_lines = sizeof(ring_reg_addrs) / REG_LEN_PER_LINE + 1; + tqp_intr_lines = sizeof(tqp_intr_reg_addrs) / REG_LEN_PER_LINE + 1; + + len = (cmdq_lines + common_lines + ring_lines * hw->tqps_num + + tqp_intr_lines * hw->num_msi) * REG_LEN_PER_LINE; + + if (!hns->is_vf) { + ret = hns3_get_regs_num(hw, ®s_num_32_bit, ®s_num_64_bit); + if (ret) { + hns3_err(hw, "Get register number failed, ret = %d.", + ret); + return -ENOTSUP; + } + len += regs_num_32_bit * sizeof(uint32_t) + + regs_num_64_bit * sizeof(uint64_t); + } + + *length = len; + return 0; +} + +static int +hns3_get_32_bit_regs(struct hns3_hw *hw, uint32_t regs_num, void *data) +{ +#define HNS3_32_BIT_REG_RTN_DATANUM 8 +#define HNS3_32_BIT_DESC_NODATA_LEN 2 + struct hns3_cmd_desc *desc; + uint32_t *reg_val = data; + uint32_t *desc_data; + int cmd_num; + int i, k, n; + int ret; + + if (regs_num == 0) + return 0; + + cmd_num = DIV_ROUND_UP(regs_num + HNS3_32_BIT_DESC_NODATA_LEN, + HNS3_32_BIT_REG_RTN_DATANUM); + desc = rte_zmalloc("hns3-32bit-regs", + sizeof(struct hns3_cmd_desc) * cmd_num, 0); + if (desc == NULL) { + hns3_err(hw, "Failed to allocate %zx bytes needed to " + "store 32bit regs", + sizeof(struct hns3_cmd_desc) * cmd_num); + return -ENOMEM; + } + + hns3_cmd_setup_basic_desc(&desc[0], HNS3_OPC_QUERY_32_BIT_REG, true); + ret = hns3_cmd_send(hw, desc, cmd_num); + if (ret) { + hns3_err(hw, "Query 32 bit register cmd failed, ret = %d", + ret); + rte_free(desc); + return ret; + } + + for (i = 0; i < cmd_num; i++) { + if (i == 0) { + desc_data = &desc[i].data[0]; + n = HNS3_32_BIT_REG_RTN_DATANUM - + HNS3_32_BIT_DESC_NODATA_LEN; + } else { + desc_data = (uint32_t *)(&desc[i]); + n = HNS3_32_BIT_REG_RTN_DATANUM; + } + for (k = 0; k < n; k++) { + *reg_val++ = rte_le_to_cpu_32(*desc_data++); + + regs_num--; + if (regs_num == 0) + break; + } + } + + rte_free(desc); + return 0; +} + +static int +hns3_get_64_bit_regs(struct hns3_hw *hw, uint32_t regs_num, void *data) +{ +#define HNS3_64_BIT_REG_RTN_DATANUM 4 +#define HNS3_64_BIT_DESC_NODATA_LEN 1 + struct hns3_cmd_desc *desc; + uint64_t *reg_val = data; + uint64_t *desc_data; + int cmd_num; + int i, k, n; + int ret; + + if (regs_num == 0) + return 0; + + cmd_num = DIV_ROUND_UP(regs_num + HNS3_64_BIT_DESC_NODATA_LEN, + HNS3_64_BIT_REG_RTN_DATANUM); + desc = rte_zmalloc("hns3-64bit-regs", + sizeof(struct hns3_cmd_desc) * cmd_num, 0); + if (desc == NULL) { + hns3_err(hw, "Failed to allocate %zx bytes needed to " + "store 64bit regs", + sizeof(struct hns3_cmd_desc) * cmd_num); + return -ENOMEM; + } + + hns3_cmd_setup_basic_desc(&desc[0], HNS3_OPC_QUERY_64_BIT_REG, true); + ret = hns3_cmd_send(hw, desc, cmd_num); + if (ret) { + hns3_err(hw, "Query 64 bit register cmd failed, ret = %d", + ret); + rte_free(desc); + return ret; + } + + for (i = 0; i < cmd_num; i++) { + if (i == 0) { + desc_data = (uint64_t *)(&desc[i].data[0]); + n = HNS3_64_BIT_REG_RTN_DATANUM - + HNS3_64_BIT_DESC_NODATA_LEN; + } else { + desc_data = (uint64_t *)(&desc[i]); + n = HNS3_64_BIT_REG_RTN_DATANUM; + } + for (k = 0; k < n; k++) { + *reg_val++ = rte_le_to_cpu_64(*desc_data++); + + regs_num--; + if (!regs_num) + break; + } + } + + rte_free(desc); + return 0; +} + +static void +hns3_direct_access_regs(struct hns3_hw *hw, uint32_t *data) +{ + struct hns3_adapter *hns = HNS3_DEV_HW_TO_ADAPTER(hw); + uint32_t reg_offset; + int separator_num; + int reg_um; + int i, j; + + /* fetching per-PF registers values from PF PCIe register space */ + reg_um = sizeof(cmdq_reg_addrs) / sizeof(uint32_t); + separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE; + for (i = 0; i < reg_um; i++) + *data++ = hns3_read_dev(hw, cmdq_reg_addrs[i]); + for (i = 0; i < separator_num; i++) + *data++ = SEPARATOR_VALUE; + + if (hns->is_vf) + reg_um = sizeof(common_vf_reg_addrs) / sizeof(uint32_t); + else + reg_um = sizeof(common_reg_addrs) / sizeof(uint32_t); + separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE; + for (i = 0; i < reg_um; i++) + if (hns->is_vf) + *data++ = hns3_read_dev(hw, common_vf_reg_addrs[i]); + else + *data++ = hns3_read_dev(hw, common_reg_addrs[i]); + for (i = 0; i < separator_num; i++) + *data++ = SEPARATOR_VALUE; + + reg_um = sizeof(ring_reg_addrs) / sizeof(uint32_t); + separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE; + for (j = 0; j < hw->tqps_num; j++) { + reg_offset = HNS3_TQP_REG_OFFSET + HNS3_TQP_REG_SIZE * j; + for (i = 0; i < reg_um; i++) + *data++ = hns3_read_dev(hw, + ring_reg_addrs[i] + reg_offset); + for (i = 0; i < separator_num; i++) + *data++ = SEPARATOR_VALUE; + } + + reg_um = sizeof(tqp_intr_reg_addrs) / sizeof(uint32_t); + separator_num = MAX_SEPARATE_NUM - reg_um % REG_NUM_PER_LINE; + for (j = 0; j < hw->num_msi; j++) { + reg_offset = HNS3_TQP_INTR_REG_SIZE * j; + for (i = 0; i < reg_um; i++) + *data++ = hns3_read_dev(hw, + tqp_intr_reg_addrs[i] + + reg_offset); + for (i = 0; i < separator_num; i++) + *data++ = SEPARATOR_VALUE; + } +} + +int +hns3_get_regs(struct rte_eth_dev *eth_dev, struct rte_dev_reg_info *regs) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint32_t regs_num_32_bit; + uint32_t regs_num_64_bit; + uint32_t length; + uint32_t *data; + int ret; + + if (regs == NULL) { + hns3_err(hw, "the input parameter regs is NULL!"); + return -EINVAL; + } + + ret = hns3_get_regs_length(hw, &length); + if (ret) + return ret; + + data = regs->data; + if (data == NULL) { + regs->length = length; + regs->width = sizeof(uint32_t); + return 0; + } + + /* Only full register dump is supported */ + if (regs->length && regs->length != length) + return -ENOTSUP; + + /* fetching per-PF registers values from PF PCIe register space */ + hns3_direct_access_regs(hw, data); + + if (hns->is_vf) + return 0; + + ret = hns3_get_regs_num(hw, ®s_num_32_bit, ®s_num_64_bit); + if (ret) { + hns3_err(hw, "Get register number failed, ret = %d", ret); + return ret; + } + + /* fetching PF common registers values from firmware */ + ret = hns3_get_32_bit_regs(hw, regs_num_32_bit, data); + if (ret) { + hns3_err(hw, "Get 32 bit register failed, ret = %d", ret); + return ret; + } + + data += regs_num_32_bit; + ret = hns3_get_64_bit_regs(hw, regs_num_64_bit, data); + if (ret) + hns3_err(hw, "Get 64 bit register failed, ret = %d", ret); + + return ret; +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_regs.h b/src/spdk/dpdk/drivers/net/hns3/hns3_regs.h new file mode 100644 index 000000000..64bd6931b --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_regs.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_REGS_H_ +#define _HNS3_REGS_H_ + +/* bar registers for cmdq */ +#define HNS3_CMDQ_TX_ADDR_L_REG 0x27000 +#define HNS3_CMDQ_TX_ADDR_H_REG 0x27004 +#define HNS3_CMDQ_TX_DEPTH_REG 0x27008 +#define HNS3_CMDQ_TX_TAIL_REG 0x27010 +#define HNS3_CMDQ_TX_HEAD_REG 0x27014 +#define HNS3_CMDQ_RX_ADDR_L_REG 0x27018 +#define HNS3_CMDQ_RX_ADDR_H_REG 0x2701c +#define HNS3_CMDQ_RX_DEPTH_REG 0x27020 +#define HNS3_CMDQ_RX_TAIL_REG 0x27024 +#define HNS3_CMDQ_RX_HEAD_REG 0x27028 +#define HNS3_CMDQ_INTR_STS_REG 0x27104 +#define HNS3_CMDQ_INTR_EN_REG 0x27108 +#define HNS3_CMDQ_INTR_GEN_REG 0x2710C + +/* Vector0 interrupt CMDQ event source register(RW) */ +#define HNS3_VECTOR0_CMDQ_SRC_REG 0x27100 +/* Vector0 interrupt CMDQ event status register(RO) */ +#define HNS3_VECTOR0_CMDQ_STAT_REG 0x27104 + +#define HNS3_VECTOR0_OTHER_INT_STS_REG 0x20800 + +#define HNS3_MISC_VECTOR_REG_BASE 0x20400 +#define HNS3_VECTOR0_OTER_EN_REG 0x20600 +#define HNS3_MISC_RESET_STS_REG 0x20700 +#define HNS3_GLOBAL_RESET_REG 0x20A00 +#define HNS3_FUN_RST_ING 0x20C00 +#define HNS3_GRO_EN_REG 0x28000 + +/* Vector0 register bits for reset */ +#define HNS3_VECTOR0_FUNCRESET_INT_B 0 +#define HNS3_VECTOR0_GLOBALRESET_INT_B 5 +#define HNS3_VECTOR0_CORERESET_INT_B 6 +#define HNS3_VECTOR0_IMPRESET_INT_B 7 + +/* CMDQ register bits for RX event(=MBX event) */ +#define HNS3_VECTOR0_RX_CMDQ_INT_B 1 +#define HNS3_VECTOR0_REG_MSIX_MASK 0x1FF00 +/* RST register bits for RESET event */ +#define HNS3_VECTOR0_RST_INT_B 2 + +#define HNS3_VF_RST_ING 0x07008 +#define HNS3_VF_RST_ING_BIT BIT(16) + +/* bar registers for rcb */ +#define HNS3_RING_RX_BASEADDR_L_REG 0x00000 +#define HNS3_RING_RX_BASEADDR_H_REG 0x00004 +#define HNS3_RING_RX_BD_NUM_REG 0x00008 +#define HNS3_RING_RX_BD_LEN_REG 0x0000C +#define HNS3_RING_RX_MERGE_EN_REG 0x00014 +#define HNS3_RING_RX_TAIL_REG 0x00018 +#define HNS3_RING_RX_HEAD_REG 0x0001C +#define HNS3_RING_RX_FBDNUM_REG 0x00020 +#define HNS3_RING_RX_OFFSET_REG 0x00024 +#define HNS3_RING_RX_FBD_OFFSET_REG 0x00028 +#define HNS3_RING_RX_PKTNUM_RECORD_REG 0x0002C +#define HNS3_RING_RX_STASH_REG 0x00030 +#define HNS3_RING_RX_BD_ERR_REG 0x00034 + +#define HNS3_RING_TX_BASEADDR_L_REG 0x00040 +#define HNS3_RING_TX_BASEADDR_H_REG 0x00044 +#define HNS3_RING_TX_BD_NUM_REG 0x00048 +#define HNS3_RING_TX_PRIORITY_REG 0x0004C +#define HNS3_RING_TX_TC_REG 0x00050 +#define HNS3_RING_TX_MERGE_EN_REG 0x00054 +#define HNS3_RING_TX_TAIL_REG 0x00058 +#define HNS3_RING_TX_HEAD_REG 0x0005C +#define HNS3_RING_TX_FBDNUM_REG 0x00060 +#define HNS3_RING_TX_OFFSET_REG 0x00064 +#define HNS3_RING_TX_EBD_NUM_REG 0x00068 +#define HNS3_RING_TX_PKTNUM_RECORD_REG 0x0006C +#define HNS3_RING_TX_EBD_OFFSET_REG 0x00070 +#define HNS3_RING_TX_BD_ERR_REG 0x00074 + +#define HNS3_RING_EN_REG 0x00090 + +#define HNS3_RING_EN_B 0 + +#define HNS3_TQP_REG_OFFSET 0x80000 +#define HNS3_TQP_REG_SIZE 0x200 + +/* bar registers for tqp interrupt */ +#define HNS3_TQP_INTR_CTRL_REG 0x20000 +#define HNS3_TQP_INTR_GL0_REG 0x20100 +#define HNS3_TQP_INTR_GL1_REG 0x20200 +#define HNS3_TQP_INTR_GL2_REG 0x20300 +#define HNS3_TQP_INTR_RL_REG 0x20900 + +#define HNS3_TQP_INTR_REG_SIZE 4 +#define HNS3_TQP_INTR_GL_MAX 0x1FE0 +#define HNS3_TQP_INTR_GL_DEFAULT 20 +#define HNS3_TQP_INTR_RL_MAX 0xEC +#define HNS3_TQP_INTR_RL_ENABLE_MASK 0x40 +#define HNS3_TQP_INTR_RL_DEFAULT 0 + +/* gl_usec convert to hardware count, as writing each 1 represents 2us */ +#define HNS3_GL_USEC_TO_REG(gl_usec) ((gl_usec) >> 1) +/* rl_usec convert to hardware count, as writing each 1 represents 4us */ +#define HNS3_RL_USEC_TO_REG(rl_usec) ((rl_usec) >> 2) + +int hns3_get_regs(struct rte_eth_dev *eth_dev, struct rte_dev_reg_info *regs); +#endif /* _HNS3_REGS_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_rss.c b/src/spdk/dpdk/drivers/net/hns3/hns3_rss.c new file mode 100644 index 000000000..a6cab29c9 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_rss.c @@ -0,0 +1,603 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdbool.h> +#include <rte_ethdev.h> +#include <rte_io.h> +#include <rte_malloc.h> +#include <rte_memcpy.h> +#include <rte_spinlock.h> + +#include "hns3_ethdev.h" +#include "hns3_logs.h" + +/* + * The hash key used for rss initialization. + */ +static const uint8_t hns3_hash_key[] = { + 0x6D, 0x5A, 0x56, 0xDA, 0x25, 0x5B, 0x0E, 0xC2, + 0x41, 0x67, 0x25, 0x3D, 0x43, 0xA3, 0x8F, 0xB0, + 0xD0, 0xCA, 0x2B, 0xCB, 0xAE, 0x7B, 0x30, 0xB4, + 0x77, 0xCB, 0x2D, 0xA3, 0x80, 0x30, 0xF2, 0x0C, + 0x6A, 0x42, 0xB7, 0x3B, 0xBE, 0xAC, 0x01, 0xFA +}; + +/* + * rss_generic_config command function, opcode:0x0D01. + * Used to set algorithm, key_offset and hash key of rss. + */ +int +hns3_set_rss_algo_key(struct hns3_hw *hw, uint8_t hash_algo, const uint8_t *key) +{ +#define HNS3_KEY_OFFSET_MAX 3 +#define HNS3_SET_HASH_KEY_BYTE_FOUR 2 + + struct hns3_rss_generic_config_cmd *req; + struct hns3_cmd_desc desc; + uint32_t key_offset, key_size; + const uint8_t *key_cur; + uint8_t cur_offset; + int ret; + + req = (struct hns3_rss_generic_config_cmd *)desc.data; + + /* + * key_offset=0, hash key byte0~15 is set to hardware. + * key_offset=1, hash key byte16~31 is set to hardware. + * key_offset=2, hash key byte32~39 is set to hardware. + */ + for (key_offset = 0; key_offset < HNS3_KEY_OFFSET_MAX; key_offset++) { + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RSS_GENERIC_CONFIG, + false); + + req->hash_config |= (hash_algo & HNS3_RSS_HASH_ALGO_MASK); + req->hash_config |= (key_offset << HNS3_RSS_HASH_KEY_OFFSET_B); + + if (key_offset == HNS3_SET_HASH_KEY_BYTE_FOUR) + key_size = HNS3_RSS_KEY_SIZE - HNS3_RSS_HASH_KEY_NUM * + HNS3_SET_HASH_KEY_BYTE_FOUR; + else + key_size = HNS3_RSS_HASH_KEY_NUM; + + cur_offset = key_offset * HNS3_RSS_HASH_KEY_NUM; + key_cur = key + cur_offset; + memcpy(req->hash_key, key_cur, key_size); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Configure RSS algo key failed %d", ret); + return ret; + } + } + /* Update the shadow RSS key with user specified */ + memcpy(hw->rss_info.key, key, HNS3_RSS_KEY_SIZE); + return 0; +} + +/* + * Used to configure the tuple selection for RSS hash input. + */ +static int +hns3_set_rss_input_tuple(struct hns3_hw *hw) +{ + struct hns3_rss_conf *rss_config = &hw->rss_info; + struct hns3_rss_input_tuple_cmd *req; + struct hns3_cmd_desc desc_tuple; + int ret; + + hns3_cmd_setup_basic_desc(&desc_tuple, HNS3_OPC_RSS_INPUT_TUPLE, false); + + req = (struct hns3_rss_input_tuple_cmd *)desc_tuple.data; + + req->ipv4_tcp_en = rss_config->rss_tuple_sets.ipv4_tcp_en; + req->ipv4_udp_en = rss_config->rss_tuple_sets.ipv4_udp_en; + req->ipv4_sctp_en = rss_config->rss_tuple_sets.ipv4_sctp_en; + req->ipv4_fragment_en = rss_config->rss_tuple_sets.ipv4_fragment_en; + req->ipv6_tcp_en = rss_config->rss_tuple_sets.ipv6_tcp_en; + req->ipv6_udp_en = rss_config->rss_tuple_sets.ipv6_udp_en; + req->ipv6_sctp_en = rss_config->rss_tuple_sets.ipv6_sctp_en; + req->ipv6_fragment_en = rss_config->rss_tuple_sets.ipv6_fragment_en; + + ret = hns3_cmd_send(hw, &desc_tuple, 1); + if (ret) + hns3_err(hw, "Configure RSS input tuple mode failed %d", ret); + + return ret; +} + +/* + * rss_indirection_table command function, opcode:0x0D07. + * Used to configure the indirection table of rss. + */ +int +hns3_set_rss_indir_table(struct hns3_hw *hw, uint8_t *indir, uint16_t size) +{ + struct hns3_rss_indirection_table_cmd *req; + struct hns3_cmd_desc desc; + int ret, i, j, num; + + req = (struct hns3_rss_indirection_table_cmd *)desc.data; + + for (i = 0; i < size / HNS3_RSS_CFG_TBL_SIZE; i++) { + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RSS_INDIR_TABLE, + false); + req->start_table_index = + rte_cpu_to_le_16(i * HNS3_RSS_CFG_TBL_SIZE); + req->rss_set_bitmap = rte_cpu_to_le_16(HNS3_RSS_SET_BITMAP_MSK); + for (j = 0; j < HNS3_RSS_CFG_TBL_SIZE; j++) { + num = i * HNS3_RSS_CFG_TBL_SIZE + j; + req->rss_result[j] = indir[num]; + } + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, + "Sets RSS indirection table failed %d size %u", + ret, size); + return ret; + } + } + + /* Update redirection table of hw */ + memcpy(hw->rss_info.rss_indirection_tbl, indir, HNS3_RSS_IND_TBL_SIZE); + + return 0; +} + +int +hns3_rss_reset_indir_table(struct hns3_hw *hw) +{ + uint8_t *lut; + int ret; + + lut = rte_zmalloc("hns3_rss_lut", HNS3_RSS_IND_TBL_SIZE, 0); + if (lut == NULL) { + hns3_err(hw, "No hns3_rss_lut memory can be allocated"); + return -ENOMEM; + } + + ret = hns3_set_rss_indir_table(hw, lut, HNS3_RSS_IND_TBL_SIZE); + if (ret) + hns3_err(hw, "RSS uninit indir table failed: %d", ret); + rte_free(lut); + + return ret; +} + +int +hns3_set_rss_tuple_by_rss_hf(struct hns3_hw *hw, + struct hns3_rss_tuple_cfg *tuple, uint64_t rss_hf) +{ + struct hns3_rss_input_tuple_cmd *req; + struct hns3_cmd_desc desc; + uint32_t i; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RSS_INPUT_TUPLE, false); + + req = (struct hns3_rss_input_tuple_cmd *)desc.data; + + /* Enable ipv4 or ipv6 tuple by flow type */ + for (i = 0; i < RTE_ETH_FLOW_MAX; i++) { + switch (rss_hf & (1ULL << i)) { + case ETH_RSS_NONFRAG_IPV4_TCP: + req->ipv4_tcp_en = HNS3_RSS_INPUT_TUPLE_OTHER; + break; + case ETH_RSS_NONFRAG_IPV4_UDP: + req->ipv4_udp_en = HNS3_RSS_INPUT_TUPLE_OTHER; + break; + case ETH_RSS_NONFRAG_IPV4_SCTP: + req->ipv4_sctp_en = HNS3_RSS_INPUT_TUPLE_SCTP; + break; + case ETH_RSS_FRAG_IPV4: + req->ipv4_fragment_en |= HNS3_IP_FRAG_BIT_MASK; + break; + case ETH_RSS_NONFRAG_IPV4_OTHER: + req->ipv4_fragment_en |= HNS3_IP_OTHER_BIT_MASK; + break; + case ETH_RSS_NONFRAG_IPV6_TCP: + req->ipv6_tcp_en = HNS3_RSS_INPUT_TUPLE_OTHER; + break; + case ETH_RSS_NONFRAG_IPV6_UDP: + req->ipv6_udp_en = HNS3_RSS_INPUT_TUPLE_OTHER; + break; + case ETH_RSS_NONFRAG_IPV6_SCTP: + req->ipv6_sctp_en = HNS3_RSS_INPUT_TUPLE_SCTP; + break; + case ETH_RSS_FRAG_IPV6: + req->ipv6_fragment_en |= HNS3_IP_FRAG_BIT_MASK; + break; + case ETH_RSS_NONFRAG_IPV6_OTHER: + req->ipv6_fragment_en |= HNS3_IP_OTHER_BIT_MASK; + break; + default: + /* + * rss_hf doesn't include unsupported flow types + * because the API framework has checked it, and + * this branch will never go unless rss_hf is zero. + */ + break; + } + } + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Update RSS flow types tuples failed %d", ret); + return ret; + } + + tuple->ipv4_tcp_en = req->ipv4_tcp_en; + tuple->ipv4_udp_en = req->ipv4_udp_en; + tuple->ipv4_sctp_en = req->ipv4_sctp_en; + tuple->ipv4_fragment_en = req->ipv4_fragment_en; + tuple->ipv6_tcp_en = req->ipv6_tcp_en; + tuple->ipv6_udp_en = req->ipv6_udp_en; + tuple->ipv6_sctp_en = req->ipv6_sctp_en; + tuple->ipv6_fragment_en = req->ipv6_fragment_en; + + return 0; +} + +/* + * Configure RSS hash protocols and hash key. + * @param dev + * Pointer to Ethernet device. + * @praram rss_conf + * The configuration select of rss key size and tuple flow_types. + * @return + * 0 on success, a negative errno value otherwise is set. + */ +int +hns3_dev_rss_hash_update(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_tuple_cfg *tuple = &hw->rss_info.rss_tuple_sets; + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint8_t key_len = rss_conf->rss_key_len; + uint8_t algo; + uint64_t rss_hf = rss_conf->rss_hf; + uint8_t *key = rss_conf->rss_key; + int ret; + + if (hw->rss_dis_flag) + return -EINVAL; + + rte_spinlock_lock(&hw->lock); + ret = hns3_set_rss_tuple_by_rss_hf(hw, tuple, rss_hf); + if (ret) + goto conf_err; + + if (rss_cfg->conf.types && rss_hf == 0) { + /* Disable RSS, reset indirection table by local variable */ + ret = hns3_rss_reset_indir_table(hw); + if (ret) + goto conf_err; + } else if (rss_hf && rss_cfg->conf.types == 0) { + /* Enable RSS, restore indirection table by hw's config */ + ret = hns3_set_rss_indir_table(hw, rss_cfg->rss_indirection_tbl, + HNS3_RSS_IND_TBL_SIZE); + if (ret) + goto conf_err; + } + + /* Update supported flow types when set tuple success */ + rss_cfg->conf.types = rss_hf; + + if (key) { + if (key_len != HNS3_RSS_KEY_SIZE) { + hns3_err(hw, "The hash key len(%u) is invalid", + key_len); + ret = -EINVAL; + goto conf_err; + } + algo = rss_cfg->conf.func == RTE_ETH_HASH_FUNCTION_SIMPLE_XOR ? + HNS3_RSS_HASH_ALGO_SIMPLE : HNS3_RSS_HASH_ALGO_TOEPLITZ; + ret = hns3_set_rss_algo_key(hw, algo, key); + if (ret) + goto conf_err; + } + rte_spinlock_unlock(&hw->lock); + + return 0; + +conf_err: + rte_spinlock_unlock(&hw->lock); + return ret; +} + +/* + * Get rss key and rss_hf types set of RSS hash configuration. + * @param dev + * Pointer to Ethernet device. + * @praram rss_conf + * The buffer to get rss key size and tuple types. + * @return + * 0 on success. + */ +int +hns3_dev_rss_hash_conf_get(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + + rte_spinlock_lock(&hw->lock); + rss_conf->rss_hf = rss_cfg->conf.types; + + /* Get the RSS Key required by the user */ + if (rss_conf->rss_key && rss_conf->rss_key_len >= HNS3_RSS_KEY_SIZE) { + memcpy(rss_conf->rss_key, rss_cfg->key, HNS3_RSS_KEY_SIZE); + rss_conf->rss_key_len = HNS3_RSS_KEY_SIZE; + } + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +/* + * Update rss redirection table of RSS. + * @param dev + * Pointer to Ethernet device. + * @praram reta_conf + * Pointer to the configuration select of mask and redirection tables. + * @param reta_size + * Redirection table size. + * @return + * 0 on success, a negative errno value otherwise is set. + */ +int +hns3_dev_rss_reta_update(struct rte_eth_dev *dev, + struct rte_eth_rss_reta_entry64 *reta_conf, + uint16_t reta_size) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint16_t i, indir_size = HNS3_RSS_IND_TBL_SIZE; /* Table size is 512 */ + uint8_t indirection_tbl[HNS3_RSS_IND_TBL_SIZE]; + uint16_t idx, shift, allow_rss_queues; + int ret; + + if (reta_size != indir_size || reta_size > ETH_RSS_RETA_SIZE_512) { + hns3_err(hw, "The size of hash lookup table configured (%u)" + "doesn't match the number hardware can supported" + "(%u)", reta_size, indir_size); + return -EINVAL; + } + rte_spinlock_lock(&hw->lock); + memcpy(indirection_tbl, rss_cfg->rss_indirection_tbl, + HNS3_RSS_IND_TBL_SIZE); + allow_rss_queues = RTE_MIN(dev->data->nb_rx_queues, hw->rss_size_max); + for (i = 0; i < reta_size; i++) { + idx = i / RTE_RETA_GROUP_SIZE; + shift = i % RTE_RETA_GROUP_SIZE; + if (reta_conf[idx].reta[shift] >= allow_rss_queues) { + rte_spinlock_unlock(&hw->lock); + hns3_err(hw, "Invalid queue id(%u) to be set in " + "redirection table, max number of rss " + "queues: %u", reta_conf[idx].reta[shift], + allow_rss_queues); + return -EINVAL; + } + + if (reta_conf[idx].mask & (1ULL << shift)) + indirection_tbl[i] = reta_conf[idx].reta[shift]; + } + + ret = hns3_set_rss_indir_table(hw, indirection_tbl, + HNS3_RSS_IND_TBL_SIZE); + + rte_spinlock_unlock(&hw->lock); + return ret; +} + +/* + * Get rss redirection table of RSS hash configuration. + * @param dev + * Pointer to Ethernet device. + * @praram reta_conf + * Pointer to the configuration select of mask and redirection tables. + * @param reta_size + * Redirection table size. + * @return + * 0 on success, a negative errno value otherwise is set. + */ +int +hns3_dev_rss_reta_query(struct rte_eth_dev *dev, + struct rte_eth_rss_reta_entry64 *reta_conf, + uint16_t reta_size) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint16_t i, indir_size = HNS3_RSS_IND_TBL_SIZE; /* Table size is 512 */ + uint16_t idx, shift; + + if (reta_size != indir_size || reta_size > ETH_RSS_RETA_SIZE_512) { + hns3_err(hw, "The size of hash lookup table configured (%u)" + " doesn't match the number hardware can supported" + "(%u)", reta_size, indir_size); + return -EINVAL; + } + rte_spinlock_lock(&hw->lock); + for (i = 0; i < reta_size; i++) { + idx = i / RTE_RETA_GROUP_SIZE; + shift = i % RTE_RETA_GROUP_SIZE; + if (reta_conf[idx].mask & (1ULL << shift)) + reta_conf[idx].reta[shift] = + rss_cfg->rss_indirection_tbl[i]; + } + rte_spinlock_unlock(&hw->lock); + return 0; +} + +/* + * Used to configure the tc_size and tc_offset. + */ +static int +hns3_set_rss_tc_mode(struct hns3_hw *hw) +{ + uint16_t rss_size = hw->alloc_rss_size; + struct hns3_rss_tc_mode_cmd *req; + uint16_t tc_offset[HNS3_MAX_TC_NUM]; + uint8_t tc_valid[HNS3_MAX_TC_NUM]; + uint16_t tc_size[HNS3_MAX_TC_NUM]; + struct hns3_cmd_desc desc; + uint16_t roundup_size; + uint16_t i; + int ret; + + req = (struct hns3_rss_tc_mode_cmd *)desc.data; + + roundup_size = roundup_pow_of_two(rss_size); + roundup_size = ilog2(roundup_size); + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + tc_valid[i] = !!(hw->hw_tc_map & BIT(i)); + tc_size[i] = roundup_size; + tc_offset[i] = rss_size * i; + } + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RSS_TC_MODE, false); + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + uint16_t mode = 0; + + hns3_set_bit(mode, HNS3_RSS_TC_VALID_B, (tc_valid[i] & 0x1)); + hns3_set_field(mode, HNS3_RSS_TC_SIZE_M, HNS3_RSS_TC_SIZE_S, + tc_size[i]); + hns3_set_field(mode, HNS3_RSS_TC_OFFSET_M, HNS3_RSS_TC_OFFSET_S, + tc_offset[i]); + + req->rss_tc_mode[i] = rte_cpu_to_le_16(mode); + } + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Sets rss tc mode failed %d", ret); + + return ret; +} + +static void +hns3_rss_tuple_uninit(struct hns3_hw *hw) +{ + struct hns3_rss_input_tuple_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RSS_INPUT_TUPLE, false); + + req = (struct hns3_rss_input_tuple_cmd *)desc.data; + + memset(req, 0, sizeof(struct hns3_rss_tuple_cfg)); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "RSS uninit tuple failed %d", ret); + return; + } +} + +/* + * Set the default rss configuration in the init of driver. + */ +void +hns3_set_default_rss_args(struct hns3_hw *hw) +{ + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint16_t queue_num = hw->alloc_rss_size; + int i; + + /* Default hash algorithm */ + rss_cfg->conf.func = RTE_ETH_HASH_FUNCTION_TOEPLITZ; + + /* Default RSS key */ + memcpy(rss_cfg->key, hns3_hash_key, HNS3_RSS_KEY_SIZE); + + /* Initialize RSS indirection table */ + for (i = 0; i < HNS3_RSS_IND_TBL_SIZE; i++) + rss_cfg->rss_indirection_tbl[i] = i % queue_num; +} + +/* + * RSS initialization for hns3 pmd driver. + */ +int +hns3_config_rss(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_rss_conf *rss_cfg = &hw->rss_info; + uint8_t hash_algo = + (hw->rss_info.conf.func == RTE_ETH_HASH_FUNCTION_TOEPLITZ ? + HNS3_RSS_HASH_ALGO_TOEPLITZ : HNS3_RSS_HASH_ALGO_SIMPLE); + uint8_t *hash_key = rss_cfg->key; + int ret, ret1; + + enum rte_eth_rx_mq_mode mq_mode = hw->data->dev_conf.rxmode.mq_mode; + + /* When RSS is off, redirect the packet queue 0 */ + if (((uint32_t)mq_mode & ETH_MQ_RX_RSS_FLAG) == 0) + hns3_rss_uninit(hns); + + /* Configure RSS hash algorithm and hash key offset */ + ret = hns3_set_rss_algo_key(hw, hash_algo, hash_key); + if (ret) + return ret; + + /* Configure the tuple selection for RSS hash input */ + ret = hns3_set_rss_input_tuple(hw); + if (ret) + return ret; + + /* + * When RSS is off, it doesn't need to configure rss redirection table + * to hardware. + */ + if (((uint32_t)mq_mode & ETH_MQ_RX_RSS_FLAG)) { + ret = hns3_set_rss_indir_table(hw, rss_cfg->rss_indirection_tbl, + HNS3_RSS_IND_TBL_SIZE); + if (ret) + goto rss_tuple_uninit; + } + + ret = hns3_set_rss_tc_mode(hw); + if (ret) + goto rss_indir_table_uninit; + + return ret; + +rss_indir_table_uninit: + if (((uint32_t)mq_mode & ETH_MQ_RX_RSS_FLAG)) { + ret1 = hns3_rss_reset_indir_table(hw); + if (ret1 != 0) + return ret; + } + +rss_tuple_uninit: + hns3_rss_tuple_uninit(hw); + + /* Disable RSS */ + hw->rss_info.conf.types = 0; + + return ret; +} + +/* + * RSS uninitialization for hns3 pmd driver. + */ +void +hns3_rss_uninit(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + hns3_rss_tuple_uninit(hw); + ret = hns3_rss_reset_indir_table(hw); + if (ret != 0) + return; + + /* Disable RSS */ + hw->rss_info.conf.types = 0; +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_rss.h b/src/spdk/dpdk/drivers/net/hns3/hns3_rss.h new file mode 100644 index 000000000..3c7905132 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_rss.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_RSS_H_ +#define _HNS3_RSS_H_ +#include <rte_ethdev.h> +#include <rte_flow.h> + +#define HNS3_ETH_RSS_SUPPORT ( \ + ETH_RSS_FRAG_IPV4 | \ + ETH_RSS_NONFRAG_IPV4_TCP | \ + ETH_RSS_NONFRAG_IPV4_UDP | \ + ETH_RSS_NONFRAG_IPV4_SCTP | \ + ETH_RSS_NONFRAG_IPV4_OTHER | \ + ETH_RSS_FRAG_IPV6 | \ + ETH_RSS_NONFRAG_IPV6_TCP | \ + ETH_RSS_NONFRAG_IPV6_UDP | \ + ETH_RSS_NONFRAG_IPV6_SCTP | \ + ETH_RSS_NONFRAG_IPV6_OTHER) + +#define HNS3_RSS_IND_TBL_SIZE 512 /* The size of hash lookup table */ +#define HNS3_RSS_KEY_SIZE 40 +#define HNS3_RSS_CFG_TBL_NUM \ + (HNS3_RSS_IND_TBL_SIZE / HNS3_RSS_CFG_TBL_SIZE) +#define HNS3_RSS_SET_BITMAP_MSK 0xffff + +#define HNS3_RSS_HASH_ALGO_TOEPLITZ 0 +#define HNS3_RSS_HASH_ALGO_SIMPLE 1 +#define HNS3_RSS_HASH_ALGO_MASK 0xf + +#define HNS3_RSS_INPUT_TUPLE_OTHER GENMASK(3, 0) +#define HNS3_RSS_INPUT_TUPLE_SCTP GENMASK(4, 0) +#define HNS3_IP_FRAG_BIT_MASK GENMASK(3, 2) +#define HNS3_IP_OTHER_BIT_MASK GENMASK(1, 0) + +struct hns3_rss_tuple_cfg { + uint8_t ipv4_tcp_en; /* Bit8.0~8.3 */ + uint8_t ipv4_udp_en; /* Bit9.0~9.3 */ + uint8_t ipv4_sctp_en; /* Bit10.0~10.4 */ + uint8_t ipv4_fragment_en; /* Bit11.0~11.3 */ + uint8_t ipv6_tcp_en; /* Bit12.0~12.3 */ + uint8_t ipv6_udp_en; /* Bit13.0~13.3 */ + uint8_t ipv6_sctp_en; /* Bit14.0~14.4 */ + uint8_t ipv6_fragment_en; /* Bit15.0~15.3 */ +}; + +#define HNS3_RSS_QUEUES_BUFFER_NUM 64 /* Same as the Max rx/tx queue num */ +struct hns3_rss_conf { + /* RSS parameters :algorithm, flow_types, key, queue */ + struct rte_flow_action_rss conf; + uint8_t key[HNS3_RSS_KEY_SIZE]; /* Hash key */ + struct hns3_rss_tuple_cfg rss_tuple_sets; + uint8_t rss_indirection_tbl[HNS3_RSS_IND_TBL_SIZE]; /* Shadow table */ + uint16_t queue[HNS3_RSS_QUEUES_BUFFER_NUM]; /* Queues indices to use */ +}; + +#ifndef ilog2 +static inline int rss_ilog2(uint32_t x) +{ + int log = 0; + x >>= 1; + + while (x) { + log++; + x >>= 1; + } + return log; +} +#define ilog2(x) rss_ilog2(x) +#endif + +static inline uint32_t fls(uint32_t x) +{ + uint32_t position; + uint32_t i; + + if (x == 0) + return 0; + + for (i = (x >> 1), position = 0; i != 0; ++position) + i >>= 1; + + return position + 1; +} + +static inline uint32_t roundup_pow_of_two(uint32_t x) +{ + return 1UL << fls(x - 1); +} + +struct hns3_adapter; + +int hns3_dev_rss_hash_update(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf); +int hns3_dev_rss_hash_conf_get(struct rte_eth_dev *dev, + struct rte_eth_rss_conf *rss_conf); +int hns3_dev_rss_reta_update(struct rte_eth_dev *dev, + struct rte_eth_rss_reta_entry64 *reta_conf, + uint16_t reta_size); +int hns3_dev_rss_reta_query(struct rte_eth_dev *dev, + struct rte_eth_rss_reta_entry64 *reta_conf, + uint16_t reta_size); +void hns3_set_default_rss_args(struct hns3_hw *hw); +int hns3_set_rss_indir_table(struct hns3_hw *hw, uint8_t *indir, uint16_t size); +int hns3_rss_reset_indir_table(struct hns3_hw *hw); +int hns3_config_rss(struct hns3_adapter *hns); +void hns3_rss_uninit(struct hns3_adapter *hns); +int hns3_set_rss_tuple_by_rss_hf(struct hns3_hw *hw, + struct hns3_rss_tuple_cfg *tuple, + uint64_t rss_hf); +int hns3_set_rss_algo_key(struct hns3_hw *hw, uint8_t hash_algo, + const uint8_t *key); +int hns3_restore_rss_filter(struct rte_eth_dev *dev); + +#endif /* _HNS3_RSS_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_rxtx.c b/src/spdk/dpdk/drivers/net/hns3/hns3_rxtx.c new file mode 100644 index 000000000..8b3ced116 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_rxtx.c @@ -0,0 +1,2515 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <rte_bus_pci.h> +#include <rte_byteorder.h> +#include <rte_common.h> +#include <rte_cycles.h> +#include <rte_dev.h> +#include <rte_eal.h> +#include <rte_ether.h> +#include <rte_vxlan.h> +#include <rte_ethdev_driver.h> +#include <rte_io.h> +#include <rte_ip.h> +#include <rte_gre.h> +#include <rte_net.h> +#include <rte_malloc.h> +#include <rte_pci.h> + +#include "hns3_ethdev.h" +#include "hns3_rxtx.h" +#include "hns3_regs.h" +#include "hns3_logs.h" + +#define HNS3_CFG_DESC_NUM(num) ((num) / 8 - 1) +#define DEFAULT_RX_FREE_THRESH 32 + +static void +hns3_rx_queue_release_mbufs(struct hns3_rx_queue *rxq) +{ + uint16_t i; + + /* Note: Fake rx queue will not enter here */ + if (rxq->sw_ring) { + for (i = 0; i < rxq->nb_rx_desc; i++) { + if (rxq->sw_ring[i].mbuf) { + rte_pktmbuf_free_seg(rxq->sw_ring[i].mbuf); + rxq->sw_ring[i].mbuf = NULL; + } + } + } +} + +static void +hns3_tx_queue_release_mbufs(struct hns3_tx_queue *txq) +{ + uint16_t i; + + /* Note: Fake rx queue will not enter here */ + if (txq->sw_ring) { + for (i = 0; i < txq->nb_tx_desc; i++) { + if (txq->sw_ring[i].mbuf) { + rte_pktmbuf_free_seg(txq->sw_ring[i].mbuf); + txq->sw_ring[i].mbuf = NULL; + } + } + } +} + +static void +hns3_rx_queue_release(void *queue) +{ + struct hns3_rx_queue *rxq = queue; + if (rxq) { + hns3_rx_queue_release_mbufs(rxq); + if (rxq->mz) + rte_memzone_free(rxq->mz); + if (rxq->sw_ring) + rte_free(rxq->sw_ring); + rte_free(rxq); + } +} + +static void +hns3_tx_queue_release(void *queue) +{ + struct hns3_tx_queue *txq = queue; + if (txq) { + hns3_tx_queue_release_mbufs(txq); + if (txq->mz) + rte_memzone_free(txq->mz); + if (txq->sw_ring) + rte_free(txq->sw_ring); + rte_free(txq); + } +} + +void +hns3_dev_rx_queue_release(void *queue) +{ + struct hns3_rx_queue *rxq = queue; + struct hns3_adapter *hns; + + if (rxq == NULL) + return; + + hns = rxq->hns; + rte_spinlock_lock(&hns->hw.lock); + hns3_rx_queue_release(queue); + rte_spinlock_unlock(&hns->hw.lock); +} + +void +hns3_dev_tx_queue_release(void *queue) +{ + struct hns3_tx_queue *txq = queue; + struct hns3_adapter *hns; + + if (txq == NULL) + return; + + hns = txq->hns; + rte_spinlock_lock(&hns->hw.lock); + hns3_tx_queue_release(queue); + rte_spinlock_unlock(&hns->hw.lock); +} + +static void +hns3_fake_rx_queue_release(struct hns3_rx_queue *queue) +{ + struct hns3_rx_queue *rxq = queue; + struct hns3_adapter *hns; + struct hns3_hw *hw; + uint16_t idx; + + if (rxq == NULL) + return; + + hns = rxq->hns; + hw = &hns->hw; + idx = rxq->queue_id; + if (hw->fkq_data.rx_queues[idx]) { + hns3_rx_queue_release(hw->fkq_data.rx_queues[idx]); + hw->fkq_data.rx_queues[idx] = NULL; + } + + /* free fake rx queue arrays */ + if (idx == (hw->fkq_data.nb_fake_rx_queues - 1)) { + hw->fkq_data.nb_fake_rx_queues = 0; + rte_free(hw->fkq_data.rx_queues); + hw->fkq_data.rx_queues = NULL; + } +} + +static void +hns3_fake_tx_queue_release(struct hns3_tx_queue *queue) +{ + struct hns3_tx_queue *txq = queue; + struct hns3_adapter *hns; + struct hns3_hw *hw; + uint16_t idx; + + if (txq == NULL) + return; + + hns = txq->hns; + hw = &hns->hw; + idx = txq->queue_id; + if (hw->fkq_data.tx_queues[idx]) { + hns3_tx_queue_release(hw->fkq_data.tx_queues[idx]); + hw->fkq_data.tx_queues[idx] = NULL; + } + + /* free fake tx queue arrays */ + if (idx == (hw->fkq_data.nb_fake_tx_queues - 1)) { + hw->fkq_data.nb_fake_tx_queues = 0; + rte_free(hw->fkq_data.tx_queues); + hw->fkq_data.tx_queues = NULL; + } +} + +static void +hns3_free_rx_queues(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_fake_queue_data *fkq_data; + struct hns3_hw *hw = &hns->hw; + uint16_t nb_rx_q; + uint16_t i; + + nb_rx_q = hw->data->nb_rx_queues; + for (i = 0; i < nb_rx_q; i++) { + if (dev->data->rx_queues[i]) { + hns3_rx_queue_release(dev->data->rx_queues[i]); + dev->data->rx_queues[i] = NULL; + } + } + + /* Free fake Rx queues */ + fkq_data = &hw->fkq_data; + for (i = 0; i < fkq_data->nb_fake_rx_queues; i++) { + if (fkq_data->rx_queues[i]) + hns3_fake_rx_queue_release(fkq_data->rx_queues[i]); + } +} + +static void +hns3_free_tx_queues(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_fake_queue_data *fkq_data; + struct hns3_hw *hw = &hns->hw; + uint16_t nb_tx_q; + uint16_t i; + + nb_tx_q = hw->data->nb_tx_queues; + for (i = 0; i < nb_tx_q; i++) { + if (dev->data->tx_queues[i]) { + hns3_tx_queue_release(dev->data->tx_queues[i]); + dev->data->tx_queues[i] = NULL; + } + } + + /* Free fake Tx queues */ + fkq_data = &hw->fkq_data; + for (i = 0; i < fkq_data->nb_fake_tx_queues; i++) { + if (fkq_data->tx_queues[i]) + hns3_fake_tx_queue_release(fkq_data->tx_queues[i]); + } +} + +void +hns3_free_all_queues(struct rte_eth_dev *dev) +{ + hns3_free_rx_queues(dev); + hns3_free_tx_queues(dev); +} + +static int +hns3_alloc_rx_queue_mbufs(struct hns3_hw *hw, struct hns3_rx_queue *rxq) +{ + struct rte_mbuf *mbuf; + uint64_t dma_addr; + uint16_t i; + + for (i = 0; i < rxq->nb_rx_desc; i++) { + mbuf = rte_mbuf_raw_alloc(rxq->mb_pool); + if (unlikely(mbuf == NULL)) { + hns3_err(hw, "Failed to allocate RXD[%d] for rx queue!", + i); + hns3_rx_queue_release_mbufs(rxq); + return -ENOMEM; + } + + rte_mbuf_refcnt_set(mbuf, 1); + mbuf->next = NULL; + mbuf->data_off = RTE_PKTMBUF_HEADROOM; + mbuf->nb_segs = 1; + mbuf->port = rxq->port_id; + + rxq->sw_ring[i].mbuf = mbuf; + dma_addr = rte_cpu_to_le_64(rte_mbuf_data_iova_default(mbuf)); + rxq->rx_ring[i].addr = dma_addr; + rxq->rx_ring[i].rx.bd_base_info = 0; + } + + return 0; +} + +static int +hns3_buf_size2type(uint32_t buf_size) +{ + int bd_size_type; + + switch (buf_size) { + case 512: + bd_size_type = HNS3_BD_SIZE_512_TYPE; + break; + case 1024: + bd_size_type = HNS3_BD_SIZE_1024_TYPE; + break; + case 4096: + bd_size_type = HNS3_BD_SIZE_4096_TYPE; + break; + default: + bd_size_type = HNS3_BD_SIZE_2048_TYPE; + } + + return bd_size_type; +} + +static void +hns3_init_rx_queue_hw(struct hns3_rx_queue *rxq) +{ + uint32_t rx_buf_len = rxq->rx_buf_len; + uint64_t dma_addr = rxq->rx_ring_phys_addr; + + hns3_write_dev(rxq, HNS3_RING_RX_BASEADDR_L_REG, (uint32_t)dma_addr); + hns3_write_dev(rxq, HNS3_RING_RX_BASEADDR_H_REG, + (uint32_t)((dma_addr >> 31) >> 1)); + + hns3_write_dev(rxq, HNS3_RING_RX_BD_LEN_REG, + hns3_buf_size2type(rx_buf_len)); + hns3_write_dev(rxq, HNS3_RING_RX_BD_NUM_REG, + HNS3_CFG_DESC_NUM(rxq->nb_rx_desc)); +} + +static void +hns3_init_tx_queue_hw(struct hns3_tx_queue *txq) +{ + uint64_t dma_addr = txq->tx_ring_phys_addr; + + hns3_write_dev(txq, HNS3_RING_TX_BASEADDR_L_REG, (uint32_t)dma_addr); + hns3_write_dev(txq, HNS3_RING_TX_BASEADDR_H_REG, + (uint32_t)((dma_addr >> 31) >> 1)); + + hns3_write_dev(txq, HNS3_RING_TX_BD_NUM_REG, + HNS3_CFG_DESC_NUM(txq->nb_tx_desc)); +} + +void +hns3_enable_all_queues(struct hns3_hw *hw, bool en) +{ + uint16_t nb_rx_q = hw->data->nb_rx_queues; + uint16_t nb_tx_q = hw->data->nb_tx_queues; + struct hns3_rx_queue *rxq; + struct hns3_tx_queue *txq; + uint32_t rcb_reg; + int i; + + for (i = 0; i < hw->cfg_max_queues; i++) { + if (i < nb_rx_q) + rxq = hw->data->rx_queues[i]; + else + rxq = hw->fkq_data.rx_queues[i - nb_rx_q]; + if (i < nb_tx_q) + txq = hw->data->tx_queues[i]; + else + txq = hw->fkq_data.tx_queues[i - nb_tx_q]; + if (rxq == NULL || txq == NULL || + (en && (rxq->rx_deferred_start || txq->tx_deferred_start))) + continue; + + rcb_reg = hns3_read_dev(rxq, HNS3_RING_EN_REG); + if (en) + rcb_reg |= BIT(HNS3_RING_EN_B); + else + rcb_reg &= ~BIT(HNS3_RING_EN_B); + hns3_write_dev(rxq, HNS3_RING_EN_REG, rcb_reg); + } +} + +static int +hns3_tqp_enable(struct hns3_hw *hw, uint16_t queue_id, bool enable) +{ + struct hns3_cfg_com_tqp_queue_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + req = (struct hns3_cfg_com_tqp_queue_cmd *)desc.data; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_CFG_COM_TQP_QUEUE, false); + req->tqp_id = rte_cpu_to_le_16(queue_id & HNS3_RING_ID_MASK); + req->stream_id = 0; + hns3_set_bit(req->enable, HNS3_TQP_ENABLE_B, enable ? 1 : 0); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "TQP enable fail, ret = %d", ret); + + return ret; +} + +static int +hns3_send_reset_tqp_cmd(struct hns3_hw *hw, uint16_t queue_id, bool enable) +{ + struct hns3_reset_tqp_queue_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RESET_TQP_QUEUE, false); + + req = (struct hns3_reset_tqp_queue_cmd *)desc.data; + req->tqp_id = rte_cpu_to_le_16(queue_id & HNS3_RING_ID_MASK); + hns3_set_bit(req->reset_req, HNS3_TQP_RESET_B, enable ? 1 : 0); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + hns3_err(hw, "Send tqp reset cmd error, ret = %d", ret); + + return ret; +} + +static int +hns3_get_reset_status(struct hns3_hw *hw, uint16_t queue_id) +{ + struct hns3_reset_tqp_queue_cmd *req; + struct hns3_cmd_desc desc; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_RESET_TQP_QUEUE, true); + + req = (struct hns3_reset_tqp_queue_cmd *)desc.data; + req->tqp_id = rte_cpu_to_le_16(queue_id & HNS3_RING_ID_MASK); + + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Get reset status error, ret =%d", ret); + return ret; + } + + return hns3_get_bit(req->ready_to_reset, HNS3_TQP_RESET_B); +} + +static int +hns3_reset_tqp(struct hns3_hw *hw, uint16_t queue_id) +{ +#define HNS3_TQP_RESET_TRY_MS 200 + uint64_t end; + int reset_status; + int ret; + + ret = hns3_tqp_enable(hw, queue_id, false); + if (ret) + return ret; + + /* + * In current version VF is not supported when PF is driven by DPDK + * driver, all task queue pairs are mapped to PF function, so PF's queue + * id is equals to the global queue id in PF range. + */ + ret = hns3_send_reset_tqp_cmd(hw, queue_id, true); + if (ret) { + hns3_err(hw, "Send reset tqp cmd fail, ret = %d", ret); + return ret; + } + ret = -ETIMEDOUT; + end = get_timeofday_ms() + HNS3_TQP_RESET_TRY_MS; + do { + /* Wait for tqp hw reset */ + rte_delay_ms(HNS3_POLL_RESPONE_MS); + reset_status = hns3_get_reset_status(hw, queue_id); + if (reset_status) { + ret = 0; + break; + } + } while (get_timeofday_ms() < end); + + if (ret) { + hns3_err(hw, "Reset TQP fail, ret = %d", ret); + return ret; + } + + ret = hns3_send_reset_tqp_cmd(hw, queue_id, false); + if (ret) + hns3_err(hw, "Deassert the soft reset fail, ret = %d", ret); + + return ret; +} + +static int +hns3vf_reset_tqp(struct hns3_hw *hw, uint16_t queue_id) +{ + uint8_t msg_data[2]; + int ret; + + /* Disable VF's queue before send queue reset msg to PF */ + ret = hns3_tqp_enable(hw, queue_id, false); + if (ret) + return ret; + + memcpy(msg_data, &queue_id, sizeof(uint16_t)); + + return hns3_send_mbx_msg(hw, HNS3_MBX_QUEUE_RESET, 0, msg_data, + sizeof(msg_data), true, NULL, 0); +} + +static int +hns3_reset_queue(struct hns3_adapter *hns, uint16_t queue_id) +{ + struct hns3_hw *hw = &hns->hw; + if (hns->is_vf) + return hns3vf_reset_tqp(hw, queue_id); + else + return hns3_reset_tqp(hw, queue_id); +} + +int +hns3_reset_all_queues(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + int ret, i; + + for (i = 0; i < hw->cfg_max_queues; i++) { + ret = hns3_reset_queue(hns, i); + if (ret) { + hns3_err(hw, "Failed to reset No.%d queue: %d", i, ret); + return ret; + } + } + return 0; +} + +void +hns3_set_queue_intr_gl(struct hns3_hw *hw, uint16_t queue_id, + uint8_t gl_idx, uint16_t gl_value) +{ + uint32_t offset[] = {HNS3_TQP_INTR_GL0_REG, + HNS3_TQP_INTR_GL1_REG, + HNS3_TQP_INTR_GL2_REG}; + uint32_t addr, value; + + if (gl_idx >= RTE_DIM(offset) || gl_value > HNS3_TQP_INTR_GL_MAX) + return; + + addr = offset[gl_idx] + queue_id * HNS3_TQP_INTR_REG_SIZE; + value = HNS3_GL_USEC_TO_REG(gl_value); + + hns3_write_dev(hw, addr, value); +} + +void +hns3_set_queue_intr_rl(struct hns3_hw *hw, uint16_t queue_id, uint16_t rl_value) +{ + uint32_t addr, value; + + if (rl_value > HNS3_TQP_INTR_RL_MAX) + return; + + addr = HNS3_TQP_INTR_RL_REG + queue_id * HNS3_TQP_INTR_REG_SIZE; + value = HNS3_RL_USEC_TO_REG(rl_value); + if (value > 0) + value |= HNS3_TQP_INTR_RL_ENABLE_MASK; + + hns3_write_dev(hw, addr, value); +} + +static void +hns3_queue_intr_enable(struct hns3_hw *hw, uint16_t queue_id, bool en) +{ + uint32_t addr, value; + + addr = HNS3_TQP_INTR_CTRL_REG + queue_id * HNS3_TQP_INTR_REG_SIZE; + value = en ? 1 : 0; + + hns3_write_dev(hw, addr, value); +} + +/* + * Enable all rx queue interrupt when in interrupt rx mode. + * This api was called before enable queue rx&tx (in normal start or reset + * recover scenes), used to fix hardware rx queue interrupt enable was clear + * when FLR. + */ +void +hns3_dev_all_rx_queue_intr_enable(struct hns3_hw *hw, bool en) +{ + struct rte_eth_dev *dev = &rte_eth_devices[hw->data->port_id]; + uint16_t nb_rx_q = hw->data->nb_rx_queues; + int i; + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return; + + for (i = 0; i < nb_rx_q; i++) + hns3_queue_intr_enable(hw, i, en); +} + +int +hns3_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id) +{ + struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev); + struct rte_intr_handle *intr_handle = &pci_dev->intr_handle; + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return -ENOTSUP; + + hns3_queue_intr_enable(hw, queue_id, true); + + return rte_intr_ack(intr_handle); +} + +int +hns3_dev_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + if (dev->data->dev_conf.intr_conf.rxq == 0) + return -ENOTSUP; + + hns3_queue_intr_enable(hw, queue_id, false); + + return 0; +} + +static int +hns3_dev_rx_queue_start(struct hns3_adapter *hns, uint16_t idx) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_rx_queue *rxq; + int ret; + + PMD_INIT_FUNC_TRACE(); + + rxq = (struct hns3_rx_queue *)hw->data->rx_queues[idx]; + ret = hns3_alloc_rx_queue_mbufs(hw, rxq); + if (ret) { + hns3_err(hw, "Failed to alloc mbuf for No.%d rx queue: %d", + idx, ret); + return ret; + } + + rxq->next_to_use = 0; + rxq->next_to_clean = 0; + rxq->nb_rx_hold = 0; + hns3_init_rx_queue_hw(rxq); + + return 0; +} + +static void +hns3_fake_rx_queue_start(struct hns3_adapter *hns, uint16_t idx) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_rx_queue *rxq; + + rxq = (struct hns3_rx_queue *)hw->fkq_data.rx_queues[idx]; + rxq->next_to_use = 0; + rxq->next_to_clean = 0; + rxq->nb_rx_hold = 0; + hns3_init_rx_queue_hw(rxq); +} + +static void +hns3_init_tx_queue(struct hns3_tx_queue *queue) +{ + struct hns3_tx_queue *txq = queue; + struct hns3_desc *desc; + int i; + + /* Clear tx bd */ + desc = txq->tx_ring; + for (i = 0; i < txq->nb_tx_desc; i++) { + desc->tx.tp_fe_sc_vld_ra_ri = 0; + desc++; + } + + txq->next_to_use = 0; + txq->next_to_clean = 0; + txq->tx_bd_ready = txq->nb_tx_desc - 1; + hns3_init_tx_queue_hw(txq); +} + +static void +hns3_dev_tx_queue_start(struct hns3_adapter *hns, uint16_t idx) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_tx_queue *txq; + + txq = (struct hns3_tx_queue *)hw->data->tx_queues[idx]; + hns3_init_tx_queue(txq); +} + +static void +hns3_fake_tx_queue_start(struct hns3_adapter *hns, uint16_t idx) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_tx_queue *txq; + + txq = (struct hns3_tx_queue *)hw->fkq_data.tx_queues[idx]; + hns3_init_tx_queue(txq); +} + +static void +hns3_init_tx_ring_tc(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_tx_queue *txq; + int i, num; + + for (i = 0; i < HNS3_MAX_TC_NUM; i++) { + struct hns3_tc_queue_info *tc_queue = &hw->tc_queue[i]; + int j; + + if (!tc_queue->enable) + continue; + + for (j = 0; j < tc_queue->tqp_count; j++) { + num = tc_queue->tqp_offset + j; + txq = (struct hns3_tx_queue *)hw->data->tx_queues[num]; + if (txq == NULL) + continue; + + hns3_write_dev(txq, HNS3_RING_TX_TC_REG, tc_queue->tc); + } + } +} + +static int +hns3_start_rx_queues(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_rx_queue *rxq; + int i, j; + int ret; + + /* Initialize RSS for queues */ + ret = hns3_config_rss(hns); + if (ret) { + hns3_err(hw, "Failed to configure rss %d", ret); + return ret; + } + + for (i = 0; i < hw->data->nb_rx_queues; i++) { + rxq = (struct hns3_rx_queue *)hw->data->rx_queues[i]; + if (rxq == NULL || rxq->rx_deferred_start) + continue; + ret = hns3_dev_rx_queue_start(hns, i); + if (ret) { + hns3_err(hw, "Failed to start No.%d rx queue: %d", i, + ret); + goto out; + } + } + + for (i = 0; i < hw->fkq_data.nb_fake_rx_queues; i++) { + rxq = (struct hns3_rx_queue *)hw->fkq_data.rx_queues[i]; + if (rxq == NULL || rxq->rx_deferred_start) + continue; + hns3_fake_rx_queue_start(hns, i); + } + return 0; + +out: + for (j = 0; j < i; j++) { + rxq = (struct hns3_rx_queue *)hw->data->rx_queues[j]; + hns3_rx_queue_release_mbufs(rxq); + } + + return ret; +} + +static void +hns3_start_tx_queues(struct hns3_adapter *hns) +{ + struct hns3_hw *hw = &hns->hw; + struct hns3_tx_queue *txq; + int i; + + for (i = 0; i < hw->data->nb_tx_queues; i++) { + txq = (struct hns3_tx_queue *)hw->data->tx_queues[i]; + if (txq == NULL || txq->tx_deferred_start) + continue; + hns3_dev_tx_queue_start(hns, i); + } + + for (i = 0; i < hw->fkq_data.nb_fake_tx_queues; i++) { + txq = (struct hns3_tx_queue *)hw->fkq_data.tx_queues[i]; + if (txq == NULL || txq->tx_deferred_start) + continue; + hns3_fake_tx_queue_start(hns, i); + } + + hns3_init_tx_ring_tc(hns); +} + +/* + * Start all queues. + * Note: just init and setup queues, and don't enable queue rx&tx. + */ +int +hns3_start_queues(struct hns3_adapter *hns, bool reset_queue) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + if (reset_queue) { + ret = hns3_reset_all_queues(hns); + if (ret) { + hns3_err(hw, "Failed to reset all queues %d", ret); + return ret; + } + } + + ret = hns3_start_rx_queues(hns); + if (ret) { + hns3_err(hw, "Failed to start rx queues: %d", ret); + return ret; + } + + hns3_start_tx_queues(hns); + + return 0; +} + +int +hns3_stop_queues(struct hns3_adapter *hns, bool reset_queue) +{ + struct hns3_hw *hw = &hns->hw; + int ret; + + hns3_enable_all_queues(hw, false); + if (reset_queue) { + ret = hns3_reset_all_queues(hns); + if (ret) { + hns3_err(hw, "Failed to reset all queues %d", ret); + return ret; + } + } + return 0; +} + +static void* +hns3_alloc_rxq_and_dma_zone(struct rte_eth_dev *dev, + struct hns3_queue_info *q_info) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + const struct rte_memzone *rx_mz; + struct hns3_rx_queue *rxq; + unsigned int rx_desc; + + rxq = rte_zmalloc_socket(q_info->type, sizeof(struct hns3_rx_queue), + RTE_CACHE_LINE_SIZE, q_info->socket_id); + if (rxq == NULL) { + hns3_err(hw, "Failed to allocate memory for No.%d rx ring!", + q_info->idx); + return NULL; + } + + /* Allocate rx ring hardware descriptors. */ + rxq->queue_id = q_info->idx; + rxq->nb_rx_desc = q_info->nb_desc; + rx_desc = rxq->nb_rx_desc * sizeof(struct hns3_desc); + rx_mz = rte_eth_dma_zone_reserve(dev, q_info->ring_name, q_info->idx, + rx_desc, HNS3_RING_BASE_ALIGN, + q_info->socket_id); + if (rx_mz == NULL) { + hns3_err(hw, "Failed to reserve DMA memory for No.%d rx ring!", + q_info->idx); + hns3_rx_queue_release(rxq); + return NULL; + } + rxq->mz = rx_mz; + rxq->rx_ring = (struct hns3_desc *)rx_mz->addr; + rxq->rx_ring_phys_addr = rx_mz->iova; + + hns3_dbg(hw, "No.%d rx descriptors iova 0x%" PRIx64, q_info->idx, + rxq->rx_ring_phys_addr); + + return rxq; +} + +static int +hns3_fake_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, + uint16_t nb_desc, unsigned int socket_id) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_queue_info q_info; + struct hns3_rx_queue *rxq; + uint16_t nb_rx_q; + + if (hw->fkq_data.rx_queues[idx]) { + hns3_rx_queue_release(hw->fkq_data.rx_queues[idx]); + hw->fkq_data.rx_queues[idx] = NULL; + } + + q_info.idx = idx; + q_info.socket_id = socket_id; + q_info.nb_desc = nb_desc; + q_info.type = "hns3 fake RX queue"; + q_info.ring_name = "rx_fake_ring"; + rxq = hns3_alloc_rxq_and_dma_zone(dev, &q_info); + if (rxq == NULL) { + hns3_err(hw, "Failed to setup No.%d fake rx ring.", idx); + return -ENOMEM; + } + + /* Don't need alloc sw_ring, because upper applications don't use it */ + rxq->sw_ring = NULL; + + rxq->hns = hns; + rxq->rx_deferred_start = false; + rxq->port_id = dev->data->port_id; + rxq->configured = true; + nb_rx_q = dev->data->nb_rx_queues; + rxq->io_base = (void *)((char *)hw->io_base + HNS3_TQP_REG_OFFSET + + (nb_rx_q + idx) * HNS3_TQP_REG_SIZE); + rxq->rx_buf_len = hw->rx_buf_len; + + rte_spinlock_lock(&hw->lock); + hw->fkq_data.rx_queues[idx] = rxq; + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static void* +hns3_alloc_txq_and_dma_zone(struct rte_eth_dev *dev, + struct hns3_queue_info *q_info) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + const struct rte_memzone *tx_mz; + struct hns3_tx_queue *txq; + struct hns3_desc *desc; + unsigned int tx_desc; + int i; + + txq = rte_zmalloc_socket(q_info->type, sizeof(struct hns3_tx_queue), + RTE_CACHE_LINE_SIZE, q_info->socket_id); + if (txq == NULL) { + hns3_err(hw, "Failed to allocate memory for No.%d tx ring!", + q_info->idx); + return NULL; + } + + /* Allocate tx ring hardware descriptors. */ + txq->queue_id = q_info->idx; + txq->nb_tx_desc = q_info->nb_desc; + tx_desc = txq->nb_tx_desc * sizeof(struct hns3_desc); + tx_mz = rte_eth_dma_zone_reserve(dev, q_info->ring_name, q_info->idx, + tx_desc, HNS3_RING_BASE_ALIGN, + q_info->socket_id); + if (tx_mz == NULL) { + hns3_err(hw, "Failed to reserve DMA memory for No.%d tx ring!", + q_info->idx); + hns3_tx_queue_release(txq); + return NULL; + } + txq->mz = tx_mz; + txq->tx_ring = (struct hns3_desc *)tx_mz->addr; + txq->tx_ring_phys_addr = tx_mz->iova; + + hns3_dbg(hw, "No.%d tx descriptors iova 0x%" PRIx64, q_info->idx, + txq->tx_ring_phys_addr); + + /* Clear tx bd */ + desc = txq->tx_ring; + for (i = 0; i < txq->nb_tx_desc; i++) { + desc->tx.tp_fe_sc_vld_ra_ri = 0; + desc++; + } + + return txq; +} + +static int +hns3_fake_tx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, + uint16_t nb_desc, unsigned int socket_id) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_queue_info q_info; + struct hns3_tx_queue *txq; + uint16_t nb_tx_q; + + if (hw->fkq_data.tx_queues[idx] != NULL) { + hns3_tx_queue_release(hw->fkq_data.tx_queues[idx]); + hw->fkq_data.tx_queues[idx] = NULL; + } + + q_info.idx = idx; + q_info.socket_id = socket_id; + q_info.nb_desc = nb_desc; + q_info.type = "hns3 fake TX queue"; + q_info.ring_name = "tx_fake_ring"; + txq = hns3_alloc_txq_and_dma_zone(dev, &q_info); + if (txq == NULL) { + hns3_err(hw, "Failed to setup No.%d fake tx ring.", idx); + return -ENOMEM; + } + + /* Don't need alloc sw_ring, because upper applications don't use it */ + txq->sw_ring = NULL; + + txq->hns = hns; + txq->tx_deferred_start = false; + txq->port_id = dev->data->port_id; + txq->configured = true; + nb_tx_q = dev->data->nb_tx_queues; + txq->io_base = (void *)((char *)hw->io_base + HNS3_TQP_REG_OFFSET + + (nb_tx_q + idx) * HNS3_TQP_REG_SIZE); + + rte_spinlock_lock(&hw->lock); + hw->fkq_data.tx_queues[idx] = txq; + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static int +hns3_fake_rx_queue_config(struct hns3_hw *hw, uint16_t nb_queues) +{ + uint16_t old_nb_queues = hw->fkq_data.nb_fake_rx_queues; + void **rxq; + uint8_t i; + + if (hw->fkq_data.rx_queues == NULL && nb_queues != 0) { + /* first time configuration */ + uint32_t size; + size = sizeof(hw->fkq_data.rx_queues[0]) * nb_queues; + hw->fkq_data.rx_queues = rte_zmalloc("fake_rx_queues", size, + RTE_CACHE_LINE_SIZE); + if (hw->fkq_data.rx_queues == NULL) { + hw->fkq_data.nb_fake_rx_queues = 0; + return -ENOMEM; + } + } else if (hw->fkq_data.rx_queues != NULL && nb_queues != 0) { + /* re-configure */ + rxq = hw->fkq_data.rx_queues; + for (i = nb_queues; i < old_nb_queues; i++) + hns3_dev_rx_queue_release(rxq[i]); + + rxq = rte_realloc(rxq, sizeof(rxq[0]) * nb_queues, + RTE_CACHE_LINE_SIZE); + if (rxq == NULL) + return -ENOMEM; + if (nb_queues > old_nb_queues) { + uint16_t new_qs = nb_queues - old_nb_queues; + memset(rxq + old_nb_queues, 0, sizeof(rxq[0]) * new_qs); + } + + hw->fkq_data.rx_queues = rxq; + } else if (hw->fkq_data.rx_queues != NULL && nb_queues == 0) { + rxq = hw->fkq_data.rx_queues; + for (i = nb_queues; i < old_nb_queues; i++) + hns3_dev_rx_queue_release(rxq[i]); + + rte_free(hw->fkq_data.rx_queues); + hw->fkq_data.rx_queues = NULL; + } + + hw->fkq_data.nb_fake_rx_queues = nb_queues; + + return 0; +} + +static int +hns3_fake_tx_queue_config(struct hns3_hw *hw, uint16_t nb_queues) +{ + uint16_t old_nb_queues = hw->fkq_data.nb_fake_tx_queues; + void **txq; + uint8_t i; + + if (hw->fkq_data.tx_queues == NULL && nb_queues != 0) { + /* first time configuration */ + uint32_t size; + size = sizeof(hw->fkq_data.tx_queues[0]) * nb_queues; + hw->fkq_data.tx_queues = rte_zmalloc("fake_tx_queues", size, + RTE_CACHE_LINE_SIZE); + if (hw->fkq_data.tx_queues == NULL) { + hw->fkq_data.nb_fake_tx_queues = 0; + return -ENOMEM; + } + } else if (hw->fkq_data.tx_queues != NULL && nb_queues != 0) { + /* re-configure */ + txq = hw->fkq_data.tx_queues; + for (i = nb_queues; i < old_nb_queues; i++) + hns3_dev_tx_queue_release(txq[i]); + txq = rte_realloc(txq, sizeof(txq[0]) * nb_queues, + RTE_CACHE_LINE_SIZE); + if (txq == NULL) + return -ENOMEM; + if (nb_queues > old_nb_queues) { + uint16_t new_qs = nb_queues - old_nb_queues; + memset(txq + old_nb_queues, 0, sizeof(txq[0]) * new_qs); + } + + hw->fkq_data.tx_queues = txq; + } else if (hw->fkq_data.tx_queues != NULL && nb_queues == 0) { + txq = hw->fkq_data.tx_queues; + for (i = nb_queues; i < old_nb_queues; i++) + hns3_dev_tx_queue_release(txq[i]); + + rte_free(hw->fkq_data.tx_queues); + hw->fkq_data.tx_queues = NULL; + } + hw->fkq_data.nb_fake_tx_queues = nb_queues; + + return 0; +} + +int +hns3_set_fake_rx_or_tx_queues(struct rte_eth_dev *dev, uint16_t nb_rx_q, + uint16_t nb_tx_q) +{ + struct hns3_hw *hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + uint16_t rx_need_add_nb_q; + uint16_t tx_need_add_nb_q; + uint16_t port_id; + uint16_t q; + int ret; + + /* Setup new number of fake RX/TX queues and reconfigure device. */ + hw->cfg_max_queues = RTE_MAX(nb_rx_q, nb_tx_q); + rx_need_add_nb_q = hw->cfg_max_queues - nb_rx_q; + tx_need_add_nb_q = hw->cfg_max_queues - nb_tx_q; + ret = hns3_fake_rx_queue_config(hw, rx_need_add_nb_q); + if (ret) { + hns3_err(hw, "Fail to configure fake rx queues: %d", ret); + goto cfg_fake_rx_q_fail; + } + + ret = hns3_fake_tx_queue_config(hw, tx_need_add_nb_q); + if (ret) { + hns3_err(hw, "Fail to configure fake rx queues: %d", ret); + goto cfg_fake_tx_q_fail; + } + + /* Allocate and set up fake RX queue per Ethernet port. */ + port_id = hw->data->port_id; + for (q = 0; q < rx_need_add_nb_q; q++) { + ret = hns3_fake_rx_queue_setup(dev, q, HNS3_MIN_RING_DESC, + rte_eth_dev_socket_id(port_id)); + if (ret) + goto setup_fake_rx_q_fail; + } + + /* Allocate and set up fake TX queue per Ethernet port. */ + for (q = 0; q < tx_need_add_nb_q; q++) { + ret = hns3_fake_tx_queue_setup(dev, q, HNS3_MIN_RING_DESC, + rte_eth_dev_socket_id(port_id)); + if (ret) + goto setup_fake_tx_q_fail; + } + + return 0; + +setup_fake_tx_q_fail: +setup_fake_rx_q_fail: + (void)hns3_fake_tx_queue_config(hw, 0); +cfg_fake_tx_q_fail: + (void)hns3_fake_rx_queue_config(hw, 0); +cfg_fake_rx_q_fail: + hw->cfg_max_queues = 0; + + return ret; +} + +void +hns3_dev_release_mbufs(struct hns3_adapter *hns) +{ + struct rte_eth_dev_data *dev_data = hns->hw.data; + struct hns3_rx_queue *rxq; + struct hns3_tx_queue *txq; + int i; + + if (dev_data->rx_queues) + for (i = 0; i < dev_data->nb_rx_queues; i++) { + rxq = dev_data->rx_queues[i]; + if (rxq == NULL || rxq->rx_deferred_start) + continue; + hns3_rx_queue_release_mbufs(rxq); + } + + if (dev_data->tx_queues) + for (i = 0; i < dev_data->nb_tx_queues; i++) { + txq = dev_data->tx_queues[i]; + if (txq == NULL || txq->tx_deferred_start) + continue; + hns3_tx_queue_release_mbufs(txq); + } +} + +int +hns3_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t nb_desc, + unsigned int socket_id, const struct rte_eth_rxconf *conf, + struct rte_mempool *mp) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_queue_info q_info; + struct hns3_rx_queue *rxq; + int rx_entry_len; + + if (dev->data->dev_started) { + hns3_err(hw, "rx_queue_setup after dev_start no supported"); + return -EINVAL; + } + + if (nb_desc > HNS3_MAX_RING_DESC || nb_desc < HNS3_MIN_RING_DESC || + nb_desc % HNS3_ALIGN_RING_DESC) { + hns3_err(hw, "Number (%u) of rx descriptors is invalid", + nb_desc); + return -EINVAL; + } + + if (dev->data->rx_queues[idx]) { + hns3_rx_queue_release(dev->data->rx_queues[idx]); + dev->data->rx_queues[idx] = NULL; + } + + q_info.idx = idx; + q_info.socket_id = socket_id; + q_info.nb_desc = nb_desc; + q_info.type = "hns3 RX queue"; + q_info.ring_name = "rx_ring"; + rxq = hns3_alloc_rxq_and_dma_zone(dev, &q_info); + if (rxq == NULL) { + hns3_err(hw, + "Failed to alloc mem and reserve DMA mem for rx ring!"); + return -ENOMEM; + } + + rxq->hns = hns; + rxq->mb_pool = mp; + if (conf->rx_free_thresh <= 0) + rxq->rx_free_thresh = DEFAULT_RX_FREE_THRESH; + else + rxq->rx_free_thresh = conf->rx_free_thresh; + rxq->rx_deferred_start = conf->rx_deferred_start; + + rx_entry_len = sizeof(struct hns3_entry) * rxq->nb_rx_desc; + rxq->sw_ring = rte_zmalloc_socket("hns3 RX sw ring", rx_entry_len, + RTE_CACHE_LINE_SIZE, socket_id); + if (rxq->sw_ring == NULL) { + hns3_err(hw, "Failed to allocate memory for rx sw ring!"); + hns3_rx_queue_release(rxq); + return -ENOMEM; + } + + rxq->next_to_use = 0; + rxq->next_to_clean = 0; + rxq->nb_rx_hold = 0; + rxq->pkt_first_seg = NULL; + rxq->pkt_last_seg = NULL; + rxq->port_id = dev->data->port_id; + rxq->configured = true; + rxq->io_base = (void *)((char *)hw->io_base + HNS3_TQP_REG_OFFSET + + idx * HNS3_TQP_REG_SIZE); + rxq->rx_buf_len = hw->rx_buf_len; + rxq->l2_errors = 0; + rxq->pkt_len_errors = 0; + rxq->l3_csum_erros = 0; + rxq->l4_csum_erros = 0; + rxq->ol3_csum_erros = 0; + rxq->ol4_csum_erros = 0; + + rte_spinlock_lock(&hw->lock); + dev->data->rx_queues[idx] = rxq; + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static inline uint32_t +rxd_pkt_info_to_pkt_type(uint32_t pkt_info, uint32_t ol_info) +{ +#define HNS3_L2TBL_NUM 4 +#define HNS3_L3TBL_NUM 16 +#define HNS3_L4TBL_NUM 16 +#define HNS3_OL3TBL_NUM 16 +#define HNS3_OL4TBL_NUM 16 + uint32_t pkt_type = 0; + uint32_t l2id, l3id, l4id; + uint32_t ol3id, ol4id; + + static const uint32_t l2table[HNS3_L2TBL_NUM] = { + RTE_PTYPE_L2_ETHER, + RTE_PTYPE_L2_ETHER_VLAN, + RTE_PTYPE_L2_ETHER_QINQ, + 0 + }; + + static const uint32_t l3table[HNS3_L3TBL_NUM] = { + RTE_PTYPE_L3_IPV4, + RTE_PTYPE_L3_IPV6, + RTE_PTYPE_L2_ETHER_ARP, + RTE_PTYPE_L2_ETHER, + RTE_PTYPE_L3_IPV4_EXT, + RTE_PTYPE_L3_IPV6_EXT, + RTE_PTYPE_L2_ETHER_LLDP, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + static const uint32_t l4table[HNS3_L4TBL_NUM] = { + RTE_PTYPE_L4_UDP, + RTE_PTYPE_L4_TCP, + RTE_PTYPE_TUNNEL_GRE, + RTE_PTYPE_L4_SCTP, + RTE_PTYPE_L4_IGMP, + RTE_PTYPE_L4_ICMP, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + static const uint32_t inner_l2table[HNS3_L2TBL_NUM] = { + RTE_PTYPE_INNER_L2_ETHER, + RTE_PTYPE_INNER_L2_ETHER_VLAN, + RTE_PTYPE_INNER_L2_ETHER_QINQ, + 0 + }; + + static const uint32_t inner_l3table[HNS3_L3TBL_NUM] = { + RTE_PTYPE_INNER_L3_IPV4, + RTE_PTYPE_INNER_L3_IPV6, + 0, + RTE_PTYPE_INNER_L2_ETHER, + RTE_PTYPE_INNER_L3_IPV4_EXT, + RTE_PTYPE_INNER_L3_IPV6_EXT, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + static const uint32_t inner_l4table[HNS3_L4TBL_NUM] = { + RTE_PTYPE_INNER_L4_UDP, + RTE_PTYPE_INNER_L4_TCP, + RTE_PTYPE_TUNNEL_GRE, + RTE_PTYPE_INNER_L4_SCTP, + RTE_PTYPE_L4_IGMP, + RTE_PTYPE_INNER_L4_ICMP, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + static const uint32_t ol3table[HNS3_OL3TBL_NUM] = { + RTE_PTYPE_L3_IPV4, + RTE_PTYPE_L3_IPV6, + 0, 0, + RTE_PTYPE_L3_IPV4_EXT, + RTE_PTYPE_L3_IPV6_EXT, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + RTE_PTYPE_UNKNOWN + }; + + static const uint32_t ol4table[HNS3_OL4TBL_NUM] = { + 0, + RTE_PTYPE_TUNNEL_VXLAN, + RTE_PTYPE_TUNNEL_NVGRE, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + l2id = hns3_get_field(pkt_info, HNS3_RXD_STRP_TAGP_M, + HNS3_RXD_STRP_TAGP_S); + l3id = hns3_get_field(pkt_info, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S); + l4id = hns3_get_field(pkt_info, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S); + ol3id = hns3_get_field(ol_info, HNS3_RXD_OL3ID_M, HNS3_RXD_OL3ID_S); + ol4id = hns3_get_field(ol_info, HNS3_RXD_OL4ID_M, HNS3_RXD_OL4ID_S); + + if (ol4table[ol4id]) + pkt_type |= (inner_l2table[l2id] | inner_l3table[l3id] | + inner_l4table[l4id] | ol3table[ol3id] | + ol4table[ol4id]); + else + pkt_type |= (l2table[l2id] | l3table[l3id] | l4table[l4id]); + return pkt_type; +} + +const uint32_t * +hns3_dev_supported_ptypes_get(struct rte_eth_dev *dev) +{ + static const uint32_t ptypes[] = { + RTE_PTYPE_L2_ETHER, + RTE_PTYPE_L2_ETHER_VLAN, + RTE_PTYPE_L2_ETHER_QINQ, + RTE_PTYPE_L2_ETHER_LLDP, + RTE_PTYPE_L2_ETHER_ARP, + RTE_PTYPE_L3_IPV4, + RTE_PTYPE_L3_IPV4_EXT, + RTE_PTYPE_L3_IPV6, + RTE_PTYPE_L3_IPV6_EXT, + RTE_PTYPE_L4_IGMP, + RTE_PTYPE_L4_ICMP, + RTE_PTYPE_L4_SCTP, + RTE_PTYPE_L4_TCP, + RTE_PTYPE_L4_UDP, + RTE_PTYPE_TUNNEL_GRE, + RTE_PTYPE_UNKNOWN + }; + + if (dev->rx_pkt_burst == hns3_recv_pkts) + return ptypes; + + return NULL; +} + +static void +hns3_clean_rx_buffers(struct hns3_rx_queue *rxq, int count) +{ + rxq->next_to_use += count; + if (rxq->next_to_use >= rxq->nb_rx_desc) + rxq->next_to_use -= rxq->nb_rx_desc; + + hns3_write_dev(rxq, HNS3_RING_RX_HEAD_REG, count); +} + +static int +hns3_handle_bdinfo(struct hns3_rx_queue *rxq, struct rte_mbuf *rxm, + uint32_t bd_base_info, uint32_t l234_info, + uint32_t *cksum_err) +{ + uint32_t tmp = 0; + + if (unlikely(l234_info & BIT(HNS3_RXD_L2E_B))) { + rxq->l2_errors++; + return -EINVAL; + } + + if (unlikely(rxm->pkt_len == 0 || + (l234_info & BIT(HNS3_RXD_TRUNCAT_B)))) { + rxq->pkt_len_errors++; + return -EINVAL; + } + + if (bd_base_info & BIT(HNS3_RXD_L3L4P_B)) { + if (unlikely(l234_info & BIT(HNS3_RXD_L3E_B))) { + rxm->ol_flags |= PKT_RX_IP_CKSUM_BAD; + rxq->l3_csum_erros++; + tmp |= HNS3_L3_CKSUM_ERR; + } + + if (unlikely(l234_info & BIT(HNS3_RXD_L4E_B))) { + rxm->ol_flags |= PKT_RX_L4_CKSUM_BAD; + rxq->l4_csum_erros++; + tmp |= HNS3_L4_CKSUM_ERR; + } + + if (unlikely(l234_info & BIT(HNS3_RXD_OL3E_B))) { + rxq->ol3_csum_erros++; + tmp |= HNS3_OUTER_L3_CKSUM_ERR; + } + + if (unlikely(l234_info & BIT(HNS3_RXD_OL4E_B))) { + rxm->ol_flags |= PKT_RX_OUTER_L4_CKSUM_BAD; + rxq->ol4_csum_erros++; + tmp |= HNS3_OUTER_L4_CKSUM_ERR; + } + } + *cksum_err = tmp; + + return 0; +} + +static void +hns3_rx_set_cksum_flag(struct rte_mbuf *rxm, uint64_t packet_type, + const uint32_t cksum_err) +{ + if (unlikely((packet_type & RTE_PTYPE_TUNNEL_MASK))) { + if (likely(packet_type & RTE_PTYPE_INNER_L3_MASK) && + (cksum_err & HNS3_L3_CKSUM_ERR) == 0) + rxm->ol_flags |= PKT_RX_IP_CKSUM_GOOD; + if (likely(packet_type & RTE_PTYPE_INNER_L4_MASK) && + (cksum_err & HNS3_L4_CKSUM_ERR) == 0) + rxm->ol_flags |= PKT_RX_L4_CKSUM_GOOD; + if (likely(packet_type & RTE_PTYPE_L4_MASK) && + (cksum_err & HNS3_OUTER_L4_CKSUM_ERR) == 0) + rxm->ol_flags |= PKT_RX_OUTER_L4_CKSUM_GOOD; + } else { + if (likely(packet_type & RTE_PTYPE_L3_MASK) && + (cksum_err & HNS3_L3_CKSUM_ERR) == 0) + rxm->ol_flags |= PKT_RX_IP_CKSUM_GOOD; + if (likely(packet_type & RTE_PTYPE_L4_MASK) && + (cksum_err & HNS3_L4_CKSUM_ERR) == 0) + rxm->ol_flags |= PKT_RX_L4_CKSUM_GOOD; + } +} + +uint16_t +hns3_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts) +{ + volatile struct hns3_desc *rx_ring; /* RX ring (desc) */ + volatile struct hns3_desc *rxdp; /* pointer of the current desc */ + struct hns3_rx_queue *rxq; /* RX queue */ + struct hns3_entry *sw_ring; + struct hns3_entry *rxe; + struct rte_mbuf *first_seg; + struct rte_mbuf *last_seg; + struct hns3_desc rxd; + struct rte_mbuf *nmb; /* pointer of the new mbuf */ + struct rte_mbuf *rxm; + struct rte_eth_dev *dev; + uint32_t bd_base_info; + uint32_t cksum_err; + uint32_t l234_info; + uint32_t ol_info; + uint64_t dma_addr; + uint16_t data_len; + uint16_t nb_rx_bd; + uint16_t pkt_len; + uint16_t nb_rx; + uint16_t rx_id; + int ret; + + nb_rx = 0; + nb_rx_bd = 0; + rxq = rx_queue; + dev = &rte_eth_devices[rxq->port_id]; + + rx_id = rxq->next_to_clean; + rx_ring = rxq->rx_ring; + first_seg = rxq->pkt_first_seg; + last_seg = rxq->pkt_last_seg; + sw_ring = rxq->sw_ring; + + while (nb_rx < nb_pkts) { + rxdp = &rx_ring[rx_id]; + bd_base_info = rte_le_to_cpu_32(rxdp->rx.bd_base_info); + if (unlikely(!hns3_get_bit(bd_base_info, HNS3_RXD_VLD_B))) + break; + /* + * The interactive process between software and hardware of + * receiving a new packet in hns3 network engine: + * 1. Hardware network engine firstly writes the packet content + * to the memory pointed by the 'addr' field of the Rx Buffer + * Descriptor, secondly fills the result of parsing the + * packet include the valid field into the Rx Buffer + * Descriptor in one write operation. + * 2. Driver reads the Rx BD's valid field in the loop to check + * whether it's valid, if valid then assign a new address to + * the addr field, clear the valid field, get the other + * information of the packet by parsing Rx BD's other fields, + * finally write back the number of Rx BDs processed by the + * driver to the HNS3_RING_RX_HEAD_REG register to inform + * hardware. + * In the above process, the ordering is very important. We must + * make sure that CPU read Rx BD's other fields only after the + * Rx BD is valid. + * + * There are two type of re-ordering: compiler re-ordering and + * CPU re-ordering under the ARMv8 architecture. + * 1. we use volatile to deal with compiler re-ordering, so you + * can see that rx_ring/rxdp defined with volatile. + * 2. we commonly use memory barrier to deal with CPU + * re-ordering, but the cost is high. + * + * In order to solve the high cost of using memory barrier, we + * use the data dependency order under the ARMv8 architecture, + * for example: + * instr01: load A + * instr02: load B <- A + * the instr02 will always execute after instr01. + * + * To construct the data dependency ordering, we use the + * following assignment: + * rxd = rxdp[(bd_base_info & (1u << HNS3_RXD_VLD_B)) - + * (1u<<HNS3_RXD_VLD_B)] + * Using gcc compiler under the ARMv8 architecture, the related + * assembly code example as follows: + * note: (1u << HNS3_RXD_VLD_B) equal 0x10 + * instr01: ldr w26, [x22, #28] --read bd_base_info + * instr02: and w0, w26, #0x10 --calc bd_base_info & 0x10 + * instr03: sub w0, w0, #0x10 --calc (bd_base_info & + * 0x10) - 0x10 + * instr04: add x0, x22, x0, lsl #5 --calc copy source addr + * instr05: ldp x2, x3, [x0] + * instr06: stp x2, x3, [x29, #256] --copy BD's [0 ~ 15]B + * instr07: ldp x4, x5, [x0, #16] + * instr08: stp x4, x5, [x29, #272] --copy BD's [16 ~ 31]B + * the instr05~08 depend on x0's value, x0 depent on w26's + * value, the w26 is the bd_base_info, this form the data + * dependency ordering. + * note: if BD is valid, (bd_base_info & (1u<<HNS3_RXD_VLD_B)) - + * (1u<<HNS3_RXD_VLD_B) will always zero, so the + * assignment is correct. + * + * So we use the data dependency ordering instead of memory + * barrier to improve receive performance. + */ + rxd = rxdp[(bd_base_info & (1u << HNS3_RXD_VLD_B)) - + (1u << HNS3_RXD_VLD_B)]; + + nmb = rte_mbuf_raw_alloc(rxq->mb_pool); + if (unlikely(nmb == NULL)) { + dev->data->rx_mbuf_alloc_failed++; + break; + } + + nb_rx_bd++; + rxe = &sw_ring[rx_id]; + rx_id++; + if (unlikely(rx_id == rxq->nb_rx_desc)) + rx_id = 0; + + rte_prefetch0(sw_ring[rx_id].mbuf); + if ((rx_id & 0x3) == 0) { + rte_prefetch0(&rx_ring[rx_id]); + rte_prefetch0(&sw_ring[rx_id]); + } + + rxm = rxe->mbuf; + rxe->mbuf = nmb; + + dma_addr = rte_cpu_to_le_64(rte_mbuf_data_iova_default(nmb)); + rxdp->rx.bd_base_info = 0; + rxdp->addr = dma_addr; + + /* Load remained descriptor data and extract necessary fields */ + data_len = (uint16_t)(rte_le_to_cpu_16(rxd.rx.size)); + l234_info = rte_le_to_cpu_32(rxd.rx.l234_info); + ol_info = rte_le_to_cpu_32(rxd.rx.ol_info); + + if (first_seg == NULL) { + first_seg = rxm; + first_seg->nb_segs = 1; + } else { + first_seg->nb_segs++; + last_seg->next = rxm; + } + + rxm->data_off = RTE_PKTMBUF_HEADROOM; + rxm->data_len = data_len; + + if (!hns3_get_bit(bd_base_info, HNS3_RXD_FE_B)) { + last_seg = rxm; + continue; + } + + /* The last buffer of the received packet */ + pkt_len = (uint16_t)(rte_le_to_cpu_16(rxd.rx.pkt_len)); + first_seg->pkt_len = pkt_len; + first_seg->port = rxq->port_id; + first_seg->hash.rss = rte_le_to_cpu_32(rxd.rx.rss_hash); + first_seg->ol_flags = PKT_RX_RSS_HASH; + if (unlikely(hns3_get_bit(bd_base_info, HNS3_RXD_LUM_B))) { + first_seg->hash.fdir.hi = + rte_le_to_cpu_32(rxd.rx.fd_id); + first_seg->ol_flags |= PKT_RX_FDIR | PKT_RX_FDIR_ID; + } + rxm->next = NULL; + + ret = hns3_handle_bdinfo(rxq, first_seg, bd_base_info, + l234_info, &cksum_err); + if (unlikely(ret)) + goto pkt_err; + + first_seg->packet_type = rxd_pkt_info_to_pkt_type(l234_info, + ol_info); + + if (bd_base_info & BIT(HNS3_RXD_L3L4P_B)) + hns3_rx_set_cksum_flag(first_seg, + first_seg->packet_type, + cksum_err); + + first_seg->vlan_tci = rte_le_to_cpu_16(rxd.rx.vlan_tag); + first_seg->vlan_tci_outer = + rte_le_to_cpu_16(rxd.rx.ot_vlan_tag); + rx_pkts[nb_rx++] = first_seg; + first_seg = NULL; + continue; +pkt_err: + rte_pktmbuf_free(first_seg); + first_seg = NULL; + } + + rxq->next_to_clean = rx_id; + rxq->pkt_first_seg = first_seg; + rxq->pkt_last_seg = last_seg; + + nb_rx_bd = nb_rx_bd + rxq->nb_rx_hold; + if (nb_rx_bd > rxq->rx_free_thresh) { + hns3_clean_rx_buffers(rxq, nb_rx_bd); + nb_rx_bd = 0; + } + rxq->nb_rx_hold = nb_rx_bd; + + return nb_rx; +} + +int +hns3_tx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t nb_desc, + unsigned int socket_id, const struct rte_eth_txconf *conf) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_queue_info q_info; + struct hns3_tx_queue *txq; + int tx_entry_len; + + if (dev->data->dev_started) { + hns3_err(hw, "tx_queue_setup after dev_start no supported"); + return -EINVAL; + } + + if (nb_desc > HNS3_MAX_RING_DESC || nb_desc < HNS3_MIN_RING_DESC || + nb_desc % HNS3_ALIGN_RING_DESC) { + hns3_err(hw, "Number (%u) of tx descriptors is invalid", + nb_desc); + return -EINVAL; + } + + if (dev->data->tx_queues[idx] != NULL) { + hns3_tx_queue_release(dev->data->tx_queues[idx]); + dev->data->tx_queues[idx] = NULL; + } + + q_info.idx = idx; + q_info.socket_id = socket_id; + q_info.nb_desc = nb_desc; + q_info.type = "hns3 TX queue"; + q_info.ring_name = "tx_ring"; + txq = hns3_alloc_txq_and_dma_zone(dev, &q_info); + if (txq == NULL) { + hns3_err(hw, + "Failed to alloc mem and reserve DMA mem for tx ring!"); + return -ENOMEM; + } + + txq->tx_deferred_start = conf->tx_deferred_start; + tx_entry_len = sizeof(struct hns3_entry) * txq->nb_tx_desc; + txq->sw_ring = rte_zmalloc_socket("hns3 TX sw ring", tx_entry_len, + RTE_CACHE_LINE_SIZE, socket_id); + if (txq->sw_ring == NULL) { + hns3_err(hw, "Failed to allocate memory for tx sw ring!"); + hns3_tx_queue_release(txq); + return -ENOMEM; + } + + txq->hns = hns; + txq->next_to_use = 0; + txq->next_to_clean = 0; + txq->tx_bd_ready = txq->nb_tx_desc - 1; + txq->port_id = dev->data->port_id; + txq->configured = true; + txq->io_base = (void *)((char *)hw->io_base + HNS3_TQP_REG_OFFSET + + idx * HNS3_TQP_REG_SIZE); + txq->over_length_pkt_cnt = 0; + txq->exceed_limit_bd_pkt_cnt = 0; + txq->exceed_limit_bd_reassem_fail = 0; + txq->unsupported_tunnel_pkt_cnt = 0; + txq->queue_full_cnt = 0; + txq->pkt_padding_fail_cnt = 0; + rte_spinlock_lock(&hw->lock); + dev->data->tx_queues[idx] = txq; + rte_spinlock_unlock(&hw->lock); + + return 0; +} + +static inline void +hns3_queue_xmit(struct hns3_tx_queue *txq, uint32_t buf_num) +{ + hns3_write_dev(txq, HNS3_RING_TX_TAIL_REG, buf_num); +} + +static void +hns3_tx_free_useless_buffer(struct hns3_tx_queue *txq) +{ + uint16_t tx_next_clean = txq->next_to_clean; + uint16_t tx_next_use = txq->next_to_use; + uint16_t tx_bd_ready = txq->tx_bd_ready; + uint16_t tx_bd_max = txq->nb_tx_desc; + struct hns3_entry *tx_bak_pkt = &txq->sw_ring[tx_next_clean]; + struct hns3_desc *desc = &txq->tx_ring[tx_next_clean]; + struct rte_mbuf *mbuf; + + while ((!hns3_get_bit(desc->tx.tp_fe_sc_vld_ra_ri, HNS3_TXD_VLD_B)) && + tx_next_use != tx_next_clean) { + mbuf = tx_bak_pkt->mbuf; + if (mbuf) { + rte_pktmbuf_free_seg(mbuf); + tx_bak_pkt->mbuf = NULL; + } + + desc++; + tx_bak_pkt++; + tx_next_clean++; + tx_bd_ready++; + + if (tx_next_clean >= tx_bd_max) { + tx_next_clean = 0; + desc = txq->tx_ring; + tx_bak_pkt = txq->sw_ring; + } + } + + txq->next_to_clean = tx_next_clean; + txq->tx_bd_ready = tx_bd_ready; +} + +static int +hns3_tso_proc_tunnel(struct hns3_desc *desc, uint64_t ol_flags, + struct rte_mbuf *rxm, uint8_t *l2_len) +{ + uint64_t tun_flags; + uint8_t ol4_len; + uint32_t otmp; + + tun_flags = ol_flags & PKT_TX_TUNNEL_MASK; + if (tun_flags == 0) + return 0; + + otmp = rte_le_to_cpu_32(desc->tx.ol_type_vlan_len_msec); + switch (tun_flags) { + case PKT_TX_TUNNEL_GENEVE: + case PKT_TX_TUNNEL_VXLAN: + *l2_len = rxm->l2_len - RTE_ETHER_VXLAN_HLEN; + break; + case PKT_TX_TUNNEL_GRE: + /* + * OL4 header size, defined in 4 Bytes, it contains outer + * L4(GRE) length and tunneling length. + */ + ol4_len = hns3_get_field(otmp, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S); + *l2_len = rxm->l2_len - (ol4_len << HNS3_L4_LEN_UNIT); + break; + default: + /* For non UDP / GRE tunneling, drop the tunnel packet */ + return -EINVAL; + } + hns3_set_field(otmp, HNS3_TXD_L2LEN_M, HNS3_TXD_L2LEN_S, + rxm->outer_l2_len >> HNS3_L2_LEN_UNIT); + desc->tx.ol_type_vlan_len_msec = rte_cpu_to_le_32(otmp); + + return 0; +} + +static void +hns3_set_tso(struct hns3_desc *desc, + uint64_t ol_flags, struct rte_mbuf *rxm) +{ + uint32_t paylen, hdr_len; + uint32_t tmp; + uint8_t l2_len = rxm->l2_len; + + if (!(ol_flags & PKT_TX_TCP_SEG)) + return; + + if (hns3_tso_proc_tunnel(desc, ol_flags, rxm, &l2_len)) + return; + + hdr_len = rxm->l2_len + rxm->l3_len + rxm->l4_len; + hdr_len += (ol_flags & PKT_TX_TUNNEL_MASK) ? + rxm->outer_l2_len + rxm->outer_l3_len : 0; + paylen = rxm->pkt_len - hdr_len; + if (paylen <= rxm->tso_segsz) + return; + + tmp = rte_le_to_cpu_32(desc->tx.type_cs_vlan_tso_len); + hns3_set_bit(tmp, HNS3_TXD_TSO_B, 1); + hns3_set_bit(tmp, HNS3_TXD_L3CS_B, 1); + hns3_set_field(tmp, HNS3_TXD_L4T_M, HNS3_TXD_L4T_S, HNS3_L4T_TCP); + hns3_set_bit(tmp, HNS3_TXD_L4CS_B, 1); + hns3_set_field(tmp, HNS3_TXD_L4LEN_M, HNS3_TXD_L4LEN_S, + sizeof(struct rte_tcp_hdr) >> HNS3_L4_LEN_UNIT); + hns3_set_field(tmp, HNS3_TXD_L2LEN_M, HNS3_TXD_L2LEN_S, + l2_len >> HNS3_L2_LEN_UNIT); + desc->tx.type_cs_vlan_tso_len = rte_cpu_to_le_32(tmp); + desc->tx.mss = rte_cpu_to_le_16(rxm->tso_segsz); +} + +static void +fill_desc(struct hns3_tx_queue *txq, uint16_t tx_desc_id, struct rte_mbuf *rxm, + bool first, int offset) +{ + struct hns3_desc *tx_ring = txq->tx_ring; + struct hns3_desc *desc = &tx_ring[tx_desc_id]; + uint8_t frag_end = rxm->next == NULL ? 1 : 0; + uint64_t ol_flags = rxm->ol_flags; + uint16_t size = rxm->data_len; + uint16_t rrcfv = 0; + uint32_t hdr_len; + uint32_t paylen; + uint32_t tmp; + + desc->addr = rte_mbuf_data_iova(rxm) + offset; + desc->tx.send_size = rte_cpu_to_le_16(size); + hns3_set_bit(rrcfv, HNS3_TXD_VLD_B, 1); + + if (first) { + hdr_len = rxm->l2_len + rxm->l3_len + rxm->l4_len; + hdr_len += (ol_flags & PKT_TX_TUNNEL_MASK) ? + rxm->outer_l2_len + rxm->outer_l3_len : 0; + paylen = rxm->pkt_len - hdr_len; + desc->tx.paylen = rte_cpu_to_le_32(paylen); + hns3_set_tso(desc, ol_flags, rxm); + } + + hns3_set_bit(rrcfv, HNS3_TXD_FE_B, frag_end); + desc->tx.tp_fe_sc_vld_ra_ri = rte_cpu_to_le_16(rrcfv); + + if (frag_end) { + if (ol_flags & (PKT_TX_VLAN_PKT | PKT_TX_QINQ_PKT)) { + tmp = rte_le_to_cpu_32(desc->tx.type_cs_vlan_tso_len); + hns3_set_bit(tmp, HNS3_TXD_VLAN_B, 1); + desc->tx.type_cs_vlan_tso_len = rte_cpu_to_le_32(tmp); + desc->tx.vlan_tag = rte_cpu_to_le_16(rxm->vlan_tci); + } + + if (ol_flags & PKT_TX_QINQ_PKT) { + tmp = rte_le_to_cpu_32(desc->tx.ol_type_vlan_len_msec); + hns3_set_bit(tmp, HNS3_TXD_OVLAN_B, 1); + desc->tx.ol_type_vlan_len_msec = rte_cpu_to_le_32(tmp); + desc->tx.outer_vlan_tag = + rte_cpu_to_le_16(rxm->vlan_tci_outer); + } + } +} + +static int +hns3_tx_alloc_mbufs(struct hns3_tx_queue *txq, struct rte_mempool *mb_pool, + uint16_t nb_new_buf, struct rte_mbuf **alloc_mbuf) +{ + struct rte_mbuf *new_mbuf = NULL; + struct rte_eth_dev *dev; + struct rte_mbuf *temp; + struct hns3_hw *hw; + uint16_t i; + + /* Allocate enough mbufs */ + for (i = 0; i < nb_new_buf; i++) { + temp = rte_pktmbuf_alloc(mb_pool); + if (unlikely(temp == NULL)) { + dev = &rte_eth_devices[txq->port_id]; + hw = HNS3_DEV_PRIVATE_TO_HW(dev->data->dev_private); + hns3_err(hw, "Failed to alloc TX mbuf port_id=%d," + "queue_id=%d in reassemble tx pkts.", + txq->port_id, txq->queue_id); + rte_pktmbuf_free(new_mbuf); + return -ENOMEM; + } + temp->next = new_mbuf; + new_mbuf = temp; + } + + if (new_mbuf == NULL) + return -ENOMEM; + + new_mbuf->nb_segs = nb_new_buf; + *alloc_mbuf = new_mbuf; + + return 0; +} + +static int +hns3_reassemble_tx_pkts(void *tx_queue, struct rte_mbuf *tx_pkt, + struct rte_mbuf **new_pkt) +{ + struct hns3_tx_queue *txq = tx_queue; + struct rte_mempool *mb_pool; + struct rte_mbuf *new_mbuf; + struct rte_mbuf *temp_new; + struct rte_mbuf *temp; + uint16_t last_buf_len; + uint16_t nb_new_buf; + uint16_t buf_size; + uint16_t buf_len; + uint16_t len_s; + uint16_t len_d; + uint16_t len; + uint16_t i; + int ret; + char *s; + char *d; + + mb_pool = tx_pkt->pool; + buf_size = tx_pkt->buf_len - RTE_PKTMBUF_HEADROOM; + nb_new_buf = (tx_pkt->pkt_len - 1) / buf_size + 1; + + last_buf_len = tx_pkt->pkt_len % buf_size; + if (last_buf_len == 0) + last_buf_len = buf_size; + + /* Allocate enough mbufs */ + ret = hns3_tx_alloc_mbufs(txq, mb_pool, nb_new_buf, &new_mbuf); + if (ret) + return ret; + + /* Copy the original packet content to the new mbufs */ + temp = tx_pkt; + s = rte_pktmbuf_mtod(temp, char *); + len_s = temp->data_len; + temp_new = new_mbuf; + for (i = 0; i < nb_new_buf; i++) { + d = rte_pktmbuf_mtod(temp_new, char *); + if (i < nb_new_buf - 1) + buf_len = buf_size; + else + buf_len = last_buf_len; + len_d = buf_len; + + while (len_d) { + len = RTE_MIN(len_s, len_d); + memcpy(d, s, len); + s = s + len; + d = d + len; + len_d = len_d - len; + len_s = len_s - len; + + if (len_s == 0) { + temp = temp->next; + if (temp == NULL) + break; + s = rte_pktmbuf_mtod(temp, char *); + len_s = temp->data_len; + } + } + + temp_new->data_len = buf_len; + temp_new = temp_new->next; + } + + /* free original mbufs */ + rte_pktmbuf_free(tx_pkt); + + *new_pkt = new_mbuf; + + return 0; +} + +static void +hns3_parse_outer_params(uint64_t ol_flags, uint32_t *ol_type_vlan_len_msec) +{ + uint32_t tmp = *ol_type_vlan_len_msec; + + /* (outer) IP header type */ + if (ol_flags & PKT_TX_OUTER_IPV4) { + /* OL3 header size, defined in 4 bytes */ + hns3_set_field(tmp, HNS3_TXD_L3LEN_M, HNS3_TXD_L3LEN_S, + sizeof(struct rte_ipv4_hdr) >> HNS3_L3_LEN_UNIT); + if (ol_flags & PKT_TX_OUTER_IP_CKSUM) + hns3_set_field(tmp, HNS3_TXD_OL3T_M, + HNS3_TXD_OL3T_S, HNS3_OL3T_IPV4_CSUM); + else + hns3_set_field(tmp, HNS3_TXD_OL3T_M, HNS3_TXD_OL3T_S, + HNS3_OL3T_IPV4_NO_CSUM); + } else if (ol_flags & PKT_TX_OUTER_IPV6) { + hns3_set_field(tmp, HNS3_TXD_OL3T_M, HNS3_TXD_OL3T_S, + HNS3_OL3T_IPV6); + /* OL3 header size, defined in 4 bytes */ + hns3_set_field(tmp, HNS3_TXD_L3LEN_M, HNS3_TXD_L3LEN_S, + sizeof(struct rte_ipv6_hdr) >> HNS3_L3_LEN_UNIT); + } + + *ol_type_vlan_len_msec = tmp; +} + +static int +hns3_parse_inner_params(uint64_t ol_flags, uint32_t *ol_type_vlan_len_msec, + struct rte_net_hdr_lens *hdr_lens) +{ + uint32_t tmp = *ol_type_vlan_len_msec; + uint8_t l4_len; + + /* OL2 header size, defined in 2 bytes */ + hns3_set_field(tmp, HNS3_TXD_L2LEN_M, HNS3_TXD_L2LEN_S, + sizeof(struct rte_ether_hdr) >> HNS3_L2_LEN_UNIT); + + /* L4TUNT: L4 Tunneling Type */ + switch (ol_flags & PKT_TX_TUNNEL_MASK) { + case PKT_TX_TUNNEL_GENEVE: + case PKT_TX_TUNNEL_VXLAN: + /* MAC in UDP tunnelling packet, include VxLAN */ + hns3_set_field(tmp, HNS3_TXD_TUNTYPE_M, HNS3_TXD_TUNTYPE_S, + HNS3_TUN_MAC_IN_UDP); + /* + * OL4 header size, defined in 4 Bytes, it contains outer + * L4(UDP) length and tunneling length. + */ + hns3_set_field(tmp, HNS3_TXD_L4LEN_M, HNS3_TXD_L4LEN_S, + (uint8_t)RTE_ETHER_VXLAN_HLEN >> + HNS3_L4_LEN_UNIT); + break; + case PKT_TX_TUNNEL_GRE: + hns3_set_field(tmp, HNS3_TXD_TUNTYPE_M, HNS3_TXD_TUNTYPE_S, + HNS3_TUN_NVGRE); + /* + * OL4 header size, defined in 4 Bytes, it contains outer + * L4(GRE) length and tunneling length. + */ + l4_len = hdr_lens->l4_len + hdr_lens->tunnel_len; + hns3_set_field(tmp, HNS3_TXD_L4LEN_M, HNS3_TXD_L4LEN_S, + l4_len >> HNS3_L4_LEN_UNIT); + break; + default: + /* For non UDP / GRE tunneling, drop the tunnel packet */ + return -EINVAL; + } + + *ol_type_vlan_len_msec = tmp; + + return 0; +} + +static int +hns3_parse_tunneling_params(struct hns3_tx_queue *txq, uint16_t tx_desc_id, + uint64_t ol_flags, + struct rte_net_hdr_lens *hdr_lens) +{ + struct hns3_desc *tx_ring = txq->tx_ring; + struct hns3_desc *desc = &tx_ring[tx_desc_id]; + uint32_t value = 0; + int ret; + + hns3_parse_outer_params(ol_flags, &value); + ret = hns3_parse_inner_params(ol_flags, &value, hdr_lens); + if (ret) + return -EINVAL; + + desc->tx.ol_type_vlan_len_msec |= rte_cpu_to_le_32(value); + + return 0; +} + +static void +hns3_parse_l3_cksum_params(uint64_t ol_flags, uint32_t *type_cs_vlan_tso_len) +{ + uint32_t tmp; + + /* Enable L3 checksum offloads */ + if (ol_flags & PKT_TX_IPV4) { + tmp = *type_cs_vlan_tso_len; + hns3_set_field(tmp, HNS3_TXD_L3T_M, HNS3_TXD_L3T_S, + HNS3_L3T_IPV4); + /* inner(/normal) L3 header size, defined in 4 bytes */ + hns3_set_field(tmp, HNS3_TXD_L3LEN_M, HNS3_TXD_L3LEN_S, + sizeof(struct rte_ipv4_hdr) >> HNS3_L3_LEN_UNIT); + if (ol_flags & PKT_TX_IP_CKSUM) + hns3_set_bit(tmp, HNS3_TXD_L3CS_B, 1); + *type_cs_vlan_tso_len = tmp; + } else if (ol_flags & PKT_TX_IPV6) { + tmp = *type_cs_vlan_tso_len; + /* L3T, IPv6 don't do checksum */ + hns3_set_field(tmp, HNS3_TXD_L3T_M, HNS3_TXD_L3T_S, + HNS3_L3T_IPV6); + /* inner(/normal) L3 header size, defined in 4 bytes */ + hns3_set_field(tmp, HNS3_TXD_L3LEN_M, HNS3_TXD_L3LEN_S, + sizeof(struct rte_ipv6_hdr) >> HNS3_L3_LEN_UNIT); + *type_cs_vlan_tso_len = tmp; + } +} + +static void +hns3_parse_l4_cksum_params(uint64_t ol_flags, uint32_t *type_cs_vlan_tso_len) +{ + uint32_t tmp; + + /* Enable L4 checksum offloads */ + switch (ol_flags & PKT_TX_L4_MASK) { + case PKT_TX_TCP_CKSUM: + tmp = *type_cs_vlan_tso_len; + hns3_set_field(tmp, HNS3_TXD_L4T_M, HNS3_TXD_L4T_S, + HNS3_L4T_TCP); + hns3_set_bit(tmp, HNS3_TXD_L4CS_B, 1); + hns3_set_field(tmp, HNS3_TXD_L4LEN_M, HNS3_TXD_L4LEN_S, + sizeof(struct rte_tcp_hdr) >> HNS3_L4_LEN_UNIT); + *type_cs_vlan_tso_len = tmp; + break; + case PKT_TX_UDP_CKSUM: + tmp = *type_cs_vlan_tso_len; + hns3_set_field(tmp, HNS3_TXD_L4T_M, HNS3_TXD_L4T_S, + HNS3_L4T_UDP); + hns3_set_bit(tmp, HNS3_TXD_L4CS_B, 1); + hns3_set_field(tmp, HNS3_TXD_L4LEN_M, HNS3_TXD_L4LEN_S, + sizeof(struct rte_udp_hdr) >> HNS3_L4_LEN_UNIT); + *type_cs_vlan_tso_len = tmp; + break; + case PKT_TX_SCTP_CKSUM: + tmp = *type_cs_vlan_tso_len; + hns3_set_field(tmp, HNS3_TXD_L4T_M, HNS3_TXD_L4T_S, + HNS3_L4T_SCTP); + hns3_set_bit(tmp, HNS3_TXD_L4CS_B, 1); + hns3_set_field(tmp, HNS3_TXD_L4LEN_M, HNS3_TXD_L4LEN_S, + sizeof(struct rte_sctp_hdr) >> HNS3_L4_LEN_UNIT); + *type_cs_vlan_tso_len = tmp; + break; + default: + break; + } +} + +static void +hns3_txd_enable_checksum(struct hns3_tx_queue *txq, uint16_t tx_desc_id, + uint64_t ol_flags) +{ + struct hns3_desc *tx_ring = txq->tx_ring; + struct hns3_desc *desc = &tx_ring[tx_desc_id]; + uint32_t value = 0; + + /* inner(/normal) L2 header size, defined in 2 bytes */ + hns3_set_field(value, HNS3_TXD_L2LEN_M, HNS3_TXD_L2LEN_S, + sizeof(struct rte_ether_hdr) >> HNS3_L2_LEN_UNIT); + + hns3_parse_l3_cksum_params(ol_flags, &value); + hns3_parse_l4_cksum_params(ol_flags, &value); + + desc->tx.type_cs_vlan_tso_len |= rte_cpu_to_le_32(value); +} + +static bool +hns3_pkt_need_linearized(struct rte_mbuf *tx_pkts, uint32_t bd_num) +{ + struct rte_mbuf *m_first = tx_pkts; + struct rte_mbuf *m_last = tx_pkts; + uint32_t tot_len = 0; + uint32_t hdr_len; + uint32_t i; + + /* + * Hardware requires that the sum of the data length of every 8 + * consecutive buffers is greater than MSS in hns3 network engine. + * We simplify it by ensuring pkt_headlen + the first 8 consecutive + * frags greater than gso header len + mss, and the remaining 7 + * consecutive frags greater than MSS except the last 7 frags. + */ + if (bd_num <= HNS3_MAX_NON_TSO_BD_PER_PKT) + return false; + + for (i = 0; m_last && i < HNS3_MAX_NON_TSO_BD_PER_PKT - 1; + i++, m_last = m_last->next) + tot_len += m_last->data_len; + + if (!m_last) + return true; + + /* ensure the first 8 frags is greater than mss + header */ + hdr_len = tx_pkts->l2_len + tx_pkts->l3_len + tx_pkts->l4_len; + hdr_len += (tx_pkts->ol_flags & PKT_TX_TUNNEL_MASK) ? + tx_pkts->outer_l2_len + tx_pkts->outer_l3_len : 0; + if (tot_len + m_last->data_len < tx_pkts->tso_segsz + hdr_len) + return true; + + /* + * ensure the sum of the data length of every 7 consecutive buffer + * is greater than mss except the last one. + */ + for (i = 0; m_last && i < bd_num - HNS3_MAX_NON_TSO_BD_PER_PKT; i++) { + tot_len -= m_first->data_len; + tot_len += m_last->data_len; + + if (tot_len < tx_pkts->tso_segsz) + return true; + + m_first = m_first->next; + m_last = m_last->next; + } + + return false; +} + +static void +hns3_outer_header_cksum_prepare(struct rte_mbuf *m) +{ + uint64_t ol_flags = m->ol_flags; + struct rte_ipv4_hdr *ipv4_hdr; + struct rte_udp_hdr *udp_hdr; + uint32_t paylen, hdr_len; + + if (!(ol_flags & (PKT_TX_OUTER_IPV4 | PKT_TX_OUTER_IPV6))) + return; + + if (ol_flags & PKT_TX_IPV4) { + ipv4_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, + m->outer_l2_len); + + if (ol_flags & PKT_TX_IP_CKSUM) + ipv4_hdr->hdr_checksum = 0; + } + + if ((ol_flags & PKT_TX_L4_MASK) == PKT_TX_UDP_CKSUM && + ol_flags & PKT_TX_TCP_SEG) { + hdr_len = m->l2_len + m->l3_len + m->l4_len; + hdr_len += (ol_flags & PKT_TX_TUNNEL_MASK) ? + m->outer_l2_len + m->outer_l3_len : 0; + paylen = m->pkt_len - hdr_len; + if (paylen <= m->tso_segsz) + return; + udp_hdr = rte_pktmbuf_mtod_offset(m, struct rte_udp_hdr *, + m->outer_l2_len + + m->outer_l3_len); + udp_hdr->dgram_cksum = 0; + } +} + +static inline bool +hns3_pkt_is_tso(struct rte_mbuf *m) +{ + return (m->tso_segsz != 0 && m->ol_flags & PKT_TX_TCP_SEG); +} + +static int +hns3_check_tso_pkt_valid(struct rte_mbuf *m) +{ + uint32_t tmp_data_len_sum = 0; + uint16_t nb_buf = m->nb_segs; + uint32_t paylen, hdr_len; + struct rte_mbuf *m_seg; + int i; + + if (nb_buf > HNS3_MAX_TSO_BD_PER_PKT) + return -EINVAL; + + hdr_len = m->l2_len + m->l3_len + m->l4_len; + hdr_len += (m->ol_flags & PKT_TX_TUNNEL_MASK) ? + m->outer_l2_len + m->outer_l3_len : 0; + if (hdr_len > HNS3_MAX_TSO_HDR_SIZE) + return -EINVAL; + + paylen = m->pkt_len - hdr_len; + if (paylen > HNS3_MAX_BD_PAYLEN) + return -EINVAL; + + /* + * The TSO header (include outer and inner L2, L3 and L4 header) + * should be provided by three descriptors in maximum in hns3 network + * engine. + */ + m_seg = m; + for (i = 0; m_seg != NULL && i < HNS3_MAX_TSO_HDR_BD_NUM && i < nb_buf; + i++, m_seg = m_seg->next) { + tmp_data_len_sum += m_seg->data_len; + } + + if (hdr_len > tmp_data_len_sum) + return -EINVAL; + + return 0; +} + +uint16_t +hns3_prep_pkts(__rte_unused void *tx_queue, struct rte_mbuf **tx_pkts, + uint16_t nb_pkts) +{ + struct rte_mbuf *m; + uint16_t i; + int ret; + + for (i = 0; i < nb_pkts; i++) { + m = tx_pkts[i]; + + /* check the size of packet */ + if (m->pkt_len < RTE_ETHER_MIN_LEN) { + rte_errno = EINVAL; + return i; + } + + if (hns3_pkt_is_tso(m) && + (hns3_pkt_need_linearized(m, m->nb_segs) || + hns3_check_tso_pkt_valid(m))) { + rte_errno = EINVAL; + return i; + } + +#ifdef RTE_LIBRTE_ETHDEV_DEBUG + ret = rte_validate_tx_offload(m); + if (ret != 0) { + rte_errno = -ret; + return i; + } +#endif + ret = rte_net_intel_cksum_prepare(m); + if (ret != 0) { + rte_errno = -ret; + return i; + } + + hns3_outer_header_cksum_prepare(m); + } + + return i; +} + +static int +hns3_parse_cksum(struct hns3_tx_queue *txq, uint16_t tx_desc_id, + const struct rte_mbuf *m, struct rte_net_hdr_lens *hdr_lens) +{ + /* Fill in tunneling parameters if necessary */ + if (m->ol_flags & PKT_TX_TUNNEL_MASK) { + (void)rte_net_get_ptype(m, hdr_lens, RTE_PTYPE_ALL_MASK); + if (hns3_parse_tunneling_params(txq, tx_desc_id, m->ol_flags, + hdr_lens)) { + txq->unsupported_tunnel_pkt_cnt++; + return -EINVAL; + } + } + /* Enable checksum offloading */ + if (m->ol_flags & HNS3_TX_CKSUM_OFFLOAD_MASK) + hns3_txd_enable_checksum(txq, tx_desc_id, m->ol_flags); + + return 0; +} + +static int +hns3_check_non_tso_pkt(uint16_t nb_buf, struct rte_mbuf **m_seg, + struct rte_mbuf *tx_pkt, struct hns3_tx_queue *txq) +{ + struct rte_mbuf *new_pkt; + int ret; + + if (hns3_pkt_is_tso(*m_seg)) + return 0; + + /* + * If packet length is greater than HNS3_MAX_FRAME_LEN + * driver support, the packet will be ignored. + */ + if (unlikely(rte_pktmbuf_pkt_len(tx_pkt) > HNS3_MAX_FRAME_LEN)) { + txq->over_length_pkt_cnt++; + return -EINVAL; + } + + if (unlikely(nb_buf > HNS3_MAX_NON_TSO_BD_PER_PKT)) { + txq->exceed_limit_bd_pkt_cnt++; + ret = hns3_reassemble_tx_pkts(txq, tx_pkt, &new_pkt); + if (ret) { + txq->exceed_limit_bd_reassem_fail++; + return ret; + } + *m_seg = new_pkt; + } + + return 0; +} + +uint16_t +hns3_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts) +{ + struct rte_net_hdr_lens hdr_lens = {0}; + struct hns3_tx_queue *txq = tx_queue; + struct hns3_entry *tx_bak_pkt; + struct rte_mbuf *tx_pkt; + struct rte_mbuf *m_seg; + uint32_t nb_hold = 0; + uint16_t tx_next_use; + uint16_t tx_pkt_num; + uint16_t tx_bd_max; + uint16_t nb_buf; + uint16_t nb_tx; + uint16_t i; + + /* free useless buffer */ + hns3_tx_free_useless_buffer(txq); + + tx_next_use = txq->next_to_use; + tx_bd_max = txq->nb_tx_desc; + tx_pkt_num = nb_pkts; + + /* send packets */ + tx_bak_pkt = &txq->sw_ring[tx_next_use]; + for (nb_tx = 0; nb_tx < tx_pkt_num; nb_tx++) { + tx_pkt = *tx_pkts++; + + nb_buf = tx_pkt->nb_segs; + + if (nb_buf > txq->tx_bd_ready) { + txq->queue_full_cnt++; + if (nb_tx == 0) + return 0; + + goto end_of_tx; + } + + /* + * If packet length is less than minimum packet size, driver + * need to pad it. + */ + if (unlikely(rte_pktmbuf_pkt_len(tx_pkt) < HNS3_MIN_PKT_SIZE)) { + uint16_t add_len; + char *appended; + + add_len = HNS3_MIN_PKT_SIZE - + rte_pktmbuf_pkt_len(tx_pkt); + appended = rte_pktmbuf_append(tx_pkt, add_len); + if (appended == NULL) { + txq->pkt_padding_fail_cnt++; + break; + } + + memset(appended, 0, add_len); + } + + m_seg = tx_pkt; + + if (hns3_check_non_tso_pkt(nb_buf, &m_seg, tx_pkt, txq)) + goto end_of_tx; + + if (hns3_parse_cksum(txq, tx_next_use, m_seg, &hdr_lens)) + goto end_of_tx; + + i = 0; + do { + fill_desc(txq, tx_next_use, m_seg, (i == 0), 0); + tx_bak_pkt->mbuf = m_seg; + m_seg = m_seg->next; + tx_next_use++; + tx_bak_pkt++; + if (tx_next_use >= tx_bd_max) { + tx_next_use = 0; + tx_bak_pkt = txq->sw_ring; + } + + i++; + } while (m_seg != NULL); + + nb_hold += i; + txq->next_to_use = tx_next_use; + txq->tx_bd_ready -= i; + } + +end_of_tx: + + if (likely(nb_tx)) + hns3_queue_xmit(txq, nb_hold); + + return nb_tx; +} + +static uint16_t +hns3_dummy_rxtx_burst(void *dpdk_txq __rte_unused, + struct rte_mbuf **pkts __rte_unused, + uint16_t pkts_n __rte_unused) +{ + return 0; +} + +void hns3_set_rxtx_function(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + + if (hns->hw.adapter_state == HNS3_NIC_STARTED && + rte_atomic16_read(&hns->hw.reset.resetting) == 0) { + eth_dev->rx_pkt_burst = hns3_recv_pkts; + eth_dev->tx_pkt_burst = hns3_xmit_pkts; + eth_dev->tx_pkt_prepare = hns3_prep_pkts; + } else { + eth_dev->rx_pkt_burst = hns3_dummy_rxtx_burst; + eth_dev->tx_pkt_burst = hns3_dummy_rxtx_burst; + eth_dev->tx_pkt_prepare = hns3_dummy_rxtx_burst; + } +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_rxtx.h b/src/spdk/dpdk/drivers/net/hns3/hns3_rxtx.h new file mode 100644 index 000000000..0cb92ce9b --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_rxtx.h @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_RXTX_H_ +#define _HNS3_RXTX_H_ + +#define HNS3_MIN_RING_DESC 64 +#define HNS3_MAX_RING_DESC 32768 +#define HNS3_DEFAULT_RING_DESC 1024 +#define HNS3_ALIGN_RING_DESC 32 +#define HNS3_RING_BASE_ALIGN 128 + +#define HNS3_BD_SIZE_512_TYPE 0 +#define HNS3_BD_SIZE_1024_TYPE 1 +#define HNS3_BD_SIZE_2048_TYPE 2 +#define HNS3_BD_SIZE_4096_TYPE 3 + +#define HNS3_RX_FLAG_VLAN_PRESENT 0x1 +#define HNS3_RX_FLAG_L3ID_IPV4 0x0 +#define HNS3_RX_FLAG_L3ID_IPV6 0x1 +#define HNS3_RX_FLAG_L4ID_UDP 0x0 +#define HNS3_RX_FLAG_L4ID_TCP 0x1 + +#define HNS3_RXD_DMAC_S 0 +#define HNS3_RXD_DMAC_M (0x3 << HNS3_RXD_DMAC_S) +#define HNS3_RXD_VLAN_S 2 +#define HNS3_RXD_VLAN_M (0x3 << HNS3_RXD_VLAN_S) +#define HNS3_RXD_L3ID_S 4 +#define HNS3_RXD_L3ID_M (0xf << HNS3_RXD_L3ID_S) +#define HNS3_RXD_L4ID_S 8 +#define HNS3_RXD_L4ID_M (0xf << HNS3_RXD_L4ID_S) +#define HNS3_RXD_FRAG_B 12 +#define HNS3_RXD_STRP_TAGP_S 13 +#define HNS3_RXD_STRP_TAGP_M (0x3 << HNS3_RXD_STRP_TAGP_S) + +#define HNS3_RXD_L2E_B 16 +#define HNS3_RXD_L3E_B 17 +#define HNS3_RXD_L4E_B 18 +#define HNS3_RXD_TRUNCAT_B 19 +#define HNS3_RXD_HOI_B 20 +#define HNS3_RXD_DOI_B 21 +#define HNS3_RXD_OL3E_B 22 +#define HNS3_RXD_OL4E_B 23 +#define HNS3_RXD_GRO_COUNT_S 24 +#define HNS3_RXD_GRO_COUNT_M (0x3f << HNS3_RXD_GRO_COUNT_S) +#define HNS3_RXD_GRO_FIXID_B 30 +#define HNS3_RXD_GRO_ECN_B 31 + +#define HNS3_RXD_ODMAC_S 0 +#define HNS3_RXD_ODMAC_M (0x3 << HNS3_RXD_ODMAC_S) +#define HNS3_RXD_OVLAN_S 2 +#define HNS3_RXD_OVLAN_M (0x3 << HNS3_RXD_OVLAN_S) +#define HNS3_RXD_OL3ID_S 4 +#define HNS3_RXD_OL3ID_M (0xf << HNS3_RXD_OL3ID_S) +#define HNS3_RXD_OL4ID_S 8 +#define HNS3_RXD_OL4ID_M (0xf << HNS3_RXD_OL4ID_S) +#define HNS3_RXD_FBHI_S 12 +#define HNS3_RXD_FBHI_M (0x3 << HNS3_RXD_FBHI_S) +#define HNS3_RXD_FBLI_S 14 +#define HNS3_RXD_FBLI_M (0x3 << HNS3_RXD_FBLI_S) + +#define HNS3_RXD_BDTYPE_S 0 +#define HNS3_RXD_BDTYPE_M (0xf << HNS3_RXD_BDTYPE_S) +#define HNS3_RXD_VLD_B 4 +#define HNS3_RXD_UDP0_B 5 +#define HNS3_RXD_EXTEND_B 7 +#define HNS3_RXD_FE_B 8 +#define HNS3_RXD_LUM_B 9 +#define HNS3_RXD_CRCP_B 10 +#define HNS3_RXD_L3L4P_B 11 +#define HNS3_RXD_TSIND_S 12 +#define HNS3_RXD_TSIND_M (0x7 << HNS3_RXD_TSIND_S) +#define HNS3_RXD_LKBK_B 15 +#define HNS3_RXD_GRO_SIZE_S 16 +#define HNS3_RXD_GRO_SIZE_M (0x3ff << HNS3_RXD_GRO_SIZE_S) + +#define HNS3_TXD_L3T_S 0 +#define HNS3_TXD_L3T_M (0x3 << HNS3_TXD_L3T_S) +#define HNS3_TXD_L4T_S 2 +#define HNS3_TXD_L4T_M (0x3 << HNS3_TXD_L4T_S) +#define HNS3_TXD_L3CS_B 4 +#define HNS3_TXD_L4CS_B 5 +#define HNS3_TXD_VLAN_B 6 +#define HNS3_TXD_TSO_B 7 + +#define HNS3_TXD_L2LEN_S 8 +#define HNS3_TXD_L2LEN_M (0xff << HNS3_TXD_L2LEN_S) +#define HNS3_TXD_L3LEN_S 16 +#define HNS3_TXD_L3LEN_M (0xff << HNS3_TXD_L3LEN_S) +#define HNS3_TXD_L4LEN_S 24 +#define HNS3_TXD_L4LEN_M (0xffUL << HNS3_TXD_L4LEN_S) + +#define HNS3_TXD_OL3T_S 0 +#define HNS3_TXD_OL3T_M (0x3 << HNS3_TXD_OL3T_S) +#define HNS3_TXD_OVLAN_B 2 +#define HNS3_TXD_MACSEC_B 3 +#define HNS3_TXD_TUNTYPE_S 4 +#define HNS3_TXD_TUNTYPE_M (0xf << HNS3_TXD_TUNTYPE_S) + +#define HNS3_TXD_BDTYPE_S 0 +#define HNS3_TXD_BDTYPE_M (0xf << HNS3_TXD_BDTYPE_S) +#define HNS3_TXD_FE_B 4 +#define HNS3_TXD_SC_S 5 +#define HNS3_TXD_SC_M (0x3 << HNS3_TXD_SC_S) +#define HNS3_TXD_EXTEND_B 7 +#define HNS3_TXD_VLD_B 8 +#define HNS3_TXD_RI_B 9 +#define HNS3_TXD_RA_B 10 +#define HNS3_TXD_TSYN_B 11 +#define HNS3_TXD_DECTTL_S 12 +#define HNS3_TXD_DECTTL_M (0xf << HNS3_TXD_DECTTL_S) + +#define HNS3_TXD_MSS_S 0 +#define HNS3_TXD_MSS_M (0x3fff << HNS3_TXD_MSS_S) + +#define HNS3_L2_LEN_UNIT 1UL +#define HNS3_L3_LEN_UNIT 2UL +#define HNS3_L4_LEN_UNIT 2UL + +enum hns3_pkt_l2t_type { + HNS3_L2_TYPE_UNICAST, + HNS3_L2_TYPE_MULTICAST, + HNS3_L2_TYPE_BROADCAST, + HNS3_L2_TYPE_INVALID, +}; + +enum hns3_pkt_l3t_type { + HNS3_L3T_NONE, + HNS3_L3T_IPV6, + HNS3_L3T_IPV4, + HNS3_L3T_RESERVED +}; + +enum hns3_pkt_l4t_type { + HNS3_L4T_UNKNOWN, + HNS3_L4T_TCP, + HNS3_L4T_UDP, + HNS3_L4T_SCTP +}; + +enum hns3_pkt_ol3t_type { + HNS3_OL3T_NONE, + HNS3_OL3T_IPV6, + HNS3_OL3T_IPV4_NO_CSUM, + HNS3_OL3T_IPV4_CSUM +}; + +enum hns3_pkt_tun_type { + HNS3_TUN_NONE, + HNS3_TUN_MAC_IN_UDP, + HNS3_TUN_NVGRE, + HNS3_TUN_OTHER +}; + +/* hardware spec ring buffer format */ +struct hns3_desc { + union { + uint64_t addr; + struct { + uint32_t addr0; + uint32_t addr1; + }; + }; + union { + struct { + uint16_t vlan_tag; + uint16_t send_size; + union { + /* + * L3T | L4T | L3CS | L4CS | VLAN | TSO | + * L2_LEN + */ + uint32_t type_cs_vlan_tso_len; + struct { + uint8_t type_cs_vlan_tso; + uint8_t l2_len; + uint8_t l3_len; + uint8_t l4_len; + }; + }; + uint16_t outer_vlan_tag; + uint16_t tv; + union { + /* OL3T | OVALAN | MACSEC */ + uint32_t ol_type_vlan_len_msec; + struct { + uint8_t ol_type_vlan_msec; + uint8_t ol2_len; + uint8_t ol3_len; + uint8_t ol4_len; + }; + }; + + uint32_t paylen; + uint16_t tp_fe_sc_vld_ra_ri; + uint16_t mss; + } tx; + + struct { + uint32_t l234_info; + uint16_t pkt_len; + uint16_t size; + uint32_t rss_hash; + uint16_t fd_id; + uint16_t vlan_tag; + union { + uint32_t ol_info; + struct { + uint16_t o_dm_vlan_id_fb; + uint16_t ot_vlan_tag; + }; + }; + uint32_t bd_base_info; + } rx; + }; +} __rte_packed; + +struct hns3_entry { + struct rte_mbuf *mbuf; +}; + +struct hns3_rx_queue { + void *io_base; + struct hns3_adapter *hns; + struct rte_mempool *mb_pool; + struct hns3_desc *rx_ring; + uint64_t rx_ring_phys_addr; /* RX ring DMA address */ + const struct rte_memzone *mz; + struct hns3_entry *sw_ring; + + struct rte_mbuf *pkt_first_seg; + struct rte_mbuf *pkt_last_seg; + + uint16_t queue_id; + uint16_t port_id; + uint16_t nb_rx_desc; + uint16_t nb_rx_hold; + uint16_t rx_tail; + uint16_t next_to_clean; + uint16_t next_to_use; + uint16_t rx_buf_len; + uint16_t rx_free_thresh; + + bool rx_deferred_start; /* don't start this queue in dev start */ + bool configured; /* indicate if rx queue has been configured */ + + uint64_t l2_errors; + uint64_t pkt_len_errors; + uint64_t l3_csum_erros; + uint64_t l4_csum_erros; + uint64_t ol3_csum_erros; + uint64_t ol4_csum_erros; +}; + +struct hns3_tx_queue { + void *io_base; + struct hns3_adapter *hns; + struct hns3_desc *tx_ring; + uint64_t tx_ring_phys_addr; /* TX ring DMA address */ + const struct rte_memzone *mz; + struct hns3_entry *sw_ring; + + uint16_t queue_id; + uint16_t port_id; + uint16_t nb_tx_desc; + uint16_t next_to_clean; + uint16_t next_to_use; + uint16_t tx_bd_ready; + + bool tx_deferred_start; /* don't start this queue in dev start */ + bool configured; /* indicate if tx queue has been configured */ + + /* + * The following items are used for the abnormal errors statistics in + * the Tx datapath. When upper level application calls the + * rte_eth_tx_burst API function to send multiple packets at a time with + * burst mode based on hns3 network engine, there are some abnormal + * conditions that cause the driver to fail to operate the hardware to + * send packets correctly. + * Note: When using burst mode to call the rte_eth_tx_burst API function + * to send multiple packets at a time. When the first abnormal error is + * detected, add one to the relevant error statistics item, and then + * exit the loop of sending multiple packets of the function. That is to + * say, even if there are multiple packets in which abnormal errors may + * be detected in the burst, the relevant error statistics in the driver + * will only be increased by one. + * The detail description of the Tx abnormal errors statistic items as + * below: + * - over_length_pkt_cnt + * Total number of greater than HNS3_MAX_FRAME_LEN the driver + * supported. + * + * - exceed_limit_bd_pkt_cnt + * Total number of exceeding the hardware limited bd which process + * a packet needed bd numbers. + * + * - exceed_limit_bd_reassem_fail + * Total number of exceeding the hardware limited bd fail which + * process a packet needed bd numbers and reassemble fail. + * + * - unsupported_tunnel_pkt_cnt + * Total number of unsupported tunnel packet. The unsupported tunnel + * type: vxlan_gpe, gtp, ipip and MPLSINUDP, MPLSINUDP is a packet + * with MPLS-in-UDP RFC 7510 header. + * + * - queue_full_cnt + * Total count which the available bd numbers in current bd queue is + * less than the bd numbers with the pkt process needed. + * + * - pkt_padding_fail_cnt + * Total count which the packet length is less than minimum packet + * size HNS3_MIN_PKT_SIZE and fail to be appended with 0. + */ + uint64_t over_length_pkt_cnt; + uint64_t exceed_limit_bd_pkt_cnt; + uint64_t exceed_limit_bd_reassem_fail; + uint64_t unsupported_tunnel_pkt_cnt; + uint64_t queue_full_cnt; + uint64_t pkt_padding_fail_cnt; +}; + +struct hns3_queue_info { + const char *type; /* point to queue memory name */ + const char *ring_name; /* point to hardware ring name */ + uint16_t idx; + uint16_t nb_desc; + unsigned int socket_id; +}; + +#define HNS3_TX_CKSUM_OFFLOAD_MASK ( \ + PKT_TX_OUTER_IPV6 | \ + PKT_TX_OUTER_IPV4 | \ + PKT_TX_OUTER_IP_CKSUM | \ + PKT_TX_IPV6 | \ + PKT_TX_IPV4 | \ + PKT_TX_IP_CKSUM | \ + PKT_TX_L4_MASK | \ + PKT_TX_TUNNEL_MASK) + +enum hns3_cksum_status { + HNS3_CKSUM_NONE = 0, + HNS3_L3_CKSUM_ERR = 1, + HNS3_L4_CKSUM_ERR = 2, + HNS3_OUTER_L3_CKSUM_ERR = 4, + HNS3_OUTER_L4_CKSUM_ERR = 8 +}; + +void hns3_dev_rx_queue_release(void *queue); +void hns3_dev_tx_queue_release(void *queue); +void hns3_free_all_queues(struct rte_eth_dev *dev); +int hns3_reset_all_queues(struct hns3_adapter *hns); +void hns3_dev_all_rx_queue_intr_enable(struct hns3_hw *hw, bool en); +int hns3_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id); +int hns3_dev_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id); +void hns3_enable_all_queues(struct hns3_hw *hw, bool en); +int hns3_start_queues(struct hns3_adapter *hns, bool reset_queue); +int hns3_stop_queues(struct hns3_adapter *hns, bool reset_queue); +void hns3_dev_release_mbufs(struct hns3_adapter *hns); +int hns3_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t nb_desc, + unsigned int socket, const struct rte_eth_rxconf *conf, + struct rte_mempool *mp); +int hns3_tx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t nb_desc, + unsigned int socket, const struct rte_eth_txconf *conf); +uint16_t hns3_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, + uint16_t nb_pkts); +uint16_t hns3_prep_pkts(__rte_unused void *tx_queue, struct rte_mbuf **tx_pkts, + uint16_t nb_pkts); +uint16_t hns3_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, + uint16_t nb_pkts); +const uint32_t *hns3_dev_supported_ptypes_get(struct rte_eth_dev *dev); +void hns3_set_rxtx_function(struct rte_eth_dev *eth_dev); +void hns3_set_queue_intr_gl(struct hns3_hw *hw, uint16_t queue_id, + uint8_t gl_idx, uint16_t gl_value); +void hns3_set_queue_intr_rl(struct hns3_hw *hw, uint16_t queue_id, + uint16_t rl_value); +int hns3_set_fake_rx_or_tx_queues(struct rte_eth_dev *dev, uint16_t nb_rx_q, + uint16_t nb_tx_q); + +#endif /* _HNS3_RXTX_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_stats.c b/src/spdk/dpdk/drivers/net/hns3/hns3_stats.c new file mode 100644 index 000000000..d2467a484 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_stats.c @@ -0,0 +1,1010 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#include <stdbool.h> +#include <stdint.h> +#include <rte_common.h> +#include <rte_ethdev.h> +#include <rte_io.h> +#include <rte_malloc.h> +#include <rte_spinlock.h> + +#include "hns3_ethdev.h" +#include "hns3_rxtx.h" +#include "hns3_logs.h" +#include "hns3_regs.h" + +/* MAC statistics */ +static const struct hns3_xstats_name_offset hns3_mac_strings[] = { + {"mac_tx_mac_pause_num", + HNS3_MAC_STATS_OFFSET(mac_tx_mac_pause_num)}, + {"mac_rx_mac_pause_num", + HNS3_MAC_STATS_OFFSET(mac_rx_mac_pause_num)}, + {"mac_tx_control_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_ctrl_pkt_num)}, + {"mac_rx_control_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_ctrl_pkt_num)}, + {"mac_tx_pfc_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pause_pkt_num)}, + {"mac_tx_pfc_pri0_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri0_pkt_num)}, + {"mac_tx_pfc_pri1_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri1_pkt_num)}, + {"mac_tx_pfc_pri2_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri2_pkt_num)}, + {"mac_tx_pfc_pri3_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri3_pkt_num)}, + {"mac_tx_pfc_pri4_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri4_pkt_num)}, + {"mac_tx_pfc_pri5_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri5_pkt_num)}, + {"mac_tx_pfc_pri6_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri6_pkt_num)}, + {"mac_tx_pfc_pri7_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_pfc_pri7_pkt_num)}, + {"mac_rx_pfc_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pause_pkt_num)}, + {"mac_rx_pfc_pri0_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri0_pkt_num)}, + {"mac_rx_pfc_pri1_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri1_pkt_num)}, + {"mac_rx_pfc_pri2_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri2_pkt_num)}, + {"mac_rx_pfc_pri3_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri3_pkt_num)}, + {"mac_rx_pfc_pri4_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri4_pkt_num)}, + {"mac_rx_pfc_pri5_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri5_pkt_num)}, + {"mac_rx_pfc_pri6_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri6_pkt_num)}, + {"mac_rx_pfc_pri7_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_pfc_pri7_pkt_num)}, + {"mac_tx_total_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_total_pkt_num)}, + {"mac_tx_total_oct_num", + HNS3_MAC_STATS_OFFSET(mac_tx_total_oct_num)}, + {"mac_tx_good_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_good_pkt_num)}, + {"mac_tx_bad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_bad_pkt_num)}, + {"mac_tx_good_oct_num", + HNS3_MAC_STATS_OFFSET(mac_tx_good_oct_num)}, + {"mac_tx_bad_oct_num", + HNS3_MAC_STATS_OFFSET(mac_tx_bad_oct_num)}, + {"mac_tx_uni_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_uni_pkt_num)}, + {"mac_tx_multi_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_multi_pkt_num)}, + {"mac_tx_broad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_broad_pkt_num)}, + {"mac_tx_undersize_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_undersize_pkt_num)}, + {"mac_tx_oversize_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_oversize_pkt_num)}, + {"mac_tx_64_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_64_oct_pkt_num)}, + {"mac_tx_65_127_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_65_127_oct_pkt_num)}, + {"mac_tx_128_255_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_128_255_oct_pkt_num)}, + {"mac_tx_256_511_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_256_511_oct_pkt_num)}, + {"mac_tx_512_1023_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_512_1023_oct_pkt_num)}, + {"mac_tx_1024_1518_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_1024_1518_oct_pkt_num)}, + {"mac_tx_1519_2047_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_1519_2047_oct_pkt_num)}, + {"mac_tx_2048_4095_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_2048_4095_oct_pkt_num)}, + {"mac_tx_4096_8191_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_4096_8191_oct_pkt_num)}, + {"mac_tx_8192_9216_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_8192_9216_oct_pkt_num)}, + {"mac_tx_9217_12287_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_9217_12287_oct_pkt_num)}, + {"mac_tx_12288_16383_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_12288_16383_oct_pkt_num)}, + {"mac_tx_1519_max_good_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_1519_max_good_oct_pkt_num)}, + {"mac_tx_1519_max_bad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_1519_max_bad_oct_pkt_num)}, + {"mac_rx_total_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_total_pkt_num)}, + {"mac_rx_total_oct_num", + HNS3_MAC_STATS_OFFSET(mac_rx_total_oct_num)}, + {"mac_rx_good_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_good_pkt_num)}, + {"mac_rx_bad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_bad_pkt_num)}, + {"mac_rx_good_oct_num", + HNS3_MAC_STATS_OFFSET(mac_rx_good_oct_num)}, + {"mac_rx_bad_oct_num", + HNS3_MAC_STATS_OFFSET(mac_rx_bad_oct_num)}, + {"mac_rx_uni_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_uni_pkt_num)}, + {"mac_rx_multi_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_multi_pkt_num)}, + {"mac_rx_broad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_broad_pkt_num)}, + {"mac_rx_undersize_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_undersize_pkt_num)}, + {"mac_rx_oversize_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_oversize_pkt_num)}, + {"mac_rx_64_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_64_oct_pkt_num)}, + {"mac_rx_65_127_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_65_127_oct_pkt_num)}, + {"mac_rx_128_255_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_128_255_oct_pkt_num)}, + {"mac_rx_256_511_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_256_511_oct_pkt_num)}, + {"mac_rx_512_1023_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_512_1023_oct_pkt_num)}, + {"mac_rx_1024_1518_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_1024_1518_oct_pkt_num)}, + {"mac_rx_1519_2047_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_1519_2047_oct_pkt_num)}, + {"mac_rx_2048_4095_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_2048_4095_oct_pkt_num)}, + {"mac_rx_4096_8191_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_4096_8191_oct_pkt_num)}, + {"mac_rx_8192_9216_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_8192_9216_oct_pkt_num)}, + {"mac_rx_9217_12287_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_9217_12287_oct_pkt_num)}, + {"mac_rx_12288_16383_oct_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_12288_16383_oct_pkt_num)}, + {"mac_rx_1519_max_good_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_1519_max_good_oct_pkt_num)}, + {"mac_rx_1519_max_bad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_1519_max_bad_oct_pkt_num)}, + {"mac_tx_fragment_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_fragment_pkt_num)}, + {"mac_tx_undermin_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_undermin_pkt_num)}, + {"mac_tx_jabber_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_jabber_pkt_num)}, + {"mac_tx_err_all_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_err_all_pkt_num)}, + {"mac_tx_from_app_good_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_from_app_good_pkt_num)}, + {"mac_tx_from_app_bad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_tx_from_app_bad_pkt_num)}, + {"mac_rx_fragment_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_fragment_pkt_num)}, + {"mac_rx_undermin_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_undermin_pkt_num)}, + {"mac_rx_jabber_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_jabber_pkt_num)}, + {"mac_rx_fcs_err_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_fcs_err_pkt_num)}, + {"mac_rx_send_app_good_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_send_app_good_pkt_num)}, + {"mac_rx_send_app_bad_pkt_num", + HNS3_MAC_STATS_OFFSET(mac_rx_send_app_bad_pkt_num)} +}; + +static const struct hns3_xstats_name_offset hns3_error_int_stats_strings[] = { + {"MAC_AFIFO_TNL_INT_R", + HNS3_ERR_INT_STATS_FIELD_OFFSET(mac_afifo_tnl_intr_cnt)}, + {"PPU_MPF_ABNORMAL_INT_ST2", + HNS3_ERR_INT_STATS_FIELD_OFFSET(ppu_mpf_abnormal_intr_st2_cnt)}, + {"SSU_PORT_BASED_ERR_INT", + HNS3_ERR_INT_STATS_FIELD_OFFSET(ssu_port_based_pf_intr_cnt)}, + {"PPP_PF_ABNORMAL_INT_ST0", + HNS3_ERR_INT_STATS_FIELD_OFFSET(ppp_pf_abnormal_intr_cnt)}, + {"PPU_PF_ABNORMAL_INT_ST", + HNS3_ERR_INT_STATS_FIELD_OFFSET(ppu_pf_abnormal_intr_cnt)} +}; + +/* The statistic of reset */ +static const struct hns3_xstats_name_offset hns3_reset_stats_strings[] = { + {"REQ_RESET_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(request_cnt)}, + {"GLOBAL_RESET_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(global_cnt)}, + {"IMP_RESET_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(imp_cnt)}, + {"RESET_EXEC_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(exec_cnt)}, + {"RESET_SUCCESS_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(success_cnt)}, + {"RESET_FAIL_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(fail_cnt)}, + {"RESET_MERGE_CNT", + HNS3_RESET_STATS_FIELD_OFFSET(merge_cnt)} +}; + +/* The statistic of errors in Rx BD */ +static const struct hns3_xstats_name_offset hns3_rx_bd_error_strings[] = { + {"RX_PKT_LEN_ERRORS", + HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(pkt_len_errors)}, + {"L2_RX_ERRORS", + HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(l2_errors)}, + {"RX_L3_CHECKSUM_ERRORS", + HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(l3_csum_erros)}, + {"RX_L4_CHECKSUM_ERRORS", + HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(l4_csum_erros)}, + {"RX_OL3_CHECKSUM_ERRORS", + HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(ol3_csum_erros)}, + {"RX_OL4_CHECKSUM_ERRORS", + HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(ol4_csum_erros)} +}; + +/* The statistic of the Tx errors */ +static const struct hns3_xstats_name_offset hns3_tx_errors_strings[] = { + {"TX_OVER_LENGTH_PKT_CNT", + HNS3_TX_ERROR_STATS_FIELD_OFFSET(over_length_pkt_cnt)}, + {"TX_EXCEED_LIMITED_BD_PKT_CNT", + HNS3_TX_ERROR_STATS_FIELD_OFFSET(exceed_limit_bd_pkt_cnt)}, + {"TX_EXCEED_LIMITED_BD_PKT_REASSEMBLE_FAIL_CNT", + HNS3_TX_ERROR_STATS_FIELD_OFFSET(exceed_limit_bd_reassem_fail)}, + {"TX_UNSUPPORTED_TUNNEL_PKT_CNT", + HNS3_TX_ERROR_STATS_FIELD_OFFSET(unsupported_tunnel_pkt_cnt)}, + {"TX_QUEUE_FULL_CNT", + HNS3_TX_ERROR_STATS_FIELD_OFFSET(queue_full_cnt)}, + {"TX_SHORT_PKT_PAD_FAIL_CNT", + HNS3_TX_ERROR_STATS_FIELD_OFFSET(pkt_padding_fail_cnt)} +}; + +/* The statistic of rx queue */ +static const struct hns3_xstats_name_offset hns3_rx_queue_strings[] = { + {"RX_QUEUE_FBD", HNS3_RING_RX_FBDNUM_REG} +}; + +/* The statistic of tx queue */ +static const struct hns3_xstats_name_offset hns3_tx_queue_strings[] = { + {"TX_QUEUE_FBD", HNS3_RING_TX_FBDNUM_REG} +}; + +#define HNS3_NUM_MAC_STATS (sizeof(hns3_mac_strings) / \ + sizeof(hns3_mac_strings[0])) + +#define HNS3_NUM_ERROR_INT_XSTATS (sizeof(hns3_error_int_stats_strings) / \ + sizeof(hns3_error_int_stats_strings[0])) + +#define HNS3_NUM_RESET_XSTATS (sizeof(hns3_reset_stats_strings) / \ + sizeof(hns3_reset_stats_strings[0])) + +#define HNS3_NUM_RX_BD_ERROR_XSTATS (sizeof(hns3_rx_bd_error_strings) / \ + sizeof(hns3_rx_bd_error_strings[0])) + +#define HNS3_NUM_TX_ERRORS_XSTATS (sizeof(hns3_tx_errors_strings) / \ + sizeof(hns3_tx_errors_strings[0])) + +#define HNS3_NUM_RX_QUEUE_STATS (sizeof(hns3_rx_queue_strings) / \ + sizeof(hns3_rx_queue_strings[0])) + +#define HNS3_NUM_TX_QUEUE_STATS (sizeof(hns3_tx_queue_strings) / \ + sizeof(hns3_tx_queue_strings[0])) + +#define HNS3_FIX_NUM_STATS (HNS3_NUM_MAC_STATS + HNS3_NUM_ERROR_INT_XSTATS + \ + HNS3_NUM_RESET_XSTATS) + +/* + * Query all the MAC statistics data of Network ICL command ,opcode id: 0x0034. + * This command is used before send 'query_mac_stat command', the descriptor + * number of 'query_mac_stat command' must match with reg_num in this command. + * @praram hw + * Pointer to structure hns3_hw. + * @return + * 0 on success. + */ +static int +hns3_update_mac_stats(struct hns3_hw *hw, const uint32_t desc_num) +{ + uint64_t *data = (uint64_t *)(&hw->mac_stats); + struct hns3_cmd_desc *desc; + uint64_t *desc_data; + uint16_t i, k, n; + int ret; + + desc = rte_malloc("hns3_mac_desc", + desc_num * sizeof(struct hns3_cmd_desc), 0); + if (desc == NULL) { + hns3_err(hw, "Mac_update_stats alloced desc malloc fail"); + return -ENOMEM; + } + + hns3_cmd_setup_basic_desc(desc, HNS3_OPC_STATS_MAC_ALL, true); + ret = hns3_cmd_send(hw, desc, desc_num); + if (ret) { + hns3_err(hw, "Update complete MAC pkt stats fail : %d", ret); + rte_free(desc); + return ret; + } + + for (i = 0; i < desc_num; i++) { + /* For special opcode 0034, only the first desc has the head */ + if (i == 0) { + desc_data = (uint64_t *)(&desc[i].data[0]); + n = HNS3_RD_FIRST_STATS_NUM; + } else { + desc_data = (uint64_t *)(&desc[i]); + n = HNS3_RD_OTHER_STATS_NUM; + } + + for (k = 0; k < n; k++) { + *data += rte_le_to_cpu_64(*desc_data); + data++; + desc_data++; + } + } + rte_free(desc); + + return 0; +} + +/* + * Query Mac stat reg num command ,opcode id: 0x0033. + * This command is used before send 'query_mac_stat command', the descriptor + * number of 'query_mac_stat command' must match with reg_num in this command. + * @praram rte_stats + * Pointer to structure rte_eth_stats. + * @return + * 0 on success. + */ +static int +hns3_mac_query_reg_num(struct rte_eth_dev *dev, uint32_t *desc_num) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_cmd_desc desc; + uint32_t *desc_data; + uint32_t reg_num; + int ret; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_MAC_REG_NUM, true); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) + return ret; + + /* + * The num of MAC statistics registers that are provided by IMP in this + * version. + */ + desc_data = (uint32_t *)(&desc.data[0]); + reg_num = rte_le_to_cpu_32(*desc_data); + + /* + * The descriptor number of 'query_additional_mac_stat command' is + * '1 + (reg_num-3)/4 + ((reg_num-3)%4 !=0)'; + * This value is 83 in this version + */ + *desc_num = 1 + ((reg_num - 3) >> 2) + + (uint32_t)(((reg_num - 3) & 0x3) ? 1 : 0); + + return 0; +} + +static int +hns3_query_update_mac_stats(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint32_t desc_num; + int ret; + + ret = hns3_mac_query_reg_num(dev, &desc_num); + if (ret == 0) + ret = hns3_update_mac_stats(hw, desc_num); + else + hns3_err(hw, "Query mac reg num fail : %d", ret); + return ret; +} + +/* Get tqp stats from register */ +static int +hns3_update_tqp_stats(struct hns3_hw *hw) +{ + struct hns3_tqp_stats *stats = &hw->tqp_stats; + struct hns3_cmd_desc desc; + uint64_t cnt; + uint16_t i; + int ret; + + for (i = 0; i < hw->tqps_num; i++) { + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_RX_STATUS, + true); + + desc.data[0] = rte_cpu_to_le_32((uint32_t)i & + HNS3_QUEUE_ID_MASK); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Failed to query RX No.%d queue stat: %d", + i, ret); + return ret; + } + cnt = rte_le_to_cpu_32(desc.data[1]); + stats->rcb_rx_ring_pktnum_rcd += cnt; + stats->rcb_rx_ring_pktnum[i] += cnt; + + hns3_cmd_setup_basic_desc(&desc, HNS3_OPC_QUERY_TX_STATUS, + true); + + desc.data[0] = rte_cpu_to_le_32((uint32_t)i & + HNS3_QUEUE_ID_MASK); + ret = hns3_cmd_send(hw, &desc, 1); + if (ret) { + hns3_err(hw, "Failed to query TX No.%d queue stat: %d", + i, ret); + return ret; + } + cnt = rte_le_to_cpu_32(desc.data[1]); + stats->rcb_tx_ring_pktnum_rcd += cnt; + stats->rcb_tx_ring_pktnum[i] += cnt; + } + + return 0; +} + +/* + * Query tqp tx queue statistics ,opcode id: 0x0B03. + * Query tqp rx queue statistics ,opcode id: 0x0B13. + * Get all statistics of a port. + * @param eth_dev + * Pointer to Ethernet device. + * @praram rte_stats + * Pointer to structure rte_eth_stats. + * @return + * 0 on success. + */ +int +hns3_stats_get(struct rte_eth_dev *eth_dev, struct rte_eth_stats *rte_stats) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_tqp_stats *stats = &hw->tqp_stats; + struct hns3_rx_queue *rxq; + struct hns3_tx_queue *txq; + uint64_t cnt; + uint64_t num; + uint16_t i; + int ret; + + /* Update tqp stats by read register */ + ret = hns3_update_tqp_stats(hw); + if (ret) { + hns3_err(hw, "Update tqp stats fail : %d", ret); + return ret; + } + + /* Get the error stats of received packets */ + num = RTE_MIN(RTE_ETHDEV_QUEUE_STAT_CNTRS, eth_dev->data->nb_rx_queues); + for (i = 0; i != num; ++i) { + rxq = eth_dev->data->rx_queues[i]; + if (rxq) { + cnt = rxq->l2_errors + rxq->pkt_len_errors; + rte_stats->q_errors[i] = cnt; + rte_stats->q_ipackets[i] = + stats->rcb_rx_ring_pktnum[i] - cnt; + rte_stats->ierrors += cnt; + } + } + /* Get the error stats of transmitted packets */ + num = RTE_MIN(RTE_ETHDEV_QUEUE_STAT_CNTRS, eth_dev->data->nb_tx_queues); + for (i = 0; i < num; i++) { + txq = eth_dev->data->tx_queues[i]; + if (txq) + rte_stats->q_opackets[i] = stats->rcb_tx_ring_pktnum[i]; + } + + rte_stats->oerrors = 0; + rte_stats->ipackets = stats->rcb_rx_ring_pktnum_rcd - + rte_stats->ierrors; + rte_stats->opackets = stats->rcb_tx_ring_pktnum_rcd - + rte_stats->oerrors; + rte_stats->rx_nombuf = eth_dev->data->rx_mbuf_alloc_failed; + + return 0; +} + +int +hns3_stats_reset(struct rte_eth_dev *eth_dev) +{ + struct hns3_adapter *hns = eth_dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_tqp_stats *stats = &hw->tqp_stats; + struct hns3_cmd_desc desc_reset; + struct hns3_rx_queue *rxq; + struct hns3_tx_queue *txq; + uint16_t i; + int ret; + + /* + * If this is a reset xstats is NULL, and we have cleared the + * registers by reading them. + */ + for (i = 0; i < hw->tqps_num; i++) { + hns3_cmd_setup_basic_desc(&desc_reset, HNS3_OPC_QUERY_RX_STATUS, + true); + desc_reset.data[0] = rte_cpu_to_le_32((uint32_t)i & + HNS3_QUEUE_ID_MASK); + ret = hns3_cmd_send(hw, &desc_reset, 1); + if (ret) { + hns3_err(hw, "Failed to reset RX No.%d queue stat: %d", + i, ret); + return ret; + } + + hns3_cmd_setup_basic_desc(&desc_reset, HNS3_OPC_QUERY_TX_STATUS, + true); + desc_reset.data[0] = rte_cpu_to_le_32((uint32_t)i & + HNS3_QUEUE_ID_MASK); + ret = hns3_cmd_send(hw, &desc_reset, 1); + if (ret) { + hns3_err(hw, "Failed to reset TX No.%d queue stat: %d", + i, ret); + return ret; + } + } + + /* Clear the Rx BD errors stats */ + for (i = 0; i != eth_dev->data->nb_rx_queues; ++i) { + rxq = eth_dev->data->rx_queues[i]; + if (rxq) { + rxq->pkt_len_errors = 0; + rxq->l2_errors = 0; + rxq->l3_csum_erros = 0; + rxq->l4_csum_erros = 0; + rxq->ol3_csum_erros = 0; + rxq->ol4_csum_erros = 0; + } + } + + /* Clear the Tx errors stats */ + for (i = 0; i != eth_dev->data->nb_tx_queues; ++i) { + txq = eth_dev->data->tx_queues[i]; + if (txq) { + txq->over_length_pkt_cnt = 0; + txq->exceed_limit_bd_pkt_cnt = 0; + txq->exceed_limit_bd_reassem_fail = 0; + txq->unsupported_tunnel_pkt_cnt = 0; + txq->queue_full_cnt = 0; + txq->pkt_padding_fail_cnt = 0; + } + } + + memset(stats, 0, sizeof(struct hns3_tqp_stats)); + + return 0; +} + +static int +hns3_mac_stats_reset(__rte_unused struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + struct hns3_mac_stats *mac_stats = &hw->mac_stats; + int ret; + + ret = hns3_query_update_mac_stats(dev); + if (ret) { + hns3_err(hw, "Clear Mac stats fail : %d", ret); + return ret; + } + + memset(mac_stats, 0, sizeof(struct hns3_mac_stats)); + + return 0; +} + +/* This function calculates the number of xstats based on the current config */ +static int +hns3_xstats_calc_num(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + int bderr_stats = dev->data->nb_rx_queues * HNS3_NUM_RX_BD_ERROR_XSTATS; + int tx_err_stats = dev->data->nb_tx_queues * HNS3_NUM_TX_ERRORS_XSTATS; + int rx_queue_stats = dev->data->nb_rx_queues * HNS3_NUM_RX_QUEUE_STATS; + int tx_queue_stats = dev->data->nb_tx_queues * HNS3_NUM_TX_QUEUE_STATS; + + if (hns->is_vf) + return bderr_stats + tx_err_stats + rx_queue_stats + + tx_queue_stats + HNS3_NUM_RESET_XSTATS; + else + return bderr_stats + tx_err_stats + rx_queue_stats + + tx_queue_stats + HNS3_FIX_NUM_STATS; +} + +static void +hns3_get_queue_stats(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats, + int *count) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_hw *hw = &hns->hw; + uint32_t reg_offset; + uint16_t i, j; + + /* Get rx queue stats */ + for (j = 0; j < dev->data->nb_rx_queues; j++) { + for (i = 0; i < HNS3_NUM_RX_QUEUE_STATS; i++) { + reg_offset = HNS3_TQP_REG_OFFSET + + HNS3_TQP_REG_SIZE * j; + xstats[*count].value = hns3_read_dev(hw, + reg_offset + hns3_rx_queue_strings[i].offset); + xstats[*count].id = *count; + (*count)++; + } + } + + /* Get tx queue stats */ + for (j = 0; j < dev->data->nb_tx_queues; j++) { + for (i = 0; i < HNS3_NUM_TX_QUEUE_STATS; i++) { + reg_offset = HNS3_TQP_REG_OFFSET + + HNS3_TQP_REG_SIZE * j; + xstats[*count].value = hns3_read_dev(hw, + reg_offset + hns3_tx_queue_strings[i].offset); + xstats[*count].id = *count; + (*count)++; + } + } + +} + +/* + * Retrieve extended(tqp | Mac) statistics of an Ethernet device. + * @param dev + * Pointer to Ethernet device. + * @praram xstats + * A pointer to a table of structure of type *rte_eth_xstat* + * to be filled with device statistics ids and values. + * This parameter can be set to NULL if n is 0. + * @param n + * The size of the xstats array (number of elements). + * @return + * 0 on fail, count(The size of the statistics elements) on success. + */ +int +hns3_dev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats, + unsigned int n) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_pf *pf = &hns->pf; + struct hns3_hw *hw = &hns->hw; + struct hns3_mac_stats *mac_stats = &hw->mac_stats; + struct hns3_reset_stats *reset_stats = &hw->reset.stats; + struct hns3_rx_queue *rxq; + struct hns3_tx_queue *txq; + uint16_t i, j; + char *addr; + int count; + int ret; + + if (xstats == NULL) + return 0; + + count = hns3_xstats_calc_num(dev); + if ((int)n < count) + return count; + + count = 0; + + if (!hns->is_vf) { + /* Update Mac stats */ + ret = hns3_query_update_mac_stats(dev); + if (ret) { + hns3_err(hw, "Update Mac stats fail : %d", ret); + return 0; + } + + /* Get MAC stats from hw->hw_xstats.mac_stats struct */ + for (i = 0; i < HNS3_NUM_MAC_STATS; i++) { + addr = (char *)mac_stats + hns3_mac_strings[i].offset; + xstats[count].value = *(uint64_t *)addr; + xstats[count].id = count; + count++; + } + + for (i = 0; i < HNS3_NUM_ERROR_INT_XSTATS; i++) { + addr = (char *)&pf->abn_int_stats + + hns3_error_int_stats_strings[i].offset; + xstats[count].value = *(uint64_t *)addr; + xstats[count].id = count; + count++; + } + } + + /* Get the reset stat */ + for (i = 0; i < HNS3_NUM_RESET_XSTATS; i++) { + addr = (char *)reset_stats + hns3_reset_stats_strings[i].offset; + xstats[count].value = *(uint64_t *)addr; + xstats[count].id = count; + count++; + } + + /* Get the Rx BD errors stats */ + for (j = 0; j < dev->data->nb_rx_queues; j++) { + for (i = 0; i < HNS3_NUM_RX_BD_ERROR_XSTATS; i++) { + rxq = dev->data->rx_queues[j]; + addr = (char *)rxq + hns3_rx_bd_error_strings[i].offset; + xstats[count].value = *(uint64_t *)addr; + xstats[count].id = count; + count++; + } + } + + /* Get the Tx errors stats */ + for (j = 0; j < dev->data->nb_tx_queues; j++) { + for (i = 0; i < HNS3_NUM_TX_ERRORS_XSTATS; i++) { + txq = dev->data->tx_queues[j]; + addr = (char *)txq + hns3_tx_errors_strings[i].offset; + xstats[count].value = *(uint64_t *)addr; + xstats[count].id = count; + count++; + } + } + + hns3_get_queue_stats(dev, xstats, &count); + return count; +} + +/* + * Retrieve names of extended statistics of an Ethernet device. + * + * There is an assumption that 'xstat_names' and 'xstats' arrays are matched + * by array index: + * xstats_names[i].name => xstats[i].value + * + * And the array index is same with id field of 'struct rte_eth_xstat': + * xstats[i].id == i + * + * This assumption makes key-value pair matching less flexible but simpler. + * + * @param dev + * Pointer to Ethernet device. + * @param xstats_names + * An rte_eth_xstat_name array of at least *size* elements to + * be filled. If set to NULL, the function returns the required number + * of elements. + * @param size + * The size of the xstats_names array (number of elements). + * @return + * - A positive value lower or equal to size: success. The return value + * is the number of entries filled in the stats table. + */ +int +hns3_dev_xstats_get_names(struct rte_eth_dev *dev, + struct rte_eth_xstat_name *xstats_names, + __rte_unused unsigned int size) +{ + struct hns3_adapter *hns = dev->data->dev_private; + int cnt_stats = hns3_xstats_calc_num(dev); + uint32_t count = 0; + uint16_t i, j; + + if (xstats_names == NULL) + return cnt_stats; + + /* Note: size limited checked in rte_eth_xstats_get_names() */ + if (!hns->is_vf) { + /* Get MAC name from hw->hw_xstats.mac_stats struct */ + for (i = 0; i < HNS3_NUM_MAC_STATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "%s", hns3_mac_strings[i].name); + count++; + } + + for (i = 0; i < HNS3_NUM_ERROR_INT_XSTATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "%s", hns3_error_int_stats_strings[i].name); + count++; + } + } + for (i = 0; i < HNS3_NUM_RESET_XSTATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "%s", hns3_reset_stats_strings[i].name); + count++; + } + + for (j = 0; j < dev->data->nb_rx_queues; j++) { + for (i = 0; i < HNS3_NUM_RX_BD_ERROR_XSTATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "rx_q%u%s", j, + hns3_rx_bd_error_strings[i].name); + count++; + } + } + + for (j = 0; j < dev->data->nb_tx_queues; j++) { + for (i = 0; i < HNS3_NUM_TX_ERRORS_XSTATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "tx_q%u%s", j, + hns3_tx_errors_strings[i].name); + count++; + } + } + + for (j = 0; j < dev->data->nb_rx_queues; j++) { + for (i = 0; i < HNS3_NUM_RX_QUEUE_STATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "rx_q%u%s", j, hns3_rx_queue_strings[i].name); + count++; + } + } + + for (j = 0; j < dev->data->nb_tx_queues; j++) { + for (i = 0; i < HNS3_NUM_TX_QUEUE_STATS; i++) { + snprintf(xstats_names[count].name, + sizeof(xstats_names[count].name), + "tx_q%u%s", j, hns3_tx_queue_strings[i].name); + count++; + } + } + + return count; +} + +/* + * Retrieve extended statistics of an Ethernet device. + * + * @param dev + * Pointer to Ethernet device. + * @param ids + * A pointer to an ids array passed by application. This tells which + * statistics values function should retrieve. This parameter + * can be set to NULL if size is 0. In this case function will retrieve + * all avalible statistics. + * @param values + * A pointer to a table to be filled with device statistics values. + * @param size + * The size of the ids array (number of elements). + * @return + * - A positive value lower or equal to size: success. The return value + * is the number of entries filled in the stats table. + * - A positive value higher than size: error, the given statistics table + * is too small. The return value corresponds to the size that should + * be given to succeed. The entries in the table are not valid and + * shall not be used by the caller. + * - 0 on no ids. + */ +int +hns3_dev_xstats_get_by_id(struct rte_eth_dev *dev, const uint64_t *ids, + uint64_t *values, uint32_t size) +{ + const uint32_t cnt_stats = hns3_xstats_calc_num(dev); + struct hns3_adapter *hns = dev->data->dev_private; + struct rte_eth_xstat *values_copy; + struct hns3_hw *hw = &hns->hw; + uint32_t count_value; + uint64_t len; + uint32_t i; + int ret; + + if (ids == NULL || size < cnt_stats) + return cnt_stats; + + /* Update tqp stats by read register */ + ret = hns3_update_tqp_stats(hw); + if (ret) { + hns3_err(hw, "Update tqp stats fail : %d", ret); + return ret; + } + + len = cnt_stats * sizeof(struct rte_eth_xstat); + values_copy = rte_zmalloc("hns3_xstats_values", len, 0); + if (values_copy == NULL) { + hns3_err(hw, "Failed to allocate %" PRIx64 " bytes needed " + "to store statistics values", len); + return -ENOMEM; + } + + count_value = hns3_dev_xstats_get(dev, values_copy, cnt_stats); + if (count_value != cnt_stats) { + rte_free(values_copy); + return -EINVAL; + } + + for (i = 0; i < size; i++) { + if (ids[i] >= cnt_stats) { + hns3_err(hw, "ids[%d] (%" PRIx64 ") is invalid, " + "should < %u", i, ids[i], cnt_stats); + rte_free(values_copy); + return -EINVAL; + } + memcpy(&values[i], &values_copy[ids[i]].value, + sizeof(values[i])); + } + + rte_free(values_copy); + return size; +} + +/* + * Retrieve names of extended statistics of an Ethernet device. + * + * @param dev + * Pointer to Ethernet device. + * @param xstats_names + * An rte_eth_xstat_name array of at least *size* elements to + * be filled. If set to NULL, the function returns the required number + * of elements. + * @param ids + * IDs array given by app to retrieve specific statistics + * @param size + * The size of the xstats_names array (number of elements). + * @return + * - A positive value lower or equal to size: success. The return value + * is the number of entries filled in the stats table. + * - A positive value higher than size: error, the given statistics table + * is too small. The return value corresponds to the size that should + * be given to succeed. The entries in the table are not valid and + * shall not be used by the caller. + */ +int +hns3_dev_xstats_get_names_by_id(struct rte_eth_dev *dev, + struct rte_eth_xstat_name *xstats_names, + const uint64_t *ids, uint32_t size) +{ + const uint32_t cnt_stats = hns3_xstats_calc_num(dev); + struct hns3_adapter *hns = dev->data->dev_private; + struct rte_eth_xstat_name *names_copy; + struct hns3_hw *hw = &hns->hw; + uint64_t len; + uint32_t i; + + if (ids == NULL || xstats_names == NULL) + return cnt_stats; + + len = cnt_stats * sizeof(struct rte_eth_xstat_name); + names_copy = rte_zmalloc("hns3_xstats_names", len, 0); + if (names_copy == NULL) { + hns3_err(hw, "Failed to allocate %" PRIx64 " bytes needed " + "to store statistics names", len); + return -ENOMEM; + } + + (void)hns3_dev_xstats_get_names(dev, names_copy, cnt_stats); + + for (i = 0; i < size; i++) { + if (ids[i] >= cnt_stats) { + hns3_err(hw, "ids[%d] (%" PRIx64 ") is invalid, " + "should < %u", i, ids[i], cnt_stats); + rte_free(names_copy); + return -EINVAL; + } + snprintf(xstats_names[i].name, sizeof(xstats_names[i].name), + "%s", names_copy[ids[i]].name); + } + + rte_free(names_copy); + return size; +} + +int +hns3_dev_xstats_reset(struct rte_eth_dev *dev) +{ + struct hns3_adapter *hns = dev->data->dev_private; + struct hns3_pf *pf = &hns->pf; + int ret; + + /* Clear tqp stats */ + ret = hns3_stats_reset(dev); + if (ret) + return ret; + + /* Clear reset stats */ + memset(&hns->hw.reset.stats, 0, sizeof(struct hns3_reset_stats)); + + if (hns->is_vf) + return 0; + + /* HW registers are cleared on read */ + ret = hns3_mac_stats_reset(dev); + if (ret) + return ret; + + /* Clear error stats */ + memset(&pf->abn_int_stats, 0, sizeof(struct hns3_err_msix_intr_stats)); + + return 0; +} diff --git a/src/spdk/dpdk/drivers/net/hns3/hns3_stats.h b/src/spdk/dpdk/drivers/net/hns3/hns3_stats.h new file mode 100644 index 000000000..0993c5f57 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/hns3_stats.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2019 Hisilicon Limited. + */ + +#ifndef _HNS3_STATS_H_ +#define _HNS3_STATS_H_ + +/* stats macro */ +#define HNS3_MAC_CMD_NUM 21 +#define HNS3_RD_FIRST_STATS_NUM 2 +#define HNS3_RD_OTHER_STATS_NUM 4 + +/* TQP stats */ +struct hns3_tqp_stats { + uint64_t rcb_tx_ring_pktnum_rcd; /* Total num of transmitted packets */ + uint64_t rcb_rx_ring_pktnum_rcd; /* Total num of received packets */ + uint64_t rcb_tx_ring_pktnum[HNS3_MAX_TQP_NUM_PER_FUNC]; + uint64_t rcb_rx_ring_pktnum[HNS3_MAX_TQP_NUM_PER_FUNC]; +}; + +/* mac stats, Statistics counters collected by the MAC, opcode id: 0x0032 */ +struct hns3_mac_stats { + uint64_t mac_tx_mac_pause_num; + uint64_t mac_rx_mac_pause_num; + uint64_t mac_tx_pfc_pri0_pkt_num; + uint64_t mac_tx_pfc_pri1_pkt_num; + uint64_t mac_tx_pfc_pri2_pkt_num; + uint64_t mac_tx_pfc_pri3_pkt_num; + uint64_t mac_tx_pfc_pri4_pkt_num; + uint64_t mac_tx_pfc_pri5_pkt_num; + uint64_t mac_tx_pfc_pri6_pkt_num; + uint64_t mac_tx_pfc_pri7_pkt_num; + uint64_t mac_rx_pfc_pri0_pkt_num; + uint64_t mac_rx_pfc_pri1_pkt_num; + uint64_t mac_rx_pfc_pri2_pkt_num; + uint64_t mac_rx_pfc_pri3_pkt_num; + uint64_t mac_rx_pfc_pri4_pkt_num; + uint64_t mac_rx_pfc_pri5_pkt_num; + uint64_t mac_rx_pfc_pri6_pkt_num; + uint64_t mac_rx_pfc_pri7_pkt_num; + uint64_t mac_tx_total_pkt_num; + uint64_t mac_tx_total_oct_num; + uint64_t mac_tx_good_pkt_num; + uint64_t mac_tx_bad_pkt_num; + uint64_t mac_tx_good_oct_num; + uint64_t mac_tx_bad_oct_num; + uint64_t mac_tx_uni_pkt_num; + uint64_t mac_tx_multi_pkt_num; + uint64_t mac_tx_broad_pkt_num; + uint64_t mac_tx_undersize_pkt_num; + uint64_t mac_tx_oversize_pkt_num; + uint64_t mac_tx_64_oct_pkt_num; + uint64_t mac_tx_65_127_oct_pkt_num; + uint64_t mac_tx_128_255_oct_pkt_num; + uint64_t mac_tx_256_511_oct_pkt_num; + uint64_t mac_tx_512_1023_oct_pkt_num; + uint64_t mac_tx_1024_1518_oct_pkt_num; + uint64_t mac_tx_1519_2047_oct_pkt_num; + uint64_t mac_tx_2048_4095_oct_pkt_num; + uint64_t mac_tx_4096_8191_oct_pkt_num; + uint64_t rsv0; + uint64_t mac_tx_8192_9216_oct_pkt_num; + uint64_t mac_tx_9217_12287_oct_pkt_num; + uint64_t mac_tx_12288_16383_oct_pkt_num; + uint64_t mac_tx_1519_max_good_oct_pkt_num; + uint64_t mac_tx_1519_max_bad_oct_pkt_num; + + uint64_t mac_rx_total_pkt_num; + uint64_t mac_rx_total_oct_num; + uint64_t mac_rx_good_pkt_num; + uint64_t mac_rx_bad_pkt_num; + uint64_t mac_rx_good_oct_num; + uint64_t mac_rx_bad_oct_num; + uint64_t mac_rx_uni_pkt_num; + uint64_t mac_rx_multi_pkt_num; + uint64_t mac_rx_broad_pkt_num; + uint64_t mac_rx_undersize_pkt_num; + uint64_t mac_rx_oversize_pkt_num; + uint64_t mac_rx_64_oct_pkt_num; + uint64_t mac_rx_65_127_oct_pkt_num; + uint64_t mac_rx_128_255_oct_pkt_num; + uint64_t mac_rx_256_511_oct_pkt_num; + uint64_t mac_rx_512_1023_oct_pkt_num; + uint64_t mac_rx_1024_1518_oct_pkt_num; + uint64_t mac_rx_1519_2047_oct_pkt_num; + uint64_t mac_rx_2048_4095_oct_pkt_num; + uint64_t mac_rx_4096_8191_oct_pkt_num; + uint64_t rsv1; + uint64_t mac_rx_8192_9216_oct_pkt_num; + uint64_t mac_rx_9217_12287_oct_pkt_num; + uint64_t mac_rx_12288_16383_oct_pkt_num; + uint64_t mac_rx_1519_max_good_oct_pkt_num; + uint64_t mac_rx_1519_max_bad_oct_pkt_num; + + uint64_t mac_tx_fragment_pkt_num; + uint64_t mac_tx_undermin_pkt_num; + uint64_t mac_tx_jabber_pkt_num; + uint64_t mac_tx_err_all_pkt_num; + uint64_t mac_tx_from_app_good_pkt_num; + uint64_t mac_tx_from_app_bad_pkt_num; + uint64_t mac_rx_fragment_pkt_num; + uint64_t mac_rx_undermin_pkt_num; + uint64_t mac_rx_jabber_pkt_num; + uint64_t mac_rx_fcs_err_pkt_num; + uint64_t mac_rx_send_app_good_pkt_num; + uint64_t mac_rx_send_app_bad_pkt_num; + uint64_t mac_tx_pfc_pause_pkt_num; + uint64_t mac_rx_pfc_pause_pkt_num; + uint64_t mac_tx_ctrl_pkt_num; + uint64_t mac_rx_ctrl_pkt_num; +}; + +/* store statistics names and its offset in stats structure */ +struct hns3_xstats_name_offset { + char name[RTE_ETH_XSTATS_NAME_SIZE]; + uint32_t offset; +}; + +#define HNS3_MAC_STATS_OFFSET(f) \ + (offsetof(struct hns3_mac_stats, f)) + +#define HNS3_ERR_INT_STATS_FIELD_OFFSET(f) \ + (offsetof(struct hns3_err_msix_intr_stats, f)) + +struct hns3_reset_stats; +#define HNS3_RESET_STATS_FIELD_OFFSET(f) \ + (offsetof(struct hns3_reset_stats, f)) + +#define HNS3_RX_BD_ERROR_STATS_FIELD_OFFSET(f) \ + (offsetof(struct hns3_rx_queue, f)) + +#define HNS3_TX_ERROR_STATS_FIELD_OFFSET(f) \ + (offsetof(struct hns3_tx_queue, f)) + +int hns3_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *rte_stats); +int hns3_dev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats, + unsigned int n); +int hns3_dev_xstats_reset(struct rte_eth_dev *dev); +int hns3_dev_xstats_get_names(struct rte_eth_dev *dev, + struct rte_eth_xstat_name *xstats_names, + __rte_unused unsigned int size); +int hns3_dev_xstats_get_by_id(struct rte_eth_dev *dev, + __rte_unused const uint64_t *ids, + __rte_unused uint64_t *values, + uint32_t size); +int hns3_dev_xstats_get_names_by_id(struct rte_eth_dev *dev, + struct rte_eth_xstat_name *xstats_names, + const uint64_t *ids, + uint32_t size); +int hns3_stats_reset(struct rte_eth_dev *dev); +#endif /* _HNS3_STATS_H_ */ diff --git a/src/spdk/dpdk/drivers/net/hns3/meson.build b/src/spdk/dpdk/drivers/net/hns3/meson.build new file mode 100644 index 000000000..e01e6ce60 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/meson.build @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2018-2019 Hisilicon Limited + +if not is_linux + build = false + reason = 'only supported on Linux' + subdir_done() +endif + +if arch_subdir != 'x86' and arch_subdir != 'arm' or not dpdk_conf.get('RTE_ARCH_64') + build = false + reason = 'only supported on x86_64 and arm64' + subdir_done() +endif + +sources = files('hns3_cmd.c', + 'hns3_dcb.c', + 'hns3_intr.c', + 'hns3_ethdev.c', + 'hns3_ethdev_vf.c', + 'hns3_fdir.c', + 'hns3_flow.c', + 'hns3_mbx.c', + 'hns3_regs.c', + 'hns3_rss.c', + 'hns3_rxtx.c', + 'hns3_stats.c', + 'hns3_mp.c') + +deps += ['hash'] diff --git a/src/spdk/dpdk/drivers/net/hns3/rte_pmd_hns3_version.map b/src/spdk/dpdk/drivers/net/hns3/rte_pmd_hns3_version.map new file mode 100644 index 000000000..f9f17e4f6 --- /dev/null +++ b/src/spdk/dpdk/drivers/net/hns3/rte_pmd_hns3_version.map @@ -0,0 +1,3 @@ +DPDK_20.0 { + local: *; +}; |