diff options
Diffstat (limited to 'drivers/net/ethernet/meta')
22 files changed, 1960 insertions, 155 deletions
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic.h b/drivers/net/ethernet/meta/fbnic/fbnic.h index 779a083b9215..d0715695c43e 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic.h @@ -57,6 +57,7 @@ struct fbnic_dev { u64 dsn; u32 mps; u32 readrq; + u8 relaxed_ord; /* Local copy of the devices TCAM */ struct fbnic_act_tcam act_tcam[FBNIC_RPC_TCAM_ACT_NUM_ENTRIES]; @@ -98,6 +99,9 @@ struct fbnic_dev { /* MDIO bus for PHYs */ struct mii_bus *mdio_bus; + + /* In units of ms since API supports values in ms */ + u16 ps_timeout; }; /* Reserve entry 0 in the MSI-X "others" array until we have filled all @@ -194,6 +198,38 @@ void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr); int fbnic_request_irq(struct fbnic_dev *dev, int nr, irq_handler_t handler, unsigned long flags, const char *name, void *data); void fbnic_free_irq(struct fbnic_dev *dev, int nr, void *data); + +/** + * enum fbnic_msix_self_test_codes - return codes from self test routines + * + * These are the codes returned from the self test routines and + * stored in the test result array indexed by the specific + * test name. + * + * @FBNIC_TEST_MSIX_SUCCESS: no errors + * @FBNIC_TEST_MSIX_NOMEM: allocation failure + * @FBNIC_TEST_MSIX_IRQ_REQ_FAIL: IRQ request failure + * @FBNIC_TEST_MSIX_MASK: masking failed to prevent IRQ + * @FBNIC_TEST_MSIX_UNMASK: unmasking failure w/ sw status set + * @FBNIC_TEST_MSIX_IRQ_CLEAR: interrupt when clearing mask + * @FBNIC_TEST_MSIX_NO_INTERRUPT: no interrupt when not masked + * @FBNIC_TEST_MSIX_NO_CLEAR_OR_MASK: status not cleared, or mask not set + * @FBNIC_TEST_MSIX_BITS_SET_AFTER_TEST: Bits are set after test + */ +enum fbnic_msix_self_test_codes { + FBNIC_TEST_MSIX_SUCCESS = 0, + FBNIC_TEST_MSIX_NOMEM = 5, + FBNIC_TEST_MSIX_IRQ_REQ_FAIL = 10, + FBNIC_TEST_MSIX_MASK = 20, + FBNIC_TEST_MSIX_UNMASK = 30, + FBNIC_TEST_MSIX_IRQ_CLEAR = 40, + FBNIC_TEST_MSIX_NO_INTERRUPT = 50, + FBNIC_TEST_MSIX_NO_CLEAR_OR_MASK = 60, + FBNIC_TEST_MSIX_BITS_SET_AFTER_TEST = 70, +}; + +enum fbnic_msix_self_test_codes fbnic_msix_test(struct fbnic_dev *fbd); + void fbnic_free_irqs(struct fbnic_dev *fbd); int fbnic_alloc_irqs(struct fbnic_dev *fbd); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.c b/drivers/net/ethernet/meta/fbnic/fbnic_csr.c index d9c0dc1c2af9..dc62d623e37c 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.c @@ -147,3 +147,131 @@ int fbnic_csr_regs_len(struct fbnic_dev *fbd) return len; } + +/* CSR register test data + * + * The register test will be used to verify hardware is behaving as expected. + * + * The test itself will have us writing to registers that should have no + * side effects due to us resetting after the test has been completed. + * While the test is being run the interface should be offline. + */ +struct fbnic_csr_reg_test_data { + int reg; + u16 reg_offset; + u8 array_len; + u32 read; + u32 write; +}; + +#define FBNIC_QUEUE_REG_TEST(_name, _read, _write) { \ + .reg = FBNIC_QUEUE(0) + FBNIC_QUEUE_##_name, \ + .reg_offset = FBNIC_QUEUE_STRIDE, \ + .array_len = 64, \ + .read = _read, \ + .write = _write \ +} + +static const struct fbnic_csr_reg_test_data pattern_test[] = { + FBNIC_QUEUE_REG_TEST(TWQ0_CTL, FBNIC_QUEUE_TWQ_CTL_RESET, + FBNIC_QUEUE_TWQ_CTL_RESET), + FBNIC_QUEUE_REG_TEST(TWQ0_PTRS, 0, ~0), + FBNIC_QUEUE_REG_TEST(TWQ0_SIZE, FBNIC_QUEUE_TWQ_SIZE_MASK, ~0), + FBNIC_QUEUE_REG_TEST(TWQ0_BAL, FBNIC_QUEUE_BAL_MASK, ~0), + FBNIC_QUEUE_REG_TEST(TWQ0_BAH, ~0, ~0), + FBNIC_QUEUE_REG_TEST(TWQ1_CTL, FBNIC_QUEUE_TWQ_CTL_RESET, + FBNIC_QUEUE_TWQ_CTL_RESET), + FBNIC_QUEUE_REG_TEST(TWQ1_PTRS, 0, ~0), + FBNIC_QUEUE_REG_TEST(TWQ1_SIZE, FBNIC_QUEUE_TWQ_SIZE_MASK, ~0), + FBNIC_QUEUE_REG_TEST(TWQ1_BAL, FBNIC_QUEUE_BAL_MASK, ~0), + FBNIC_QUEUE_REG_TEST(TWQ1_BAH, ~0, ~0), + FBNIC_QUEUE_REG_TEST(TCQ_CTL, FBNIC_QUEUE_TCQ_CTL_RESET, + FBNIC_QUEUE_TCQ_CTL_RESET), + FBNIC_QUEUE_REG_TEST(TCQ_PTRS, 0, ~0), + FBNIC_QUEUE_REG_TEST(TCQ_SIZE, FBNIC_QUEUE_TCQ_SIZE_MASK, ~0), + FBNIC_QUEUE_REG_TEST(TCQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0), + FBNIC_QUEUE_REG_TEST(TCQ_BAH, ~0, ~0), + FBNIC_QUEUE_REG_TEST(RCQ_CTL, FBNIC_QUEUE_RCQ_CTL_RESET, + FBNIC_QUEUE_RCQ_CTL_RESET), + FBNIC_QUEUE_REG_TEST(RCQ_PTRS, 0, ~0), + FBNIC_QUEUE_REG_TEST(RCQ_SIZE, FBNIC_QUEUE_RCQ_SIZE_MASK, ~0), + FBNIC_QUEUE_REG_TEST(RCQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0), + FBNIC_QUEUE_REG_TEST(RCQ_BAH, ~0, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_CTL, FBNIC_QUEUE_BDQ_CTL_RESET, + FBNIC_QUEUE_BDQ_CTL_RESET), + FBNIC_QUEUE_REG_TEST(BDQ_HPQ_PTRS, 0, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_HPQ_SIZE, FBNIC_QUEUE_BDQ_SIZE_MASK, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_HPQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_HPQ_BAH, ~0, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_PPQ_PTRS, 0, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_PPQ_SIZE, FBNIC_QUEUE_BDQ_SIZE_MASK, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_PPQ_BAL, FBNIC_QUEUE_BAL_MASK, ~0), + FBNIC_QUEUE_REG_TEST(BDQ_PPQ_BAH, ~0, ~0), +}; + +static enum fbnic_reg_self_test_codes +fbnic_csr_reg_pattern_test(struct fbnic_dev *fbd, int index, + const struct fbnic_csr_reg_test_data *test_data) +{ + static const u32 pattern[] = { ~0, 0x5A5A5A5A, 0xA5A5A5A5, 0}; + enum fbnic_reg_self_test_codes reg; + int i; + + reg = test_data->reg + test_data->reg_offset * index; + for (i = 0; i < ARRAY_SIZE(pattern); i++) { + u32 val = pattern[i] & test_data->write; + u32 result; + + wr32(fbd, reg, val); + result = rd32(fbd, reg); + val &= test_data->read; + + if (result == val) + continue; + + dev_err(fbd->dev, + "%s: reg 0x%06X failed, expected 0x%08X received 0x%08X\n", + __func__, reg, val, result); + + /* Note that FBNIC_INTR_STATUS(0) could be tested and fail + * and the result would not be reported since the register + * offset is 0. However as that register isn't included in + * the register test that isn't an issue. + */ + return reg; + } + + return FBNIC_REG_TEST_SUCCESS; +} + +/** + * fbnic_csr_regs_test() - Verify behavior of NIC registers + * @fbd: device to test + * + * This function is meant to test the bit values of various registers in + * the NIC device. Specifically this test will verify which bits are + * writable and which ones are not. It will write varying patterns of bits + * to the registers testing for sticky bits, or bits that are writable but + * should not be. + * + * Return: FBNIC_REG_TEST_SUCCESS on success, register number on failure + **/ +enum fbnic_reg_self_test_codes fbnic_csr_regs_test(struct fbnic_dev *fbd) +{ + const struct fbnic_csr_reg_test_data *test_data; + + for (test_data = pattern_test; + test_data < pattern_test + ARRAY_SIZE(pattern_test); test_data++) { + u32 i; + + for (i = 0; i < test_data->array_len; i++) { + enum fbnic_reg_self_test_codes reg = + fbnic_csr_reg_pattern_test(fbd, i, test_data); + + if (reg) + return reg; + } + } + + return FBNIC_REG_TEST_SUCCESS; +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h index 422265dc7abd..baba3471bf5a 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h @@ -6,6 +6,8 @@ #include <linux/bitops.h> +struct fbnic_dev; + #define CSR_BIT(nr) (1u << (nr)) #define CSR_GENMASK(h, l) GENMASK(h, l) @@ -230,6 +232,7 @@ enum { #define FBNIC_INTR_MSIX_CTRL_VECTOR_MASK CSR_GENMASK(7, 0) #define FBNIC_INTR_MSIX_CTRL_ENABLE CSR_BIT(31) enum { + FBNIC_INTR_MSIX_CTRL_RXB_IDX = 7, FBNIC_INTR_MSIX_CTRL_PCS_IDX = 34, }; @@ -560,6 +563,11 @@ enum { #define FBNIC_RXB_DROP_THLD_CNT 8 #define FBNIC_RXB_DROP_THLD_ON CSR_GENMASK(12, 0) #define FBNIC_RXB_DROP_THLD_OFF CSR_GENMASK(25, 13) +#define FBNIC_RXB_PAUSE_STORM(n) (0x08019 + (n)) /* 0x20064 + 4*n */ +#define FBNIC_RXB_PAUSE_STORM_CNT 4 +#define FBNIC_RXB_PAUSE_STORM_FORCE_NORMAL CSR_BIT(20) +#define FBNIC_RXB_PAUSE_STORM_THLD_TIME CSR_GENMASK(19, 0) +#define FBNIC_RXB_PAUSE_STORM_UNIT_WR 0x0801d /* 0x20074 */ #define FBNIC_RXB_ECN_THLD(n) (0x0801e + (n)) /* 0x20078 + 4*n */ #define FBNIC_RXB_ECN_THLD_CNT 8 #define FBNIC_RXB_ECN_THLD_ON CSR_GENMASK(12, 0) @@ -596,6 +604,9 @@ enum { #define FBNIC_RXB_INTF_CREDIT_MASK2 CSR_GENMASK(11, 8) #define FBNIC_RXB_INTF_CREDIT_MASK3 CSR_GENMASK(15, 12) +#define FBNIC_RXB_ERR_INTR_STS 0x08050 /* 0x20140 */ +#define FBNIC_RXB_ERR_INTR_STS_PS CSR_GENMASK(15, 12) +#define FBNIC_RXB_ERR_INTR_MASK 0x08052 /* 0x20148 */ #define FBNIC_RXB_PAUSE_EVENT_CNT(n) (0x08053 + (n)) /* 0x2014c + 4*n */ #define FBNIC_RXB_DROP_FRMS_STS(n) (0x08057 + (n)) /* 0x2015c + 4*n */ #define FBNIC_RXB_DROP_BYTES_STS_L(n) \ @@ -618,6 +629,7 @@ enum { FBNIC_RXB_ENQUEUE_INDICES = 4 }; +#define FBNIC_RXB_INTR_PS_COUNT(n) (0x080e9 + (n)) /* 0x203a4 + 4*n */ #define FBNIC_RXB_DRBO_FRM_CNT_SRC(n) (0x080f9 + (n)) /* 0x203e4 + 4*n */ #define FBNIC_RXB_DRBO_BYTE_CNT_SRC_L(n) \ (0x080fd + (n)) /* 0x203f4 + 4*n */ @@ -636,6 +648,7 @@ enum { #define FBNIC_RXB_PBUF_FIFO_LEVEL(n) (0x0811d + (n)) /* 0x20474 + 4*n */ +#define FBNIC_RXB_PAUSE_STORM_UNIT_RD 0x08125 /* 0x20494 */ #define FBNIC_RXB_INTEGRITY_ERR(n) (0x0812f + (n)) /* 0x204bc + 4*n */ #define FBNIC_RXB_MAC_ERR(n) (0x08133 + (n)) /* 0x204cc + 4*n */ #define FBNIC_RXB_PARSER_ERR(n) (0x08137 + (n)) /* 0x204dc + 4*n */ @@ -792,6 +805,7 @@ enum { #define FBNIC_CSR_END_PCS 0x10668 /* CSR section delimiter */ #define FBNIC_CSR_START_RSFEC 0x10800 /* CSR section delimiter */ +#define FBNIC_RSFEC_CONTROL(n) (0x10800 + 8 * (n)) /* 0x42000 + 32*n */ /* We have 4 RSFEC engines present in our part, however we are only using 1. * As such only CCW(0) and NCCW(0) will never be non-zero and the other @@ -960,11 +974,24 @@ enum { /* PUL User Registers */ #define FBNIC_CSR_START_PUL_USER 0x31000 /* CSR section delimiter */ #define FBNIC_PUL_OB_TLP_HDR_AW_CFG 0x3103d /* 0xc40f4 */ +#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH_MODE CSR_BIT(20) #define FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH CSR_BIT(19) #define FBNIC_PUL_OB_TLP_HDR_AW_CFG_BME CSR_BIT(18) +#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_RDE_ATTR CSR_GENMASK(17, 15) +#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_RQM_ATTR CSR_GENMASK(14, 12) +#define FBNIC_PUL_OB_TLP_HDR_AW_CFG_TQM_ATTR CSR_GENMASK(11, 9) +enum { + FBNIC_TLP_ATTR_NS = 1, + FBNIC_TLP_ATTR_RO = 2, + FBNIC_TLP_ATTR_IDO = 4, +}; + #define FBNIC_PUL_OB_TLP_HDR_AR_CFG 0x3103e /* 0xc40f8 */ #define FBNIC_PUL_OB_TLP_HDR_AR_CFG_FLUSH CSR_BIT(19) #define FBNIC_PUL_OB_TLP_HDR_AR_CFG_BME CSR_BIT(18) +#define FBNIC_PUL_OB_TLP_HDR_AR_CFG_TDE_ATTR CSR_GENMASK(17, 15) +#define FBNIC_PUL_OB_TLP_HDR_AR_CFG_RQM_ATTR CSR_GENMASK(14, 12) +#define FBNIC_PUL_OB_TLP_HDR_AR_CFG_TQM_ATTR CSR_GENMASK(11, 9) #define FBNIC_PUL_USER_OB_RD_TLP_CNT_31_0 \ 0x3106e /* 0xc41b8 */ #define FBNIC_PUL_USER_OB_RD_DWORD_CNT_31_0 \ @@ -1019,6 +1046,9 @@ enum { #define FBNIC_QUEUE_TWQ_CTL_ENABLE CSR_BIT(1) #define FBNIC_QUEUE_TWQ0_TAIL 0x002 /* 0x008 */ #define FBNIC_QUEUE_TWQ1_TAIL 0x003 /* 0x00c */ +#define FBNIC_QUEUE_TWQ0_PTRS 0x004 /* 0x010 */ +#define FBNIC_QUEUE_TWQ1_PTRS 0x005 /* 0x014 */ +#define FBNIC_QUEUE_TWQ_PTRS_HEAD_MASK CSR_GENMASK(31, 16) #define FBNIC_QUEUE_TWQ0_SIZE 0x00a /* 0x028 */ #define FBNIC_QUEUE_TWQ1_SIZE 0x00b /* 0x02c */ @@ -1042,6 +1072,8 @@ enum { #define FBNIC_QUEUE_TCQ_CTL_ENABLE CSR_BIT(1) #define FBNIC_QUEUE_TCQ_HEAD 0x081 /* 0x204 */ +#define FBNIC_QUEUE_TCQ_PTRS 0x082 /* 0x208 */ +#define FBNIC_QUEUE_TCQ_PTRS_TAIL_MASK CSR_GENMASK(31, 16) #define FBNIC_QUEUE_TCQ_SIZE 0x084 /* 0x210 */ #define FBNIC_QUEUE_TCQ_SIZE_MASK CSR_GENMASK(3, 0) @@ -1075,6 +1107,9 @@ enum { #define FBNIC_QUEUE_RCQ_CTL_ENABLE CSR_BIT(1) #define FBNIC_QUEUE_RCQ_HEAD 0x201 /* 0x804 */ +#define FBNIC_QUEUE_RCQ_PTRS 0x202 /* 0x808 */ +#define FBNIC_QUEUE_RCQ_PTRS_TAIL_MASK CSR_GENMASK(31, 16) +#define FBNIC_QUEUE_RCQ_PTRS_HEAD_MASK CSR_GENMASK(15, 0) #define FBNIC_QUEUE_RCQ_SIZE 0x204 /* 0x810 */ #define FBNIC_QUEUE_RCQ_SIZE_MASK CSR_GENMASK(3, 0) @@ -1090,6 +1125,10 @@ enum { #define FBNIC_QUEUE_BDQ_HPQ_TAIL 0x241 /* 0x904 */ #define FBNIC_QUEUE_BDQ_PPQ_TAIL 0x242 /* 0x908 */ +#define FBNIC_QUEUE_BDQ_HPQ_PTRS 0x243 /* 0x90c */ +#define FBNIC_QUEUE_BDQ_PPQ_PTRS 0x244 /* 0x910 */ +#define FBNIC_QUEUE_BDQ_PTRS_HEAD_MASK CSR_GENMASK(31, 16) +#define FBNIC_QUEUE_BDQ_PTRS_TAIL_MASK CSR_GENMASK(15, 0) #define FBNIC_QUEUE_BDQ_HPQ_SIZE 0x247 /* 0x91c */ #define FBNIC_QUEUE_BDQ_PPQ_SIZE 0x248 /* 0x920 */ @@ -1177,6 +1216,10 @@ enum { #define FBNIC_IPC_MBX_DESC_LEN_MASK DESC_GENMASK(63, 48) #define FBNIC_IPC_MBX_DESC_EOM DESC_BIT(46) #define FBNIC_IPC_MBX_DESC_ADDR_MASK DESC_GENMASK(45, 3) +/* Set with FW_CMPL when the FW completed a descriptor without successfully + * processing it (e.g. a mailbox DMA error); the completion has no valid data. + */ +#define FBNIC_IPC_MBX_DESC_FW_ERR DESC_BIT(2) #define FBNIC_IPC_MBX_DESC_FW_CMPL DESC_BIT(1) #define FBNIC_IPC_MBX_DESC_HOST_CMPL DESC_BIT(0) @@ -1198,4 +1241,21 @@ enum{ FBNIC_CSR_VERSION_V1_0_ASIC = 1, }; +/** + * enum fbnic_reg_self_test_codes - return codes from self test routines + * + * This is the code that is returned from the register self test + * routines. + * + * The test either returns success or the register number + * that failed during the test. + * + * @FBNIC_REG_TEST_SUCCESS: no errors + */ +enum fbnic_reg_self_test_codes { + FBNIC_REG_TEST_SUCCESS = 0, +}; + +enum fbnic_reg_self_test_codes fbnic_csr_regs_test(struct fbnic_dev *fbd); + #endif /* _FBNIC_CSR_H_ */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c index b7238dd967fe..6edfa0aa69f1 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c @@ -7,9 +7,12 @@ #include <linux/seq_file.h> #include "fbnic.h" +#include "fbnic_txrx.h" static struct dentry *fbnic_dbg_root; +/* Descriptor Seq Functions */ + static void fbnic_dbg_desc_break(struct seq_file *s, int i) { while (i--) @@ -18,6 +21,362 @@ static void fbnic_dbg_desc_break(struct seq_file *s, int i) seq_putc(s, '\n'); } +static void fbnic_dbg_ring_show(struct seq_file *s) +{ + struct fbnic_ring *ring = s->private; + unsigned long doorbell_offset; + u32 head = 0, tail = 0; + u32 __iomem *csr_base; + + csr_base = fbnic_ring_csr_base(ring); + doorbell_offset = ring->doorbell - csr_base; + + seq_printf(s, "doorbell CSR: %#05lx q_idx: %d\n", + doorbell_offset, ring->q_idx); + seq_printf(s, "size_mask: %#06x size: %zu flags: 0x%02x\n", + ring->size_mask, ring->size, ring->flags); + seq_printf(s, "SW: head: %#06x tail: %#06x\n", + ring->head, ring->tail); + + switch (doorbell_offset) { + case FBNIC_QUEUE_TWQ0_TAIL: + tail = readl(csr_base + FBNIC_QUEUE_TWQ0_PTRS); + head = FIELD_GET(FBNIC_QUEUE_TWQ_PTRS_HEAD_MASK, tail); + break; + case FBNIC_QUEUE_TWQ1_TAIL: + tail = readl(csr_base + FBNIC_QUEUE_TWQ1_PTRS); + head = FIELD_GET(FBNIC_QUEUE_TWQ_PTRS_HEAD_MASK, tail); + break; + case FBNIC_QUEUE_TCQ_HEAD: + head = readl(csr_base + FBNIC_QUEUE_TCQ_PTRS); + tail = FIELD_GET(FBNIC_QUEUE_TCQ_PTRS_TAIL_MASK, head); + break; + case FBNIC_QUEUE_BDQ_HPQ_TAIL: + tail = readl(csr_base + FBNIC_QUEUE_BDQ_HPQ_PTRS); + head = FIELD_GET(FBNIC_QUEUE_BDQ_PTRS_HEAD_MASK, tail); + break; + case FBNIC_QUEUE_BDQ_PPQ_TAIL: + tail = readl(csr_base + FBNIC_QUEUE_BDQ_PPQ_PTRS); + head = FIELD_GET(FBNIC_QUEUE_BDQ_PTRS_HEAD_MASK, tail); + break; + case FBNIC_QUEUE_RCQ_HEAD: + head = readl(csr_base + FBNIC_QUEUE_RCQ_PTRS); + tail = FIELD_GET(FBNIC_QUEUE_RCQ_PTRS_TAIL_MASK, head); + break; + } + + tail &= FBNIC_QUEUE_BDQ_PTRS_TAIL_MASK; + head &= FBNIC_QUEUE_RCQ_PTRS_HEAD_MASK; + + seq_printf(s, "HW: head: %#06x tail: %#06x\n", head, tail); + + seq_puts(s, "\n"); +} + +static void fbnic_dbg_twd_desc_seq_show(struct seq_file *s, int i) +{ + struct fbnic_ring *ring = s->private; + u64 twd = le64_to_cpu(ring->desc[i]); + + switch (FIELD_GET(FBNIC_TWD_TYPE_MASK, twd)) { + case FBNIC_TWD_TYPE_META: + seq_printf(s, "%04x %#06llx %llx %llx %llx %llx %llx %#llx %#llx %llx %#04llx %#04llx %llx %#04llx\n", + i, FIELD_GET(FBNIC_TWD_LEN_MASK, twd), + FIELD_GET(FBNIC_TWD_TYPE_MASK, twd), + FIELD_GET(FBNIC_TWD_FLAG_REQ_COMPLETION, twd), + FIELD_GET(FBNIC_TWD_FLAG_REQ_CSO, twd), + FIELD_GET(FBNIC_TWD_FLAG_REQ_LSO, twd), + FIELD_GET(FBNIC_TWD_FLAG_REQ_TS, twd), + FIELD_GET(FBNIC_TWD_L4_HLEN_MASK, twd), + FIELD_GET(FBNIC_TWD_CSUM_OFFSET_MASK, twd), + FIELD_GET(FBNIC_TWD_L4_TYPE_MASK, twd), + FIELD_GET(FBNIC_TWD_L3_IHLEN_MASK, twd), + FIELD_GET(FBNIC_TWD_L3_OHLEN_MASK, twd), + FIELD_GET(FBNIC_TWD_L3_TYPE_MASK, twd), + FIELD_GET(FBNIC_TWD_L2_HLEN_MASK, twd)); + break; + default: + seq_printf(s, "%04x %#06llx %llx %#014llx\n", i, + FIELD_GET(FBNIC_TWD_LEN_MASK, twd), + FIELD_GET(FBNIC_TWD_TYPE_MASK, twd), + FIELD_GET(FBNIC_TWD_ADDR_MASK, twd)); + break; + } +} + +static int fbnic_dbg_twq_desc_seq_show(struct seq_file *s, void *v) +{ + struct fbnic_ring *ring = s->private; + char hdr[80]; + int i; + + /* Generate header on first entry */ + fbnic_dbg_ring_show(s); + snprintf(hdr, sizeof(hdr), "%4s %5s %s %s\n", + "DESC", "LEN/MSS", "T", "METADATA/TIMESTAMP/BUFFER_ADDR"); + seq_puts(s, hdr); + fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr))); + + /* Display descriptor */ + if (!ring->desc) { + seq_puts(s, "Descriptor ring not allocated.\n"); + return 0; + } + + for (i = 0; i <= ring->size_mask; i++) + fbnic_dbg_twd_desc_seq_show(s, i); + + return 0; +} + +static int fbnic_dbg_tcq_desc_seq_show(struct seq_file *s, void *v) +{ + struct fbnic_ring *ring = s->private; + char hdr[80]; + int i; + + /* Generate header on first entry */ + fbnic_dbg_ring_show(s); + snprintf(hdr, sizeof(hdr), "%4s %s %s %s %5s %-16s %-6s %-6s\n", + "DESC", "D", "T", "Q", "STATUS", "TIMESTAMP", "HEAD1", "HEAD0"); + seq_puts(s, hdr); + fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr))); + + /* Display descriptor */ + if (!ring->desc) { + seq_puts(s, "Descriptor ring not allocated.\n"); + return 0; + } + + for (i = 0; i <= ring->size_mask; i++) { + u64 tcd = le64_to_cpu(ring->desc[i]); + + switch (FIELD_GET(FBNIC_TCD_TYPE_MASK, tcd)) { + case FBNIC_TCD_TYPE_0: + seq_printf(s, "%04x %llx %llx %llx %#05llx %-17s %#06llx %#06llx\n", + i, FIELD_GET(FBNIC_TCD_DONE, tcd), + FIELD_GET(FBNIC_TCD_TYPE_MASK, tcd), + FIELD_GET(FBNIC_TCD_TWQ1, tcd), + FIELD_GET(FBNIC_TCD_STATUS_MASK, tcd), + "", + FIELD_GET(FBNIC_TCD_TYPE0_HEAD1_MASK, tcd), + FIELD_GET(FBNIC_TCD_TYPE0_HEAD0_MASK, tcd)); + break; + case FBNIC_TCD_TYPE_1: + seq_printf(s, "%04x %llx %llx %llx %#05llx %#012llx\n", + i, FIELD_GET(FBNIC_TCD_DONE, tcd), + FIELD_GET(FBNIC_TCD_TYPE_MASK, tcd), + FIELD_GET(FBNIC_TCD_TWQ1, tcd), + FIELD_GET(FBNIC_TCD_STATUS_MASK, tcd), + FIELD_GET(FBNIC_TCD_TYPE1_TS_MASK, tcd)); + break; + default: + break; + } + } + + return 0; +} + +static int fbnic_dbg_bdq_desc_seq_show(struct seq_file *s, void *v) +{ + struct fbnic_ring *ring = s->private; + char hdr[80]; + int i; + + /* Generate header on first entry */ + fbnic_dbg_ring_show(s); + snprintf(hdr, sizeof(hdr), "%4s %-4s %s\n", + "DESC", "ID", "BUFFER_ADDR"); + seq_puts(s, hdr); + fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr))); + + /* Display descriptor */ + if (!ring->desc) { + seq_puts(s, "Descriptor ring not allocated.\n"); + return 0; + } + + for (i = 0; i < (ring->size_mask + 1) * FBNIC_BD_FRAG_COUNT; i++) { + u64 bd = le64_to_cpu(ring->desc[i]); + + seq_printf(s, "%04x %#04llx %#014llx\n", i, + FIELD_GET(FBNIC_BD_DESC_ID_MASK, bd), + FIELD_GET(FBNIC_BD_DESC_ADDR_MASK, bd)); + } + + return 0; +} + +static void fbnic_dbg_rcd_desc_seq_show(struct seq_file *s, int i) +{ + struct fbnic_ring *ring = s->private; + u64 rcd = le64_to_cpu(ring->desc[i]); + + switch (FIELD_GET(FBNIC_RCD_TYPE_MASK, rcd)) { + case FBNIC_RCD_TYPE_HDR_AL: + case FBNIC_RCD_TYPE_PAY_AL: + seq_printf(s, "%04x %llx %llx %llx %#06llx %#06llx %#06llx\n", + i, FIELD_GET(FBNIC_RCD_DONE, rcd), + FIELD_GET(FBNIC_RCD_TYPE_MASK, rcd), + FIELD_GET(FBNIC_RCD_AL_PAGE_FIN, rcd), + FIELD_GET(FBNIC_RCD_AL_BUFF_OFF_MASK, rcd), + FIELD_GET(FBNIC_RCD_AL_BUFF_LEN_MASK, rcd), + FIELD_GET(FBNIC_RCD_AL_BUFF_ID_MASK, rcd)); + break; + case FBNIC_RCD_TYPE_OPT_META: + seq_printf(s, "%04x %llx %llx %llx %llx %llx %#06llx %#012llx\n", + i, FIELD_GET(FBNIC_RCD_DONE, rcd), + FIELD_GET(FBNIC_RCD_TYPE_MASK, rcd), + FIELD_GET(FBNIC_RCD_OPT_META_TYPE_MASK, rcd), + FIELD_GET(FBNIC_RCD_OPT_META_TS, rcd), + FIELD_GET(FBNIC_RCD_OPT_META_ACTION, rcd), + FIELD_GET(FBNIC_RCD_OPT_META_ACTION_MASK, rcd), + FIELD_GET(FBNIC_RCD_OPT_META_TS_MASK, rcd)); + break; + case FBNIC_RCD_TYPE_META: + seq_printf(s, "%04x %llx %llx %llx %llx %llx %llx %llx %llx %llx %#06llx %#010llx\n", + i, FIELD_GET(FBNIC_RCD_DONE, rcd), + FIELD_GET(FBNIC_RCD_TYPE_MASK, rcd), + FIELD_GET(FBNIC_RCD_META_ECN, rcd), + FIELD_GET(FBNIC_RCD_META_L4_CSUM_UNNECESSARY, rcd), + FIELD_GET(FBNIC_RCD_META_ERR_MAC_EOP, rcd), + FIELD_GET(FBNIC_RCD_META_ERR_TRUNCATED_FRAME, rcd), + FIELD_GET(FBNIC_RCD_META_ERR_PARSER, rcd), + FIELD_GET(FBNIC_RCD_META_L4_TYPE_MASK, rcd), + FIELD_GET(FBNIC_RCD_META_L3_TYPE_MASK, rcd), + FIELD_GET(FBNIC_RCD_META_L2_CSUM_MASK, rcd), + FIELD_GET(FBNIC_RCD_META_RSS_HASH_MASK, rcd)); + break; + } +} + +static int fbnic_dbg_rcq_desc_seq_show(struct seq_file *s, void *v) +{ + struct fbnic_ring *ring = s->private; + char hdr[80]; + int i; + + /* Generate header on first entry */ + fbnic_dbg_ring_show(s); + snprintf(hdr, sizeof(hdr), + "%18s %s %s\n", "OFFSET/", "L", "L"); + seq_puts(s, hdr); + snprintf(hdr, sizeof(hdr), + "%4s %s %s %s %s %s %s %s %s %s %-8s %s\n", + "DESC", "D", "T", "F", "C", "M", "T", "P", "4", "3", "LEN/CSUM", "ID/TS/RSS"); + seq_puts(s, hdr); + fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr))); + + /* Display descriptor */ + if (!ring->desc) { + seq_puts(s, "Descriptor ring not allocated.\n"); + return 0; + } + + for (i = 0; i <= ring->size_mask; i++) + fbnic_dbg_rcd_desc_seq_show(s, i); + + return 0; +} + +static int fbnic_dbg_desc_open(struct inode *inode, struct file *file) +{ + struct fbnic_ring *ring = inode->i_private; + int (*show)(struct seq_file *s, void *v); + + switch (ring->doorbell - fbnic_ring_csr_base(ring)) { + case FBNIC_QUEUE_TWQ0_TAIL: + case FBNIC_QUEUE_TWQ1_TAIL: + show = fbnic_dbg_twq_desc_seq_show; + break; + case FBNIC_QUEUE_TCQ_HEAD: + show = fbnic_dbg_tcq_desc_seq_show; + break; + case FBNIC_QUEUE_BDQ_HPQ_TAIL: + case FBNIC_QUEUE_BDQ_PPQ_TAIL: + show = fbnic_dbg_bdq_desc_seq_show; + break; + case FBNIC_QUEUE_RCQ_HEAD: + show = fbnic_dbg_rcq_desc_seq_show; + break; + default: + return -EINVAL; + } + + return single_open(file, show, ring); +} + +static const struct file_operations fbnic_dbg_desc_fops = { + .owner = THIS_MODULE, + .open = fbnic_dbg_desc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void fbnic_dbg_nv_init(struct fbnic_napi_vector *nv) +{ + struct fbnic_dev *fbd = nv->fbd; + char name[16]; + int i, j; + + /* Generate a folder for each napi vector */ + snprintf(name, sizeof(name), "nv.%03d", nv->v_idx); + + nv->dbg_nv = debugfs_create_dir(name, fbd->dbg_fbd); + + /* Generate a file for each Tx ring in the napi vector */ + for (i = 0; i < nv->txt_count; i++) { + struct fbnic_q_triad *qt = &nv->qt[i]; + unsigned int hw_idx; + + hw_idx = fbnic_ring_csr_base(&qt->cmpl) - + &fbd->uc_addr0[FBNIC_QUEUE(0)]; + hw_idx /= FBNIC_QUEUE_STRIDE; + + snprintf(name, sizeof(name), "twq0.%03d", hw_idx); + debugfs_create_file(name, 0400, nv->dbg_nv, &qt->sub0, + &fbnic_dbg_desc_fops); + + snprintf(name, sizeof(name), "twq1.%03d", hw_idx); + debugfs_create_file(name, 0400, nv->dbg_nv, &qt->sub1, + &fbnic_dbg_desc_fops); + + snprintf(name, sizeof(name), "tcq.%03d", hw_idx); + debugfs_create_file(name, 0400, nv->dbg_nv, &qt->cmpl, + &fbnic_dbg_desc_fops); + } + + /* Generate a file for each Rx ring in the napi vector */ + for (j = 0; j < nv->rxt_count; j++, i++) { + struct fbnic_q_triad *qt = &nv->qt[i]; + unsigned int hw_idx; + + hw_idx = fbnic_ring_csr_base(&qt->cmpl) - + &fbd->uc_addr0[FBNIC_QUEUE(0)]; + hw_idx /= FBNIC_QUEUE_STRIDE; + + snprintf(name, sizeof(name), "hpq.%03d", hw_idx); + debugfs_create_file(name, 0400, nv->dbg_nv, &qt->sub0, + &fbnic_dbg_desc_fops); + + snprintf(name, sizeof(name), "ppq.%03d", hw_idx); + debugfs_create_file(name, 0400, nv->dbg_nv, &qt->sub1, + &fbnic_dbg_desc_fops); + + snprintf(name, sizeof(name), "rcq.%03d", hw_idx); + debugfs_create_file(name, 0400, nv->dbg_nv, &qt->cmpl, + &fbnic_dbg_desc_fops); + } +} + +void fbnic_dbg_nv_exit(struct fbnic_napi_vector *nv) +{ + debugfs_remove_recursive(nv->dbg_nv); + nv->dbg_nv = NULL; +} + static int fbnic_dbg_mac_addr_show(struct seq_file *s, void *v) { struct fbnic_dev *fbd = s->private; @@ -170,6 +529,52 @@ static int fbnic_dbg_ipo_dst_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_ipo_dst); +static void fbnic_dbg_fw_mbx_display(struct seq_file *s, + struct fbnic_dev *fbd, int mbx_idx) +{ + struct fbnic_fw_mbx *mbx = &fbd->mbx[mbx_idx]; + char hdr[80]; + int i; + + /* Generate header */ + seq_puts(s, mbx_idx == FBNIC_IPC_MBX_RX_IDX ? "Rx\n" : "Tx\n"); + + seq_printf(s, "Rdy: %d Head: %d Tail: %d resp_error: %llu\n", + mbx->ready, mbx->head, mbx->tail, mbx->resp_error); + + snprintf(hdr, sizeof(hdr), "%3s %-4s %s %-12s %s %-3s %-16s\n", + "Idx", "Len", "E", "Addr", "F", "H", "Raw"); + seq_puts(s, hdr); + fbnic_dbg_desc_break(s, strnlen(hdr, sizeof(hdr))); + + for (i = 0; i < FBNIC_IPC_MBX_DESC_LEN; i++) { + u64 desc = __fbnic_mbx_rd_desc(fbd, mbx_idx, i); + + seq_printf(s, "%-3.2d %04lld %d %012llx %d %-3d %016llx\n", + i, FIELD_GET(FBNIC_IPC_MBX_DESC_LEN_MASK, desc), + !!(desc & FBNIC_IPC_MBX_DESC_EOM), + desc & FBNIC_IPC_MBX_DESC_ADDR_MASK, + !!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL), + !!(desc & FBNIC_IPC_MBX_DESC_HOST_CMPL), + desc); + } +} + +static int fbnic_dbg_fw_mbx_show(struct seq_file *s, void *v) +{ + struct fbnic_dev *fbd = s->private; + + fbnic_dbg_fw_mbx_display(s, fbd, FBNIC_IPC_MBX_RX_IDX); + + /* Add blank line between Rx and Tx */ + seq_puts(s, "\n"); + + fbnic_dbg_fw_mbx_display(s, fbd, FBNIC_IPC_MBX_TX_IDX); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(fbnic_dbg_fw_mbx); + static int fbnic_dbg_fw_log_show(struct seq_file *s, void *v) { struct fbnic_dev *fbd = s->private; @@ -249,6 +654,8 @@ void fbnic_dbg_fbd_init(struct fbnic_dev *fbd) &fbnic_dbg_ipo_src_fops); debugfs_create_file("ipo_dst", 0400, fbd->dbg_fbd, fbd, &fbnic_dbg_ipo_dst_fops); + debugfs_create_file("fw_mbx", 0400, fbd->dbg_fbd, fbd, + &fbnic_dbg_fw_mbx_fops); debugfs_create_file("fw_log", 0400, fbd->dbg_fbd, fbd, &fbnic_dbg_fw_log_fops); } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c b/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c index b62b1d5b1453..546e1c12d287 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c @@ -178,7 +178,7 @@ fbnic_flash_start(struct fbnic_dev *fbd, struct pldmfw_component *component) goto cmpl_free; /* Wait for firmware to ack firmware upgrade start */ - if (wait_for_completion_timeout(&cmpl->done, 10 * HZ)) + if (fbnic_mbx_wait_for_cmpl(cmpl)) err = cmpl->result; else err = -ETIMEDOUT; @@ -252,7 +252,7 @@ fbnic_flash_component(struct pldmfw *context, goto err_no_msg; while (offset < size) { - if (!wait_for_completion_timeout(&cmpl->done, 15 * HZ)) { + if (!fbnic_mbx_wait_for_cmpl(cmpl)) { err = -ETIMEDOUT; break; } @@ -390,7 +390,7 @@ static int fbnic_fw_reporter_dump(struct devlink_health_reporter *reporter, "Failed to transmit core dump info msg"); goto cmpl_free; } - if (!wait_for_completion_timeout(&fw_cmpl->done, 2 * HZ)) { + if (!fbnic_mbx_wait_for_cmpl(fw_cmpl)) { NL_SET_ERR_MSG_MOD(extack, "Timed out waiting on core dump info"); err = -ETIMEDOUT; @@ -447,7 +447,7 @@ static int fbnic_fw_reporter_dump(struct devlink_health_reporter *reporter, goto cmpl_cleanup; } - if (wait_for_completion_timeout(&fw_cmpl->done, 2 * HZ)) { + if (fbnic_mbx_wait_for_cmpl(fw_cmpl)) { reinit_completion(&fw_cmpl->done); } else { NL_SET_ERR_MSG_FMT_MOD(extack, @@ -647,6 +647,7 @@ struct fbnic_dev *fbnic_devlink_alloc(struct pci_dev *pdev) fbd->dsn = pci_get_dsn(pdev); fbd->mps = pcie_get_mps(pdev); fbd->readrq = pcie_get_readrq(pdev); + fbd->relaxed_ord = pcie_relaxed_ordering_enabled(pdev); fbd->mac_addr_boundary = FBNIC_RPC_TCAM_MACDA_DEFAULT_BOUNDARY; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c index 693ebdf38705..76e9a545bb16 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c @@ -126,6 +126,20 @@ static const struct fbnic_stat fbnic_gstrings_xdp_stats[] = { #define FBNIC_STATS_LEN \ (FBNIC_HW_STATS_LEN + FBNIC_XDP_STATS_LEN * FBNIC_MAX_XDPQS) +enum fbnic_self_test_results { + TEST_REG = 0, + TEST_MSIX, + TEST_MBX, +}; + +static const char fbnic_gstrings_self_test[][ETH_GSTRING_LEN] = { + [TEST_REG] = "Register test (offline)", + [TEST_MSIX] = "MSI-X Interrupt test (offline)", + [TEST_MBX] = "FW mailbox test (on/offline)", +}; + +#define FBNIC_TEST_LEN ARRAY_SIZE(fbnic_gstrings_self_test) + static void fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { @@ -299,6 +313,11 @@ fbnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, kernel_ring->hds_thresh = fbn->hds_thresh; } +static u32 fbnic_ring_size_pow2(u32 size) +{ + return size ? roundup_pow_of_two(size) : 0; +} + static void fbnic_set_rings(struct fbnic_net *fbn, struct ethtool_ringparam *ring, struct kernel_ethtool_ringparam *kernel_ring) @@ -320,10 +339,10 @@ fbnic_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, struct fbnic_net *clone; int err; - ring->rx_pending = roundup_pow_of_two(ring->rx_pending); - ring->rx_mini_pending = roundup_pow_of_two(ring->rx_mini_pending); - ring->rx_jumbo_pending = roundup_pow_of_two(ring->rx_jumbo_pending); - ring->tx_pending = roundup_pow_of_two(ring->tx_pending); + ring->rx_pending = fbnic_ring_size_pow2(ring->rx_pending); + ring->rx_mini_pending = fbnic_ring_size_pow2(ring->rx_mini_pending); + ring->rx_jumbo_pending = fbnic_ring_size_pow2(ring->rx_jumbo_pending); + ring->tx_pending = fbnic_ring_size_pow2(ring->tx_pending); /* These are absolute minimums allowing the device and driver to operate * but not necessarily guarantee reasonable performance. Settings below @@ -475,6 +494,10 @@ static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data) for (i = 0; i < FBNIC_MAX_XDPQS; i++) fbnic_get_xdp_queue_strings(&data, i); break; + case ETH_SS_TEST: + memcpy(data, fbnic_gstrings_self_test, + sizeof(fbnic_gstrings_self_test)); + break; } } @@ -566,6 +589,8 @@ static int fbnic_get_sset_count(struct net_device *dev, int sset) switch (sset) { case ETH_SS_STATS: return FBNIC_STATS_LEN; + case ETH_SS_TEST: + return FBNIC_TEST_LEN; default: return -EOPNOTSUPP; } @@ -825,6 +850,13 @@ static int fbnic_get_cls_rule(struct fbnic_net *fbn, struct ethtool_rxnfc *cmd) return 0; } +static u32 fbnic_get_rx_ring_count(struct net_device *netdev) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + + return fbn->num_rx_queues; +} + static int fbnic_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, u32 *rule_locs) { @@ -833,10 +865,6 @@ static int fbnic_get_rxnfc(struct net_device *netdev, u32 special = 0; switch (cmd->cmd) { - case ETHTOOL_GRXRINGS: - cmd->data = fbn->num_rx_queues; - ret = 0; - break; case ETHTOOL_GRXCLSRULE: ret = fbnic_get_cls_rule(fbn, cmd); break; @@ -1142,6 +1170,9 @@ ipv6_flow: return -EINVAL; } + dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DMA_HINT, + FBNIC_RCD_HDR_AL_DMA_HINT_L4); + /* Write action table values */ act_tcam->dest = dest; act_tcam->rss_en_mask = fbnic_flow_hash_2_rss_en_mask(fbn, hash_idx); @@ -1472,6 +1503,78 @@ fbnic_remove_rxfh_context(struct net_device *netdev, return 0; } +static int fbnic_ethtool_regs_test(struct net_device *netdev, u64 *data) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_dev *fbd = fbn->fbd; + + *data = fbnic_csr_regs_test(fbd); + + return !!*data; +} + +/** + * fbnic_ethtool_msix_test - Verify behavior of NIC interrupts + * @netdev: netdev device to test + * @data: Pointer to results storage + * + * This function is meant to test the global interrupt registers and the + * PCIe IP MSI-X functionality. It essentially goes through and tests + * test various combinations of the set, clear, and mask bits in order to + * verify the behavior is as we expect it to be from the driver. + * + * Return: non-zero on failure. + **/ +static int fbnic_ethtool_msix_test(struct net_device *netdev, u64 *data) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_dev *fbd = fbn->fbd; + + *data = fbnic_msix_test(fbd); + + return !!*data; +} + +static int fbnic_ethtool_mbx_self_test(struct net_device *netdev, u64 *data) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_dev *fbd = fbn->fbd; + + *data = fbnic_fw_mbx_self_test(fbd); + + return !!*data; +} + +static void fbnic_self_test(struct net_device *netdev, + struct ethtool_test *eth_test, u64 *data) +{ + bool if_running = netif_running(netdev); + + if (fbnic_ethtool_mbx_self_test(netdev, &data[TEST_MBX])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + if (!(eth_test->flags & ETH_TEST_FL_OFFLINE)) { + data[TEST_REG] = 0; + data[TEST_MSIX] = 0; + return; + } + + if (if_running) + netif_close(netdev); + + if (fbnic_ethtool_regs_test(netdev, &data[TEST_REG])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + if (fbnic_ethtool_msix_test(netdev, &data[TEST_MSIX])) + eth_test->flags |= ETH_TEST_FL_FAILED; + + if (if_running && netif_open(netdev, NULL)) { + netdev_err(netdev, + "Failed to re-initialize hardware following test\n"); + eth_test->flags |= ETH_TEST_FL_FAILED; + } +} + static void fbnic_get_channels(struct net_device *netdev, struct ethtool_channels *ch) { @@ -1635,6 +1738,47 @@ static void fbnic_get_ts_stats(struct net_device *netdev, } } +static int fbnic_get_tunable(struct net_device *netdev, + const struct ethtool_tunable *tun, + void *data) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + int err = 0; + + switch (tun->id) { + case ETHTOOL_PFC_PREVENTION_TOUT: + *(u16 *)data = fbn->fbd->ps_timeout; + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static int fbnic_set_tunable(struct net_device *netdev, + const struct ethtool_tunable *tun, + const void *data) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + int err; + + switch (tun->id) { + case ETHTOOL_PFC_PREVENTION_TOUT: { + u16 ps_timeout = *(u16 *)data; + + err = fbnic_mac_ps_protect_to_config(fbn->fbd, ps_timeout); + break; + } + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + static int fbnic_get_module_eeprom_by_page(struct net_device *netdev, const struct ethtool_module_eeprom *page_data, @@ -1671,7 +1815,7 @@ fbnic_get_module_eeprom_by_page(struct net_device *netdev, goto exit_free; } - if (!wait_for_completion_timeout(&fw_cmpl->done, 2 * HZ)) { + if (!fbnic_mbx_wait_for_cmpl(fw_cmpl)) { err = -ETIMEDOUT; NL_SET_ERR_MSG_MOD(extack, "Timed out waiting for firmware response"); @@ -1707,6 +1851,7 @@ fbnic_get_pause_stats(struct net_device *netdev, struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_mac_stats *mac_stats; struct fbnic_dev *fbd = fbn->fbd; + u64 tx_ps_events; mac_stats = &fbd->hw_stats.mac; @@ -1714,6 +1859,8 @@ fbnic_get_pause_stats(struct net_device *netdev, pause_stats->tx_pause_frames = mac_stats->pause.tx_pause_frames.value; pause_stats->rx_pause_frames = mac_stats->pause.rx_pause_frames.value; + tx_ps_events = mac_stats->pause.tx_pause_storm_events.value; + pause_stats->tx_pause_storm_events = tx_ps_events; } static void @@ -1878,6 +2025,13 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT | ETHTOOL_RING_USE_HDS_THRS, .rxfh_max_num_contexts = FBNIC_RPC_RSS_TBL_COUNT, + .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS | + ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM | + ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM | + ETHTOOL_OP_NEEDS_RTNL_SCHANNELS | + ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM | + ETHTOOL_OP_NEEDS_RTNL_GLINK | + ETHTOOL_OP_NEEDS_RTNL_TEST, .get_drvinfo = fbnic_get_drvinfo, .get_regs_len = fbnic_get_regs_len, .get_regs = fbnic_get_regs, @@ -1890,11 +2044,13 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .get_pause_stats = fbnic_get_pause_stats, .get_pauseparam = fbnic_phylink_get_pauseparam, .set_pauseparam = fbnic_phylink_set_pauseparam, + .self_test = fbnic_self_test, .get_strings = fbnic_get_strings, .get_ethtool_stats = fbnic_get_ethtool_stats, .get_sset_count = fbnic_get_sset_count, .get_rxnfc = fbnic_get_rxnfc, .set_rxnfc = fbnic_set_rxnfc, + .get_rx_ring_count = fbnic_get_rx_ring_count, .get_rxfh_key_size = fbnic_get_rxfh_key_size, .get_rxfh_indir_size = fbnic_get_rxfh_indir_size, .get_rxfh = fbnic_get_rxfh, @@ -1908,6 +2064,8 @@ static const struct ethtool_ops fbnic_ethtool_ops = { .set_channels = fbnic_set_channels, .get_ts_info = fbnic_get_ts_info, .get_ts_stats = fbnic_get_ts_stats, + .get_tunable = fbnic_get_tunable, + .set_tunable = fbnic_set_tunable, .get_link_ksettings = fbnic_phylink_ethtool_ksettings_get, .get_fec_stats = fbnic_get_fec_stats, .get_fecparam = fbnic_phylink_get_fecparam, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c index d8d9b6cfde82..6d7eb8479edf 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c @@ -40,7 +40,7 @@ static void __fbnic_mbx_invalidate_desc(struct fbnic_dev *fbd, int mbx_idx, fw_wr32(fbd, desc_offset + 1, 0); } -static u64 __fbnic_mbx_rd_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx) +u64 __fbnic_mbx_rd_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx) { u32 desc_offset = FBNIC_IPC_MBX(mbx_idx, desc_idx); u64 desc; @@ -60,8 +60,15 @@ static void fbnic_mbx_reset_desc_ring(struct fbnic_dev *fbd, int mbx_idx) */ switch (mbx_idx) { case FBNIC_IPC_MBX_RX_IDX: + /* Clearing BME blocks the device from writing to the host + * but leaves the requests parked in the write pipeline. The + * write path only clears outstanding requests when both FLUSH + * and FLUSH_MODE are set; FLUSH_MODE lets them drain without + * landing on the host. + */ wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AW_CFG, - FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH); + FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH | + FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH_MODE); break; case FBNIC_IPC_MBX_TX_IDX: wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AR_CFG, @@ -205,8 +212,7 @@ static int fbnic_mbx_alloc_rx_msgs(struct fbnic_dev *fbd) while (!err && count--) { struct fbnic_tlv_msg *msg; - msg = (struct fbnic_tlv_msg *)__get_free_page(GFP_ATOMIC | - __GFP_NOWARN); + msg = (struct fbnic_tlv_msg *)__get_free_page(GFP_KERNEL); if (!msg) { err = -ENOMEM; break; @@ -286,6 +292,12 @@ static void fbnic_mbx_process_tx_msgs(struct fbnic_dev *fbd) if (!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL)) break; + if (desc & FBNIC_IPC_MBX_DESC_FW_ERR) { + tx_mbx->resp_error++; + dev_warn_ratelimited(fbd->dev, + "FW completed a Tx mailbox request with an error\n"); + } + fbnic_mbx_unmap_and_free_msg(fbd, FBNIC_IPC_MBX_TX_IDX, head); head++; @@ -380,6 +392,37 @@ fbnic_fw_get_cmpl_by_type(struct fbnic_dev *fbd, u32 msg_type) } /** + * fbnic_fw_xmit_test_msg - Create and transmit a test message to FW mailbox + * @fbd: FBNIC device structure + * @cmpl: fw completion struct + * + * Return: zero on success, negative value on failure + * + * Generates a single page mailbox test message and places it in the Tx + * mailbox queue. Expectation is that the FW will validate that the nested + * value matches the external values, and then will echo them back to us. + * + * Also sets a completion slot for use in the completion wait calls when + * the cmpl arg is non-NULL. + */ +int fbnic_fw_xmit_test_msg(struct fbnic_dev *fbd, + struct fbnic_fw_completion *cmpl) +{ + struct fbnic_tlv_msg *test_msg; + int err; + + test_msg = fbnic_tlv_test_create(fbd); + if (!test_msg) + return -ENOMEM; + + err = fbnic_mbx_map_req_w_cmpl(fbd, test_msg, cmpl); + if (err) + free_page((unsigned long)test_msg); + + return err; +} + +/** * fbnic_fw_xmit_simple_msg - Transmit a simple single TLV message w/o data * @fbd: FBNIC device structure * @msg_type: ENUM value indicating message type to send @@ -416,8 +459,9 @@ static int fbnic_fw_xmit_simple_msg(struct fbnic_dev *fbd, u32 msg_type) return err; } -static void fbnic_mbx_init_desc_ring(struct fbnic_dev *fbd, int mbx_idx) +static int fbnic_mbx_init_desc_ring(struct fbnic_dev *fbd, int mbx_idx) { + u8 tlp_attr = fbd->relaxed_ord ? FBNIC_TLP_ATTR_RO : 0; struct fbnic_fw_mbx *mbx = &fbd->mbx[mbx_idx]; mbx->ready = true; @@ -426,17 +470,28 @@ static void fbnic_mbx_init_desc_ring(struct fbnic_dev *fbd, int mbx_idx) case FBNIC_IPC_MBX_RX_IDX: /* Enable DMA writes from the device */ wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AW_CFG, - FBNIC_PUL_OB_TLP_HDR_AW_CFG_BME); + FBNIC_PUL_OB_TLP_HDR_AW_CFG_BME | + FIELD_PREP(FBNIC_PUL_OB_TLP_HDR_AW_CFG_RDE_ATTR, + tlp_attr) | + FIELD_PREP(FBNIC_PUL_OB_TLP_HDR_AW_CFG_TQM_ATTR, + tlp_attr)); /* Make sure we have a page for the FW to write to */ - fbnic_mbx_alloc_rx_msgs(fbd); - break; + return fbnic_mbx_alloc_rx_msgs(fbd); case FBNIC_IPC_MBX_TX_IDX: /* Enable DMA reads from the device */ wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AR_CFG, - FBNIC_PUL_OB_TLP_HDR_AR_CFG_BME); + FBNIC_PUL_OB_TLP_HDR_AR_CFG_BME | + FIELD_PREP(FBNIC_PUL_OB_TLP_HDR_AR_CFG_TDE_ATTR, + tlp_attr) | + FIELD_PREP(FBNIC_PUL_OB_TLP_HDR_AR_CFG_RQM_ATTR, + tlp_attr) | + FIELD_PREP(FBNIC_PUL_OB_TLP_HDR_AR_CFG_TQM_ATTR, + tlp_attr)); break; } + + return 0; } static bool fbnic_mbx_event(struct fbnic_dev *fbd) @@ -484,15 +539,10 @@ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership) goto free_message; } - err = fbnic_mbx_map_tlv_msg(fbd, msg); - if (err) - goto free_message; - /* Initialize heartbeat, set last response to 1 second in the past * so that we will trigger a timeout if the firmware doesn't respond */ fbd->last_heartbeat_response = req_time - HZ; - fbd->last_heartbeat_request = req_time; /* Set prev_firmware_time to 0 to avoid triggering firmware crash @@ -500,6 +550,10 @@ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership) */ fbd->prev_firmware_time = 0; + err = fbnic_mbx_map_tlv_msg(fbd, msg); + if (err) + goto free_message; + /* Set heartbeat detection based on if we are taking ownership */ fbd->fw_heartbeat_enabled = take_ownership; @@ -1556,7 +1610,29 @@ free_message: return err; } +static int +fbnic_fw_parser_test(void *opaque, struct fbnic_tlv_msg **results) +{ + struct fbnic_fw_completion *cmpl; + struct fbnic_dev *fbd = opaque; + int err; + + /* find cmpl */ + cmpl = fbnic_fw_get_cmpl_by_type(fbd, FBNIC_TLV_MSG_ID_TEST); + if (!cmpl) + return -ENOSPC; + + err = fbnic_tlv_parser_test(opaque, results); + + cmpl->result = err; + complete(&cmpl->done); + fbnic_fw_put_cmpl(cmpl); + + return err; +} + static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = { + FBNIC_TLV_PARSER(TEST, fbnic_tlv_test_index, fbnic_fw_parser_test), FBNIC_TLV_PARSER(FW_CAP_RESP, fbnic_fw_cap_resp_index, fbnic_fw_parse_cap_resp), FBNIC_TLV_PARSER(OWNERSHIP_RESP, fbnic_ownership_resp_index, @@ -1592,7 +1668,7 @@ static const struct fbnic_tlv_parser fbnic_fw_tlv_parser[] = { static void fbnic_mbx_process_rx_msgs(struct fbnic_dev *fbd) { struct fbnic_fw_mbx *rx_mbx = &fbd->mbx[FBNIC_IPC_MBX_RX_IDX]; - u8 head = rx_mbx->head; + u8 head = rx_mbx->head, tail = rx_mbx->tail; u64 desc, length; while (head != rx_mbx->tail) { @@ -1603,8 +1679,15 @@ static void fbnic_mbx_process_rx_msgs(struct fbnic_dev *fbd) if (!(desc & FBNIC_IPC_MBX_DESC_FW_CMPL)) break; - dma_unmap_single(fbd->dev, rx_mbx->buf_info[head].addr, - PAGE_SIZE, DMA_FROM_DEVICE); + if (desc & FBNIC_IPC_MBX_DESC_FW_ERR) { + rx_mbx->resp_error++; + dev_warn_ratelimited(fbd->dev, + "FW reported an error on an Rx mailbox message; dropping\n"); + goto next_page; + } + + dma_sync_single_for_cpu(fbd->dev, rx_mbx->buf_info[head].addr, + FBNIC_RX_PAGE_SIZE, DMA_FROM_DEVICE); msg = rx_mbx->buf_info[head].msg; @@ -1637,19 +1720,26 @@ static void fbnic_mbx_process_rx_msgs(struct fbnic_dev *fbd) dev_dbg(fbd->dev, "Parsed msg type %d\n", msg->hdr.type); next_page: + fw_wr32(fbd, FBNIC_IPC_MBX(FBNIC_IPC_MBX_RX_IDX, head), 0); - free_page((unsigned long)rx_mbx->buf_info[head].msg); + rx_mbx->buf_info[tail] = rx_mbx->buf_info[head]; rx_mbx->buf_info[head].msg = NULL; + rx_mbx->buf_info[head].addr = 0; - head++; - head %= FBNIC_IPC_MBX_DESC_LEN; + __fbnic_mbx_wr_desc(fbd, FBNIC_IPC_MBX_RX_IDX, tail, + FIELD_PREP(FBNIC_IPC_MBX_DESC_LEN_MASK, + FBNIC_RX_PAGE_SIZE) | + (rx_mbx->buf_info[tail].addr & + FBNIC_IPC_MBX_DESC_ADDR_MASK) | + FBNIC_IPC_MBX_DESC_HOST_CMPL); + + head = (head + 1) & (FBNIC_IPC_MBX_DESC_LEN - 1); + tail = (tail + 1) & (FBNIC_IPC_MBX_DESC_LEN - 1); } /* Record head for next interrupt */ rx_mbx->head = head; - - /* Make sure we have at least one page for the FW to write to */ - fbnic_mbx_alloc_rx_msgs(fbd); + rx_mbx->tail = tail; } void fbnic_mbx_poll(struct fbnic_dev *fbd) @@ -1663,7 +1753,9 @@ void fbnic_mbx_poll(struct fbnic_dev *fbd) int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd) { struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX]; + struct fbnic_fw_mbx *rx_mbx = &fbd->mbx[FBNIC_IPC_MBX_RX_IDX]; unsigned long timeout = jiffies + 10 * HZ + 1; + u64 tx_resp_error, rx_resp_error; int err, i; do { @@ -1684,13 +1776,19 @@ int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd) } while (!fbnic_mbx_event(fbd)); /* FW has shown signs of life. Enable DMA and start Tx/Rx */ - for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++) - fbnic_mbx_init_desc_ring(fbd, i); + for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++) { + err = fbnic_mbx_init_desc_ring(fbd, i); + if (err) + goto clean_mbx; + } /* Request an update from the firmware. This should overwrite * mgmt.version once we get the actual version from the firmware * in the capabilities request message. */ +send_cap_req: + tx_resp_error = tx_mbx->resp_error; + rx_resp_error = rx_mbx->resp_error; err = fbnic_fw_xmit_simple_msg(fbd, FBNIC_TLV_MSG_ID_HOST_CAP_REQ); if (err) goto clean_mbx; @@ -1708,9 +1806,27 @@ int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd) msleep(20); fbnic_mbx_poll(fbd); + /* A valid capabilities response ends the poll. Check it + * before the FW_ERR retry below so a response parsed in the + * same poll as an unrelated FW_ERR is not discarded. + */ + if (fbd->fw_cap.running.mgmt.version >= MIN_FW_VER_CODE) + break; + /* set err, but wait till mgmt.version check to report it */ - if (!time_is_after_jiffies(timeout)) + if (!time_is_after_jiffies(timeout)) { err = -ETIMEDOUT; + continue; + } + + /* The FW can flag our capabilities request (Tx) or its + * response (Rx) with FW_ERR, in which case it produced no + * usable response. The ring is not wedged, so re-issue the + * request instead of spinning until the timeout. + */ + if (tx_mbx->resp_error != tx_resp_error || + rx_mbx->resp_error != rx_resp_error) + goto send_cap_req; } return 0; @@ -1777,6 +1893,53 @@ void fbnic_mbx_flush_tx(struct fbnic_dev *fbd) } while (time_is_after_jiffies(timeout)); } +/** + * fbnic_fw_mbx_self_test() - verify firmware interface + * @fbd: device to test + * + * This function tests the interfaces to/from the firmware. + * + * Return: See enum fbnic_fw_self_test_codes + **/ +enum fbnic_fw_self_test_codes fbnic_fw_mbx_self_test(struct fbnic_dev *fbd) +{ + enum fbnic_fw_self_test_codes err; + struct fbnic_fw_completion *cmpl; + + /* Skip test if FW interface is not present */ + if (!fbnic_fw_present(fbd)) + return FBNIC_TEST_FW_NO_FIRMWARE; + + cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_TEST); + if (!cmpl) + return FBNIC_TEST_FW_NO_CMPL; + + /* Load a test message onto the FW mailbox interface + * and arm the completion. + */ + err = fbnic_fw_xmit_test_msg(fbd, cmpl); + if (err) { + err = FBNIC_TEST_FW_NO_XMIT; + goto exit_free; + } + + /* Verify we received a message back */ + if (!fbnic_mbx_wait_for_cmpl(cmpl)) { + err = FBNIC_TEST_FW_NO_MSG; + goto exit_cleanup; + } + + /* Verify there were no parsing errors */ + if (cmpl->result) + err = FBNIC_TEST_FW_PARSE; +exit_cleanup: + fbnic_mbx_clear_cmpl(fbd, cmpl); +exit_free: + fbnic_fw_put_cmpl(cmpl); + + return err; +} + int fbnic_fw_xmit_rpc_macda_sync(struct fbnic_dev *fbd) { struct fbnic_tlv_msg *mac_array; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h index 1ecd777aaada..5f9969247e30 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h @@ -4,6 +4,7 @@ #ifndef _FBNIC_FW_H_ #define _FBNIC_FW_H_ +#include <linux/completion.h> #include <linux/if_ether.h> #include <linux/types.h> @@ -12,6 +13,7 @@ struct fbnic_tlv_msg; struct fbnic_fw_mbx { u8 ready, head, tail; + u64 resp_error; struct { struct fbnic_tlv_msg *msg; dma_addr_t addr; @@ -36,6 +38,7 @@ struct fbnic_fw_mbx { * + INDEX_SZ)) */ #define FBNIC_FW_MAX_LOG_HISTORY 14 +#define FBNIC_MBX_RX_TO_SEC 10 struct fbnic_fw_ver { u32 version; @@ -92,6 +95,7 @@ struct fbnic_fw_completion { } u; }; +u64 __fbnic_mbx_rd_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx); void fbnic_mbx_init(struct fbnic_dev *fbd); void fbnic_mbx_clean(struct fbnic_dev *fbd); int fbnic_mbx_set_cmpl(struct fbnic_dev *fbd, @@ -101,6 +105,33 @@ void fbnic_mbx_clear_cmpl(struct fbnic_dev *fbd, void fbnic_mbx_poll(struct fbnic_dev *fbd); int fbnic_mbx_poll_tx_ready(struct fbnic_dev *fbd); void fbnic_mbx_flush_tx(struct fbnic_dev *fbd); + +/** + * enum fbnic_fw_self_test_codes - return codes from self test routines + * + * These are the codes returned from the self test routines and + * stored in the test result array indexed by the specific + * test name. + * + * @FBNIC_TEST_FW_SUCCESS: test success + * @FBNIC_TEST_FW_NO_FIRMWARE: FW interface not present + * @FBNIC_TEST_FW_NO_CMPL: No completion available + * @FBNIC_TEST_FW_NO_XMIT: Could not xmit message + * @FBNIC_TEST_FW_NO_MSG: no message returned + * @FBNIC_TEST_FW_PARSE: returned message had parsing error + */ +enum fbnic_fw_self_test_codes { + FBNIC_TEST_FW_SUCCESS = 0, + FBNIC_TEST_FW_NO_FIRMWARE = 10, + FBNIC_TEST_FW_NO_CMPL = 20, + FBNIC_TEST_FW_NO_XMIT = 30, + FBNIC_TEST_FW_NO_MSG = 40, + FBNIC_TEST_FW_PARSE = 50, +}; + +enum fbnic_fw_self_test_codes fbnic_fw_mbx_self_test(struct fbnic_dev *fbd); +int fbnic_fw_xmit_test_msg(struct fbnic_dev *fbd, + struct fbnic_fw_completion *c); int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership); int fbnic_fw_init_heartbeat(struct fbnic_dev *fbd, bool poll); void fbnic_fw_check_heartbeat(struct fbnic_dev *fbd); @@ -129,6 +160,13 @@ struct fbnic_fw_completion *__fbnic_fw_alloc_cmpl(u32 msg_type, struct fbnic_fw_completion *fbnic_fw_alloc_cmpl(u32 msg_type); void fbnic_fw_put_cmpl(struct fbnic_fw_completion *cmpl_data); +static inline unsigned long +fbnic_mbx_wait_for_cmpl(struct fbnic_fw_completion *cmpl) +{ + return wait_for_completion_timeout(&cmpl->done, + FBNIC_MBX_RX_TO_SEC * HZ); +} + #define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str, _str_sz) \ do { \ const u32 __rev_id = _rev_id; \ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c index 85a883dba385..d8a9a7d7c237 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c @@ -51,8 +51,6 @@ int fbnic_fw_log_init(struct fbnic_dev *fbd) log->data_start = data; log->data_end = data + FBNIC_FW_LOG_SIZE; - fbnic_fw_log_enable(fbd, true); - return 0; } @@ -63,7 +61,6 @@ void fbnic_fw_log_free(struct fbnic_dev *fbd) if (!fbnic_fw_log_ready(fbd)) return; - fbnic_fw_log_disable(fbd); INIT_LIST_HEAD(&log->entries); log->size = 0; vfree(log->data_start); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h b/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h index aa3f429a9aed..caea4be46762 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h @@ -54,6 +54,7 @@ struct fbnic_rmon_stats { struct fbnic_pause_stats { struct fbnic_stat_counter tx_pause_frames; struct fbnic_stat_counter rx_pause_frames; + struct fbnic_stat_counter tx_pause_storm_events; }; struct fbnic_eth_mac_stats { diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c index 02e8b0b257fe..ec3b628ecb2d 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c @@ -170,6 +170,8 @@ int fbnic_mac_request_irq(struct fbnic_dev *fbd) fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX), FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE); + fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_RXB_IDX), 0); + fbd->mac_msix_vector = vector; return 0; @@ -238,6 +240,160 @@ void fbnic_free_irq(struct fbnic_dev *fbd, int nr, void *data) free_irq(irq, data); } +struct fbnic_msix_test_data { + struct fbnic_dev *fbd; + unsigned long test_msix_status[BITS_TO_LONGS(FBNIC_MAX_MSIX_VECS)]; + int irq_vector[FBNIC_MAX_MSIX_VECS]; +}; + +static irqreturn_t fbnic_irq_test(int irq, void *data) +{ + struct fbnic_msix_test_data *test_data = data; + struct fbnic_dev *fbd = test_data->fbd; + int i; + + for (i = fbd->num_irqs; i--;) { + if (test_data->irq_vector[i] == irq) { + set_bit(i, test_data->test_msix_status); + break; + } + } + + return IRQ_HANDLED; +} + +/** + * fbnic_msix_test - Verify behavior of NIC interrupts + * @fbd: device to test + * + * This function is meant to test the global interrupt registers and the + * PCIe IP MSI-X functionality. It essentially goes through and tests + * various combinations of the set, clear, and mask bits in order to + * verify the behavior is as we expect it to be from the driver. + * + * Return: See enum fbnic_msix_self_test_codes + **/ +enum fbnic_msix_self_test_codes fbnic_msix_test(struct fbnic_dev *fbd) +{ + enum fbnic_msix_self_test_codes result = FBNIC_TEST_MSIX_SUCCESS; + struct pci_dev *pdev = to_pci_dev(fbd->dev); + struct fbnic_msix_test_data *test_data; + u32 mask = 0; + int i; + + /* Allocate bitmap and IRQ vector table */ + test_data = kzalloc_obj(*test_data); + + /* memory allocation failure */ + if (!test_data) + return FBNIC_TEST_MSIX_NOMEM; + + /* Initialize test data */ + test_data->fbd = fbd; + + for (i = FBNIC_NON_NAPI_VECTORS; i < fbd->num_irqs; i++) { + /* Add IRQ to vector table so it can be found */ + test_data->irq_vector[i] = pci_irq_vector(pdev, i); + + /* Enable the interrupt */ + if (!fbnic_request_irq(fbd, i, fbnic_irq_test, 0, + fbd->netdev->name, test_data)) + continue; + + while (i-- > FBNIC_NON_NAPI_VECTORS) + fbnic_free_irq(fbd, i, test_data); + kfree(test_data); + + /* IRQ request failure */ + return FBNIC_TEST_MSIX_IRQ_REQ_FAIL; + } + + /* Test each bit individually */ + for (i = FBNIC_NON_NAPI_VECTORS; i < fbd->num_irqs; i++) { + mask = 1U << (i % 32); + + /* Start with mask set and interrupt cleared */ + fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(i / 32), mask); + fbnic_wrfl(fbd); + fbnic_wr32(fbd, FBNIC_INTR_CLEAR(i / 32), mask); + fbnic_wrfl(fbd); + + /* masking failure to prevent interrupt */ + result = FBNIC_TEST_MSIX_MASK; + + fbnic_wr32(fbd, FBNIC_INTR_SET(i / 32), mask); + fbnic_wrfl(fbd); + usleep_range(10000, 11000); + + if (test_bit(i, test_data->test_msix_status)) + break; + + /* unmasking failure w/ sw status set */ + result = FBNIC_TEST_MSIX_UNMASK; + + fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(i / 32), mask); + fbnic_wrfl(fbd); + usleep_range(10000, 11000); + + if (!test_bit(i, test_data->test_msix_status)) + break; + + /* interrupt when clearing mask */ + result = FBNIC_TEST_MSIX_IRQ_CLEAR; + + clear_bit(i, test_data->test_msix_status); + fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(i / 32), mask); + fbnic_wrfl(fbd); + usleep_range(10000, 11000); + + if (test_bit(i, test_data->test_msix_status)) + break; + + /* interrupt not triggering when not masked */ + result = FBNIC_TEST_MSIX_NO_INTERRUPT; + + fbnic_wr32(fbd, FBNIC_INTR_SET(i / 32), mask); + fbnic_wrfl(fbd); + usleep_range(10000, 11000); + + if (!test_bit(i, test_data->test_msix_status)) + break; + + /* status not cleared, or mask not set */ + result = FBNIC_TEST_MSIX_NO_CLEAR_OR_MASK; + if (mask & fbnic_rd32(fbd, FBNIC_INTR_STATUS(i / 32))) + break; + if (!(mask & fbnic_rd32(fbd, FBNIC_INTR_MASK(i / 32)))) + break; + + /* Result = 0 - Success */ + result = FBNIC_TEST_MSIX_SUCCESS; + + clear_bit(i, test_data->test_msix_status); + } + + if (i < fbd->num_irqs) { + fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(i / 32), mask); + fbnic_wrfl(fbd); + fbnic_wr32(fbd, FBNIC_INTR_CLEAR(i / 32), mask); + fbnic_wrfl(fbd); + clear_bit(i, test_data->test_msix_status); + } + + for (i = FBNIC_NON_NAPI_VECTORS; i < fbd->num_irqs; i++) { + /* Test for bits set after testing */ + if (test_bit(i, test_data->test_msix_status)) + result = FBNIC_TEST_MSIX_BITS_SET_AFTER_TEST; + + /* Free IRQ */ + fbnic_free_irq(fbd, i, test_data); + } + + kfree(test_data); + + return result; +} + void fbnic_napi_name_irqs(struct fbnic_dev *fbd) { unsigned int i; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c index fc7abea4ef5b..53b7a938b4c2 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c @@ -143,6 +143,7 @@ static void fbnic_mac_init_qm(struct fbnic_dev *fbd) #define FBNIC_DROP_EN_MASK 0x7d #define FBNIC_PAUSE_EN_MASK 0x14 #define FBNIC_ECN_EN_MASK 0x10 +#define FBNIC_PS_EN_MASK 0x01 struct fbnic_fifo_config { unsigned int addr; @@ -417,9 +418,29 @@ static void __fbnic_mac_stat_rd64(struct fbnic_dev *fbd, bool reset, u32 reg, stat->reported = true; } +static void fbnic_mac_stat_rd32(struct fbnic_dev *fbd, bool reset, u32 reg, + struct fbnic_stat_counter *stat) +{ + u32 new_reg_value; + + new_reg_value = rd32(fbd, reg); + if (!reset) + stat->value += new_reg_value - stat->u.old_reg_value_32; + stat->u.old_reg_value_32 = new_reg_value; + stat->reported = true; +} + #define fbnic_mac_stat_rd64(fbd, reset, __stat, __CSR) \ __fbnic_mac_stat_rd64(fbd, reset, FBNIC_##__CSR##_L, &(__stat)) +bool fbnic_mac_check_tx_pause(struct fbnic_dev *fbd) +{ + u32 command_config; + + command_config = rd32(fbd, FBNIC_MAC_COMMAND_CONFIG); + return !(command_config & FBNIC_MAC_COMMAND_CONFIG_TX_PAUSE_DIS); +} + static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause) { u32 rxb_pause_ctrl; @@ -434,6 +455,49 @@ static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause) wr32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL, rxb_pause_ctrl); } +static void +fbnic_mac_ps_protect_to_reset(struct fbnic_dev *fbd, u16 timeout_ms) +{ + wr32(fbd, FBNIC_RXB_PAUSE_STORM_UNIT_WR, FBNIC_RXB_PS_CLK_DIV); + + wr32(fbd, FBNIC_RXB_PAUSE_STORM(FBNIC_RXB_INTF_NET), + FIELD_PREP(FBNIC_RXB_PAUSE_STORM_THLD_TIME, + FBNIC_MAC_RXB_PS_TO(timeout_ms)) | + FBNIC_RXB_PAUSE_STORM_FORCE_NORMAL); + wrfl(fbd); + wr32(fbd, FBNIC_RXB_PAUSE_STORM(FBNIC_RXB_INTF_NET), + FIELD_PREP(FBNIC_RXB_PAUSE_STORM_THLD_TIME, + FBNIC_MAC_RXB_PS_TO(timeout_ms))); +} + +static void +fbnic_mac_ps_protect_config(struct fbnic_dev *fbd, bool ps_protect) +{ + u16 timeout; + u32 reg; + + ps_protect = ps_protect && fbd->ps_timeout; + timeout = ps_protect ? fbd->ps_timeout : FBNIC_MAC_PS_TO_DEFAULT_MS; + + fbnic_mac_ps_protect_to_reset(fbd, timeout); + + reg = rd32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL); + reg &= ~FBNIC_RXB_PAUSE_DROP_CTRL_PS_ENABLE; + reg |= FIELD_PREP(FBNIC_RXB_PAUSE_DROP_CTRL_PS_ENABLE, ps_protect); + wr32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL, reg); + + /* Clear any pending interrupt status first */ + wr32(fbd, FBNIC_RXB_ERR_INTR_STS, + FIELD_PREP(FBNIC_RXB_ERR_INTR_STS_PS, FBNIC_PS_EN_MASK)); + + /* Unmask the Network to Host PS interrupt if tx_pause is on */ + reg = rd32(fbd, FBNIC_RXB_ERR_INTR_MASK); + reg |= FBNIC_RXB_ERR_INTR_STS_PS; + if (ps_protect) + reg &= ~FBNIC_RXB_ERR_INTR_STS_PS; + wr32(fbd, FBNIC_RXB_ERR_INTR_MASK, reg); +} + static int fbnic_mac_get_link_event(struct fbnic_dev *fbd) { u32 intr_mask = rd32(fbd, FBNIC_SIG_PCS_INTR_STS); @@ -658,6 +722,7 @@ static void fbnic_mac_link_up_asic(struct fbnic_dev *fbd, u32 cmd_cfg, mac_ctrl; fbnic_mac_tx_pause_config(fbd, tx_pause); + fbnic_mac_ps_protect_config(fbd, tx_pause); cmd_cfg = __fbnic_mac_cmd_config_asic(fbd, tx_pause, rx_pause); mac_ctrl = rd32(fbd, FBNIC_SIG_MAC_IN0); @@ -759,6 +824,9 @@ fbnic_mac_get_pause_stats(struct fbnic_dev *fbd, bool reset, MAC_STAT_TX_XOFF_STB); fbnic_mac_stat_rd64(fbd, reset, pause_stats->rx_pause_frames, MAC_STAT_RX_XOFF_STB); + fbnic_mac_stat_rd32(fbd, reset, + FBNIC_RXB_INTR_PS_COUNT(FBNIC_RXB_INTF_NET), + &pause_stats->tx_pause_storm_events); } static void @@ -835,7 +903,7 @@ static int fbnic_mac_get_sensor_asic(struct fbnic_dev *fbd, int id, long *val) { struct fbnic_fw_completion *fw_cmpl; - int err = 0, retries = 5; + int err = 0; s32 *sensor; fw_cmpl = fbnic_fw_alloc_cmpl(FBNIC_TLV_MSG_ID_TSENE_READ_RESP); @@ -862,24 +930,10 @@ static int fbnic_mac_get_sensor_asic(struct fbnic_dev *fbd, int id, goto exit_free; } - /* Allow 2 seconds for reply, resend and try up to 5 times */ - while (!wait_for_completion_timeout(&fw_cmpl->done, 2 * HZ)) { - retries--; - - if (retries == 0) { - dev_err(fbd->dev, - "Timed out waiting for TSENE read\n"); - err = -ETIMEDOUT; - goto exit_cleanup; - } - - err = fbnic_fw_xmit_tsene_read_msg(fbd, NULL); - if (err) { - dev_err(fbd->dev, - "Failed to transmit TSENE read msg, err %d\n", - err); - goto exit_cleanup; - } + if (!wait_for_completion_timeout(&fw_cmpl->done, 10 * HZ)) { + dev_err(fbd->dev, "Timed out waiting for TSENE read\n"); + err = -ETIMEDOUT; + goto exit_cleanup; } /* Handle error returned by firmware */ @@ -932,3 +986,46 @@ int fbnic_mac_init(struct fbnic_dev *fbd) return 0; } + +int fbnic_mac_ps_protect_to_config(struct fbnic_dev *fbd, u16 timeout_ms) +{ + u16 old_timeout_ms = fbd->ps_timeout; + + if (timeout_ms == old_timeout_ms) + return 0; + + if (timeout_ms == PFC_STORM_PREVENTION_AUTO) + timeout_ms = FBNIC_MAC_PS_TO_DEFAULT_MS; + + if (timeout_ms > FBNIC_MAC_PS_TO_MAX_MS) + return -EINVAL; + + fbd->ps_timeout = timeout_ms; + + if (!fbnic_mac_check_tx_pause(fbd)) + return 0; + + if (timeout_ms == 0) + fbnic_mac_ps_protect_config(fbd, false); + else if (old_timeout_ms == 0) + fbnic_mac_ps_protect_config(fbd, true); + else + fbnic_mac_ps_protect_to_reset(fbd, fbd->ps_timeout); + + return 0; +} + +void fbnic_mac_ps_protect_handler(struct fbnic_dev *fbd) +{ + u32 rxb_err_sts = rd32(fbd, FBNIC_RXB_ERR_INTR_STS); + + /* Check if pause storm interrupt for network was triggered */ + if (rxb_err_sts & FIELD_PREP(FBNIC_RXB_ERR_INTR_STS_PS, + FBNIC_PS_EN_MASK)) { + /* Write 1 to clear the interrupt status first */ + wr32(fbd, FBNIC_RXB_ERR_INTR_STS, + FIELD_PREP(FBNIC_RXB_ERR_INTR_STS_PS, FBNIC_PS_EN_MASK)); + + fbnic_mac_ps_protect_to_reset(fbd, fbd->ps_timeout); + } +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h index f08fe8b7c497..10f30e0e8f69 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h @@ -8,6 +8,30 @@ struct fbnic_dev; +/* The RXB clock runs at 600 MHZ in the ASIC and the PAUSE_STORM_UNIT_WR + * is 10us granularity, so set the clock to 6000 (0x1770) + */ +#define FBNIC_RXB_PS_CLK_DIV 0x1770 + +/* Convert milliseconds to pause storm timeout units (10us granularity) */ +#define FBNIC_MAC_RXB_PS_TO(ms) ((ms) * 100) + +/* Convert pause storm timeout units (10us granularity) to milliseconds */ +#define FBNIC_MAC_RXB_PS_TO_MS(ps) ((ps) / 100) + +/* Set the default timer to 500ms, which should be longer than any + * reasonable period of continuous pausing. The service task, which runs + * once per second, periodically resets the pause storm trigger. + * + * As a result, on a functioning system, if pause continues, we enforce + * a duty cycle determined by the configured pause storm timeout (50% + * default). A crashed system will not have the service task and therefore + * pause will remain disabled until reboot recovery. + */ +#define FBNIC_MAC_PS_TO_DEFAULT_MS 500 +#define FBNIC_MAC_PS_TO_MAX_MS \ + FBNIC_MAC_RXB_PS_TO_MS(FIELD_MAX(FBNIC_RXB_PAUSE_STORM_THLD_TIME)) + #define FBNIC_MAX_JUMBO_FRAME_SIZE 9742 /* States loosely based on section 136.8.11.7.5 of IEEE 802.3-2022 Ethernet @@ -119,4 +143,7 @@ struct fbnic_mac { int fbnic_mac_init(struct fbnic_dev *fbd); void fbnic_mac_get_fw_settings(struct fbnic_dev *fbd, u8 *aui, u8 *fec); +int fbnic_mac_ps_protect_to_config(struct fbnic_dev *fbd, u16 timeout); +void fbnic_mac_ps_protect_handler(struct fbnic_dev *fbd); +bool fbnic_mac_check_tx_pause(struct fbnic_dev *fbd); #endif /* _FBNIC_MAC_H_ */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c index 709041f7fc43..fe3a4ce88413 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c @@ -7,11 +7,50 @@ #include "fbnic.h" #include "fbnic_netdev.h" +/* fbnic MDIO Interface Layout + * + * +-------------------+ + * | MAC | + * +-------------------+ + * | | | | <-- 25GMII, 50GMII, or CGMII + * +-------------------+ + * MMD 3 | PCS | + * +-------------------+ + * | FEC | + * +-------------------+ + * MMD 8 | Separated PMA | + * +-------------------+ + * | | <-- PMD Service Interface + * +-------------------+ + * MMD 1 | PMD | + * +-------------------+ + */ + #define DW_VENDOR BIT(15) #define FBNIC_PCS_VENDOR BIT(9) #define FBNIC_PCS_ZERO_MASK (DW_VENDOR - FBNIC_PCS_VENDOR) static int +fbnic_mdio_ids(int id, int regnum) +{ + /* return correct IDs */ + switch (regnum) { + case MDIO_DEVID1: + return id >> 16; + case MDIO_DEVID2: + return id & 0xffff; + case MDIO_DEVS1: + return MDIO_DEVS_SEP_PMA1 | MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; + case MDIO_DEVS2: + return 0; + case MDIO_STAT2: + return MDIO_STAT2_DEVPRST_VAL; + } + + return 0; +} + +static int fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) { u8 aui = FBNIC_AUI_UNKNOWN; @@ -29,18 +68,6 @@ fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) } switch (regnum) { - case MDIO_DEVID1: - ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16; - break; - case MDIO_DEVID2: - ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff; - break; - case MDIO_DEVS1: - ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; - break; - case MDIO_STAT2: - ret = MDIO_STAT2_DEVPRST_VAL; - break; case MDIO_PMA_RXDET: /* If training isn't complete default to 0 */ if (fbd->pmd_state != FBNIC_PMD_SEND_DATA) @@ -51,6 +78,7 @@ fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2)); break; default: + ret = fbnic_mdio_ids(MP_FBNIC_XPCS_PMA_100G_ID, regnum); break; } @@ -64,7 +92,7 @@ fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) static int fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) { - int ret, offset = 0; + int ret, offset = 0, overrides = 0; /* We will need access to both PCS instances to get config info */ if (addr >= 2) @@ -75,18 +103,25 @@ fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) return 0; /* Intercept and return correct ID for PCS */ - if (regnum == MDIO_DEVID1) - return DW_XPCS_ID >> 16; - if (regnum == MDIO_DEVID2) - return DW_XPCS_ID & 0xffff; - if (regnum == MDIO_DEVS1) - return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; - - /* Swap vendor page bit for FBNIC PCS vendor page bit */ - if (regnum & DW_VENDOR) - offset ^= DW_VENDOR | FBNIC_PCS_VENDOR; + switch (regnum) { + case MDIO_DEVID1 ... MDIO_DEVID2: + ret = fbnic_mdio_ids(DW_XPCS_ID, regnum); + break; + case MDIO_DEVS1: + /* DW IP returns MDIO_DEVS_SEP_PMA1, MDIO_DEVS_PMAPMD, + * and MDIO_DEVS_PCS as 0 + */ + overrides = fbnic_mdio_ids(DW_XPCS_ID, regnum); + fallthrough; + default: + /* Swap vendor page bit for FBNIC PCS vendor page bit */ + if (regnum & DW_VENDOR) + offset ^= DW_VENDOR | FBNIC_PCS_VENDOR; - ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset)); + ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset)); + ret |= overrides; + break; + } dev_dbg(fbd->dev, "SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n", @@ -96,6 +131,32 @@ fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) } static int +fbnic_mdio_read_pma(struct fbnic_dev *fbd, int addr, int regnum) +{ + int ret = 0; + + /* We will need access to both PMA instances to get config info */ + if (addr >= 2) + return 0; + + switch (regnum) { + case MDIO_PMA_RSFEC_CTRL ... MDIO_PMA_RSFEC_LANE_MAP: + ret = fbnic_rd32(fbd, FBNIC_RSFEC_CONTROL(addr) + + regnum - MDIO_PMA_RSFEC_CTRL); + break; + default: + ret = fbnic_mdio_ids(MP_FBNIC_XPCS_PMA_100G_ID, regnum); + break; + } + + dev_dbg(fbd->dev, + "SWMII PMA Rd: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, ret); + + return ret; +} + +static int fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) { struct fbnic_dev *fbd = bus->priv; @@ -106,6 +167,9 @@ fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) if (devnum == MDIO_MMD_PCS) return fbnic_mdio_read_pcs(fbd, addr, regnum); + if (devnum == MDIO_MMD_SEP_PMA1) + return fbnic_mdio_read_pma(fbd, addr, regnum); + return 0; } @@ -125,7 +189,7 @@ fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val) addr, regnum, val); /* Allow access to both halves of PCS for 50R2 config */ - if (addr > 2) + if (addr >= 2) return; /* Skip write for reserved registers */ @@ -139,6 +203,26 @@ fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val) fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val); } +static void +fbnic_mdio_write_pma(struct fbnic_dev *fbd, int addr, int regnum, u16 val) +{ + dev_dbg(fbd->dev, + "SWMII PMA Wr: Addr: %d RegNum: %d Value: 0x%04x\n", + addr, regnum, val); + + if (addr >= 2) + return; + + switch (regnum) { + case MDIO_PMA_RSFEC_CTRL ... MDIO_PMA_RSFEC_LANE_MAP: + fbnic_wr32(fbd, FBNIC_RSFEC_CONTROL(addr) + + regnum - MDIO_PMA_RSFEC_CTRL, val); + break; + default: + break; + } +} + static int fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, int regnum, u16 val) @@ -151,6 +235,9 @@ fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, if (devnum == MDIO_MMD_PCS) fbnic_mdio_write_pcs(fbd, addr, regnum, val); + if (devnum == MDIO_MMD_SEP_PMA1) + fbnic_mdio_write_pma(fbd, addr, regnum, val); + return 0; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index 81c9d5c9a4b2..10bf99be3f24 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -183,7 +183,9 @@ static int fbnic_mc_unsync(struct net_device *netdev, const unsigned char *addr) return ret; } -void __fbnic_set_rx_mode(struct fbnic_dev *fbd) +void __fbnic_set_rx_mode(struct fbnic_dev *fbd, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { bool uc_promisc = false, mc_promisc = false; struct net_device *netdev = fbd->netdev; @@ -213,10 +215,10 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd) } /* Synchronize unicast and multicast address lists */ - err = __dev_uc_sync(netdev, fbnic_uc_sync, fbnic_uc_unsync); + err = __hw_addr_sync_dev(uc, netdev, fbnic_uc_sync, fbnic_uc_unsync); if (err == -ENOSPC) uc_promisc = true; - err = __dev_mc_sync(netdev, fbnic_mc_sync, fbnic_mc_unsync); + err = __hw_addr_sync_dev(mc, netdev, fbnic_mc_sync, fbnic_mc_unsync); if (err == -ENOSPC) mc_promisc = true; @@ -238,18 +240,23 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd) fbnic_write_tce_tcam(fbd); } -static void fbnic_set_rx_mode(struct net_device *netdev) +static int fbnic_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; /* No need to update the hardware if we are not running */ if (netif_running(netdev)) - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, uc, mc); + + return 0; } static int fbnic_set_mac(struct net_device *netdev, void *p) { + struct fbnic_net *fbn = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) @@ -257,7 +264,28 @@ static int fbnic_set_mac(struct net_device *netdev, void *p) eth_hw_addr_set(netdev, addr->sa_data); - fbnic_set_rx_mode(netdev); + if (netif_running(netdev)) { + netif_addr_lock_bh(netdev); + __fbnic_set_rx_mode(fbn->fbd, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); + } + + return 0; +} + +static int fbnic_change_mtu(struct net_device *dev, int new_mtu) +{ + struct fbnic_net *fbn = netdev_priv(dev); + + if (fbnic_check_split_frames(fbn->xdp_prog, new_mtu, fbn->hds_thresh)) { + dev_err(&dev->dev, + "MTU %d is larger than HDS threshold %d in XDP mode\n", + new_mtu, fbn->hds_thresh); + + return -EINVAL; + } + + WRITE_ONCE(dev->mtu, new_mtu); return 0; } @@ -285,8 +313,10 @@ void fbnic_clear_rx_mode(struct fbnic_dev *fbd) /* Write updates to hardware */ fbnic_write_macda(fbd); + netif_addr_lock_bh(netdev); __dev_uc_unsync(netdev, NULL); __dev_mc_unsync(netdev, NULL); + netif_addr_unlock_bh(netdev); } static int fbnic_hwtstamp_get(struct net_device *netdev, @@ -533,7 +563,8 @@ static const struct net_device_ops fbnic_netdev_ops = { .ndo_start_xmit = fbnic_xmit_frame, .ndo_features_check = fbnic_features_check, .ndo_set_mac_address = fbnic_set_mac, - .ndo_set_rx_mode = fbnic_set_rx_mode, + .ndo_change_mtu = fbnic_change_mtu, + .ndo_set_rx_mode_async = fbnic_set_rx_mode, .ndo_get_stats64 = fbnic_get_stats64, .ndo_bpf = fbnic_bpf, .ndo_hwtstamp_get = fbnic_hwtstamp_get, @@ -728,7 +759,7 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netdev->netdev_ops = &fbnic_netdev_ops; netdev->stat_ops = &fbnic_stat_ops; netdev->queue_mgmt_ops = &fbnic_queue_mgmt_ops; - netdev->netmem_tx = true; + netdev->netmem_tx = NETMEM_TX_DMA; fbnic_set_ethtool_ops(netdev); @@ -787,6 +818,8 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netdev->hw_enc_features |= netdev->features; netdev->features |= NETIF_F_NTUPLE; + netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_RX_SG; + netdev->min_mtu = IPV6_MIN_MTU; netdev->max_mtu = FBNIC_MAX_JUMBO_FRAME_SIZE - ETH_HLEN; @@ -800,7 +833,8 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netif_tx_stop_all_queues(netdev); if (fbnic_phylink_create(netdev)) { - fbnic_netdev_free(fbd); + free_netdev(netdev); + fbd->netdev = NULL; return NULL; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h index 9129a658f8fa..eded20b0e9e4 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h @@ -97,7 +97,9 @@ void fbnic_time_init(struct fbnic_net *fbn); int fbnic_time_start(struct fbnic_net *fbn); void fbnic_time_stop(struct fbnic_net *fbn); -void __fbnic_set_rx_mode(struct fbnic_dev *fbd); +void __fbnic_set_rx_mode(struct fbnic_dev *fbd, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc); void fbnic_clear_rx_mode(struct fbnic_dev *fbd); void fbnic_phylink_get_pauseparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index 9240673c7533..c6698e3002a1 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -135,17 +135,23 @@ void fbnic_up(struct fbnic_net *fbn) fbnic_rss_reinit_hw(fbn->fbd, fbn); - __fbnic_set_rx_mode(fbn->fbd); + netif_addr_lock_bh(fbn->netdev); + __fbnic_set_rx_mode(fbn->fbd, &fbn->netdev->uc, &fbn->netdev->mc); + netif_addr_unlock_bh(fbn->netdev); /* Enable Tx/Rx processing */ fbnic_napi_enable(fbn); - netif_tx_start_all_queues(fbn->netdev); + netif_tx_wake_all_queues(fbn->netdev); fbnic_service_task_start(fbn); + + fbnic_dbg_up(fbn); } void fbnic_down_noidle(struct fbnic_net *fbn) { + fbnic_dbg_down(fbn); + fbnic_service_task_stop(fbn); /* Disable Tx/Rx Processing */ @@ -176,7 +182,9 @@ static int fbnic_fw_config_after_crash(struct fbnic_dev *fbd) } fbnic_rpc_reset_valid_entries(fbd); - __fbnic_set_rx_mode(fbd); + netif_addr_lock_bh(fbd->netdev); + __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); + netif_addr_unlock_bh(fbd->netdev); return 0; } @@ -216,6 +224,9 @@ static void fbnic_service_task(struct work_struct *work) fbnic_get_hw_stats32(fbd); + if (fbd->ps_timeout && fbnic_mac_check_tx_pause(fbd)) + fbnic_mac_ps_protect_handler(fbd); + fbnic_fw_check_heartbeat(fbd); fbnic_health_check(fbd); @@ -292,6 +303,8 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Populate driver with hardware-specific info and handlers */ fbd->max_num_queues = info->max_num_queues; + fbd->ps_timeout = FBNIC_MAC_PS_TO_DEFAULT_MS; + pci_set_master(pdev); pci_save_state(pdev); @@ -307,11 +320,17 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto free_irqs; } + err = fbnic_fw_log_init(fbd); + if (err) + dev_warn(fbd->dev, + "Unable to initialize firmware log buffer: %d\n", + err); + err = fbnic_fw_request_mbx(fbd); if (err) { dev_err(&pdev->dev, "Firmware mailbox initialization failure\n"); - goto free_irqs; + goto free_fw_log; } /* Send the request to enable the FW logging to host. Note if this @@ -319,11 +338,7 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * possible the FW is just too old to support the logging and needs * to be updated. */ - err = fbnic_fw_log_init(fbd); - if (err) - dev_warn(fbd->dev, - "Unable to initialize firmware log buffer: %d\n", - err); + fbnic_fw_log_enable(fbd, true); fbnic_devlink_register(fbd); fbnic_devlink_otp_check(fbd, "error detected during probe"); @@ -370,6 +385,8 @@ init_failure_mode: * firmware updates for fixes. */ return 0; +free_fw_log: + fbnic_fw_log_free(fbd); free_irqs: fbnic_free_irqs(fbd); err_destroy_health: @@ -404,8 +421,9 @@ static void fbnic_remove(struct pci_dev *pdev) fbnic_hwmon_unregister(fbd); fbnic_dbg_fbd_exit(fbd); fbnic_devlink_unregister(fbd); - fbnic_fw_log_free(fbd); + fbnic_fw_log_disable(fbd); fbnic_fw_free_mbx(fbd); + fbnic_fw_log_free(fbd); fbnic_free_irqs(fbd); fbnic_devlink_health_destroy(fbd); @@ -416,6 +434,7 @@ static int fbnic_pm_suspend(struct device *dev) { struct fbnic_dev *fbd = dev_get_drvdata(dev); struct net_device *netdev = fbd->netdev; + struct fbnic_net *fbn; if (fbnic_init_failure(fbd)) goto null_uc_addr; @@ -423,11 +442,16 @@ static int fbnic_pm_suspend(struct device *dev) rtnl_lock(); netdev_lock(netdev); + fbn = netdev_priv(netdev); + netif_device_detach(netdev); if (netif_running(netdev)) netdev->netdev_ops->ndo_stop(netdev); + /* The IRQs are about to be freed, so drop the napi vector count */ + fbn->num_napi = 0; + netdev_unlock(netdev); rtnl_unlock(); @@ -490,16 +514,20 @@ static int __fbnic_pm_resume(struct device *dev) if (fbnic_init_failure(fbd)) return 0; + rtnl_lock(); + netdev_lock(netdev); + fbn = netdev_priv(netdev); /* Reset the queues if needed */ fbnic_reset_queues(fbn, fbn->num_tx_queues, fbn->num_rx_queues); - rtnl_lock(); - netdev_lock(netdev); - - if (netif_running(netdev)) + if (netif_running(netdev)) { err = __fbnic_open(fbn); + /* On failure the vectors are freed, so drop the count */ + if (err) + fbn->num_napi = 0; + } netdev_unlock(netdev); rtnl_unlock(); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c index 7f31e890031c..bc0f38b6a2b2 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c @@ -244,7 +244,9 @@ void fbnic_bmc_rpc_check(struct fbnic_dev *fbd) if (fbd->fw_cap.need_bmc_tcam_reinit) { fbnic_bmc_rpc_init(fbd); - __fbnic_set_rx_mode(fbd); + netif_addr_lock_bh(fbd->netdev); + __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); + netif_addr_unlock_bh(fbd->netdev); fbd->fw_cap.need_bmc_tcam_reinit = false; } @@ -338,9 +340,8 @@ void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn) else if (tstamp_mask & (1u << flow_type)) dest |= FBNIC_RPC_ACT_TBL0_TS_ENA; - if (act1_value[flow_type] & FBNIC_RPC_TCAM_ACT1_L4_VALID) - dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DMA_HINT, - FBNIC_RCD_HDR_AL_DMA_HINT_L4); + dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DMA_HINT, + FBNIC_RCD_HDR_AL_DMA_HINT_L4); rss_en_mask = fbnic_flow_hash_2_rss_en_mask(fbn, flow_type); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.c b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.c index 517ed8b2f1cb..c55d4f76a5fc 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.c @@ -551,6 +551,172 @@ int fbnic_tlv_parser_error(void *opaque, struct fbnic_tlv_msg **results) return -EBADMSG; } +#define FBNIC_TLV_TEST_STRING_LEN 32 + +struct fbnic_tlv_test { + u64 test_u64; + s64 test_s64; + u32 test_u32; + s32 test_s32; + u16 test_u16; + s16 test_s16; + u8 test_mac[ETH_ALEN]; + u8 test_mac_array[4][ETH_ALEN]; + u8 test_true; + u8 test_false; + char test_string[FBNIC_TLV_TEST_STRING_LEN]; +}; + +static struct fbnic_tlv_test test_struct; + +const struct fbnic_tlv_index fbnic_tlv_test_index[] = { + FBNIC_TLV_ATTR_U64(FBNIC_TLV_TEST_MSG_U64), + FBNIC_TLV_ATTR_S64(FBNIC_TLV_TEST_MSG_S64), + FBNIC_TLV_ATTR_U32(FBNIC_TLV_TEST_MSG_U32), + FBNIC_TLV_ATTR_S32(FBNIC_TLV_TEST_MSG_S32), + FBNIC_TLV_ATTR_U32(FBNIC_TLV_TEST_MSG_U16), + FBNIC_TLV_ATTR_S32(FBNIC_TLV_TEST_MSG_S16), + FBNIC_TLV_ATTR_MAC_ADDR(FBNIC_TLV_TEST_MSG_MAC_ADDR), + FBNIC_TLV_ATTR_FLAG(FBNIC_TLV_TEST_MSG_FLAG_TRUE), + FBNIC_TLV_ATTR_FLAG(FBNIC_TLV_TEST_MSG_FLAG_FALSE), + FBNIC_TLV_ATTR_STRING(FBNIC_TLV_TEST_MSG_STRING, + FBNIC_TLV_TEST_STRING_LEN), + FBNIC_TLV_ATTR_ARRAY(FBNIC_TLV_TEST_MSG_ARRAY), + FBNIC_TLV_ATTR_NESTED(FBNIC_TLV_TEST_MSG_NESTED), + FBNIC_TLV_ATTR_LAST +}; + +static void fbnic_tlv_test_struct_init(void) +{ + int i = FBNIC_TLV_TEST_STRING_LEN - 1; + + /* Populate the struct with random data */ + get_random_once(&test_struct, + offsetof(struct fbnic_tlv_test, test_string) + i); + + /* Force true/false to their expected values */ + test_struct.test_false = false; + test_struct.test_true = true; + + /* Convert test_string to a true ASCII string */ + test_struct.test_string[i] = '\0'; + while (i--) { + /* Force characters into displayable range */ + if (test_struct.test_string[i] < 64 || + test_struct.test_string[i] >= 96) { + test_struct.test_string[i] %= 32; + test_struct.test_string[i] += 64; + } + } +} + +static int fbnic_tlv_test_attr_data(struct fbnic_tlv_msg *msg) +{ + struct fbnic_tlv_msg *array; + int err, i; + + err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_U64, + test_struct.test_u64); + if (err) + return err; + + err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_S64, + test_struct.test_s64); + if (err) + return err; + + err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_U32, + test_struct.test_u32); + if (err) + return err; + + err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_S32, + test_struct.test_s32); + if (err) + return err; + + err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_U16, + test_struct.test_u16); + if (err) + return err; + + err = fbnic_tlv_attr_put_int(msg, FBNIC_TLV_TEST_MSG_S16, + test_struct.test_s16); + if (err) + return err; + + err = fbnic_tlv_attr_put_value(msg, FBNIC_TLV_TEST_MSG_MAC_ADDR, + test_struct.test_mac, ETH_ALEN); + if (err) + return err; + + /* Start MAC address array */ + array = fbnic_tlv_attr_nest_start(msg, FBNIC_TLV_TEST_MSG_ARRAY); + if (!array) + return -ENOSPC; + + for (i = 0; i < 4; i++) { + err = fbnic_tlv_attr_put_value(array, + FBNIC_TLV_TEST_MSG_MAC_ADDR, + test_struct.test_mac_array[i], + ETH_ALEN); + if (err) + return err; + } + + /* Close array */ + fbnic_tlv_attr_nest_stop(msg); + + err = fbnic_tlv_attr_put_flag(msg, FBNIC_TLV_TEST_MSG_FLAG_TRUE); + if (err) + return err; + + return fbnic_tlv_attr_put_string(msg, FBNIC_TLV_TEST_MSG_STRING, + test_struct.test_string); +} + +/** + * fbnic_tlv_test_create - Allocate a test message and fill it w/ data + * @fbd: FBNIC device structure + * + * Return: NULL on failure to allocate or pointer to new TLV test message. + **/ +struct fbnic_tlv_msg *fbnic_tlv_test_create(struct fbnic_dev *fbd) +{ + struct fbnic_tlv_msg *msg, *nest; + int err; + + msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_TEST); + if (!msg) + return NULL; + + /* Randomize struct data */ + fbnic_tlv_test_struct_init(); + + /* Add first level of data to message */ + err = fbnic_tlv_test_attr_data(msg); + if (err) + goto free_message; + + /* Start second level nested */ + nest = fbnic_tlv_attr_nest_start(msg, FBNIC_TLV_TEST_MSG_NESTED); + if (!nest) + goto free_message; + + /* Add nested data */ + err = fbnic_tlv_test_attr_data(nest); + if (err) + goto free_message; + + /* Close nest and report full message */ + fbnic_tlv_attr_nest_stop(msg); + + return msg; +free_message: + free_page((unsigned long)msg); + return NULL; +} + void fbnic_tlv_attr_addr_copy(u8 *dest, struct fbnic_tlv_msg *src) { u8 *mac_addr; @@ -558,3 +724,113 @@ void fbnic_tlv_attr_addr_copy(u8 *dest, struct fbnic_tlv_msg *src) mac_addr = fbnic_tlv_attr_get_value_ptr(src); memcpy(dest, mac_addr, ETH_ALEN); } + +/** + * fbnic_tlv_parser_test_attr - Function loading test attributes into structure + * @str: Test structure to load + * @results: Pointer to results array + * + * Copies attributes into structure. Any attribute that doesn't exist in the + * results array is not populated. + **/ +static void fbnic_tlv_parser_test_attr(struct fbnic_tlv_test *str, + struct fbnic_tlv_msg **results) +{ + struct fbnic_tlv_msg *array_results[4]; + struct fbnic_tlv_msg *attr; + char *string = NULL; + int i, err; + + str->test_u64 = fta_get_uint(results, FBNIC_TLV_TEST_MSG_U64); + str->test_u32 = fta_get_uint(results, FBNIC_TLV_TEST_MSG_U32); + str->test_u16 = fta_get_uint(results, FBNIC_TLV_TEST_MSG_U16); + + str->test_s64 = fta_get_sint(results, FBNIC_TLV_TEST_MSG_S64); + str->test_s32 = fta_get_sint(results, FBNIC_TLV_TEST_MSG_S32); + str->test_s16 = fta_get_sint(results, FBNIC_TLV_TEST_MSG_S16); + + attr = results[FBNIC_TLV_TEST_MSG_MAC_ADDR]; + if (attr) + fbnic_tlv_attr_addr_copy(str->test_mac, attr); + + attr = results[FBNIC_TLV_TEST_MSG_ARRAY]; + if (attr) { + int len = le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1; + + err = fbnic_tlv_attr_parse_array(&attr[1], len, + array_results, + fbnic_tlv_test_index, + FBNIC_TLV_TEST_MSG_MAC_ADDR, + 4); + if (!err) { + for (i = 0; i < 4 && array_results[i]; i++) + fbnic_tlv_attr_addr_copy(str->test_mac_array[i], + array_results[i]); + } + } + + str->test_true = !!results[FBNIC_TLV_TEST_MSG_FLAG_TRUE]; + str->test_false = !!results[FBNIC_TLV_TEST_MSG_FLAG_FALSE]; + + attr = results[FBNIC_TLV_TEST_MSG_STRING]; + if (attr) { + string = fbnic_tlv_attr_get_value_ptr(attr); + strscpy(str->test_string, string, FBNIC_TLV_TEST_STRING_LEN); + } +} + +static void fbnic_tlv_test_dump(struct fbnic_tlv_test *value, char *prefix) +{ + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 16, 1, + value, sizeof(*value), true); +} + +/** + * fbnic_tlv_parser_test - Function for parsing and testing test message + * @opaque: Unused value + * @results: Results of parser output + * + * Return: negative value on error, or 0 on success. + * + * Parses attributes to structures and compares the structure to the + * expected test value that should have been used to populate the message. + * + * Used to verify message generation and parser are working correctly. + **/ +int fbnic_tlv_parser_test(void *opaque, struct fbnic_tlv_msg **results) +{ + struct fbnic_tlv_msg *nest_results[FBNIC_TLV_RESULTS_MAX] = { 0 }; + struct fbnic_tlv_test result_struct; + struct fbnic_tlv_msg *attr; + int err; + + memset(&result_struct, 0, sizeof(result_struct)); + fbnic_tlv_parser_test_attr(&result_struct, results); + + if (memcmp(&test_struct, &result_struct, sizeof(test_struct))) { + fbnic_tlv_test_dump(&result_struct, "fbnic: found - "); + fbnic_tlv_test_dump(&test_struct, "fbnic: expected - "); + return -EINVAL; + } + + attr = results[FBNIC_TLV_TEST_MSG_NESTED]; + if (!attr) + return -EINVAL; + + err = fbnic_tlv_attr_parse(&attr[1], + le16_to_cpu(attr->hdr.len) / sizeof(u32) - 1, + nest_results, fbnic_tlv_test_index); + if (err) + return err; + + memset(&result_struct, 0, sizeof(result_struct)); + fbnic_tlv_parser_test_attr(&result_struct, nest_results); + + if (memcmp(&test_struct, &result_struct, sizeof(test_struct))) { + fbnic_tlv_test_dump(&result_struct, "fbnic: found - "); + fbnic_tlv_test_dump(&test_struct, "fbnic: expected - "); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h index 3508b46ebdd0..9c4e4759394a 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h @@ -9,6 +9,8 @@ #include <linux/const.h> #include <linux/types.h> +struct fbnic_dev; + #define FBNIC_TLV_MSG_ALIGN(len) ALIGN(len, sizeof(u32)) #define FBNIC_TLV_MSG_SIZE(len) \ (FBNIC_TLV_MSG_ALIGN(len) / sizeof(u32)) @@ -153,6 +155,31 @@ int fbnic_tlv_parser_error(void *opaque, struct fbnic_tlv_msg **results); #define fta_get_str(_results, _id, _dst, _dstsize) \ fbnic_tlv_attr_get_string(_results[_id], _dst, _dstsize) +#define FBNIC_TLV_MSG_ID_TEST 0 + +enum fbnic_tlv_test_attr_id { + FBNIC_TLV_TEST_MSG_U64, + FBNIC_TLV_TEST_MSG_S64, + FBNIC_TLV_TEST_MSG_U32, + FBNIC_TLV_TEST_MSG_S32, + FBNIC_TLV_TEST_MSG_U16, + FBNIC_TLV_TEST_MSG_S16, + FBNIC_TLV_TEST_MSG_MAC_ADDR, + FBNIC_TLV_TEST_MSG_FLAG_TRUE, + FBNIC_TLV_TEST_MSG_FLAG_FALSE, + FBNIC_TLV_TEST_MSG_STRING, + FBNIC_TLV_TEST_MSG_NESTED, + FBNIC_TLV_TEST_MSG_ARRAY, + FBNIC_TLV_TEST_MSG_MAX +}; + +extern const struct fbnic_tlv_index fbnic_tlv_test_index[]; +struct fbnic_tlv_msg *fbnic_tlv_test_create(struct fbnic_dev *fbd); +int fbnic_tlv_parser_test(void *opaque, struct fbnic_tlv_msg **results); + +#define FBNIC_TLV_MSG_TEST \ + FBNIC_TLV_PARSER(TEST, fbnic_tlv_test_index, \ + fbnic_tlv_parser_test) #define FBNIC_TLV_MSG_ERROR \ FBNIC_TLV_PARSER(UNKNOWN, NULL, fbnic_tlv_parser_error) #endif /* _FBNIC_TLV_H_ */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 13d508ce637f..10caacffee0f 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -7,6 +7,7 @@ #include <linux/iopoll.h> #include <linux/pci.h> #include <net/netdev_queues.h> +#include <net/netdev_rx_queue.h> #include <net/page_pool/helpers.h> #include <net/tcp.h> #include <net/xdp.h> @@ -39,7 +40,7 @@ struct fbnic_xmit_cb { #define FBNIC_XMIT_NOUNMAP ((void *)1) -static u32 __iomem *fbnic_ring_csr_base(const struct fbnic_ring *ring) +u32 __iomem *fbnic_ring_csr_base(const struct fbnic_ring *ring) { unsigned long csr_base = (unsigned long)ring->doorbell; @@ -194,16 +195,18 @@ static bool fbnic_tx_tstamp(struct sk_buff *skb) static bool fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, - struct skb_shared_info *shinfo, __le64 *meta, - unsigned int *l2len, unsigned int *i3len) + __le64 *meta, unsigned int *l2len, unsigned int *i3len) { unsigned int l3_type, l4_type, l4len, hdrlen; + struct skb_shared_info *shinfo; unsigned char *l4hdr; __be16 payload_len; if (unlikely(skb_cow_head(skb, 0))) return true; + shinfo = skb_shinfo(skb); + if (shinfo->gso_type & SKB_GSO_PARTIAL) { l3_type = FBNIC_TWD_L3_TYPE_OTHER; } else if (!skb->encapsulation) { @@ -258,7 +261,6 @@ fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, static bool fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) { - struct skb_shared_info *shinfo = skb_shinfo(skb); unsigned int l2len, i3len; if (fbnic_tx_tstamp(skb)) @@ -273,8 +275,8 @@ fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) *meta |= cpu_to_le64(FIELD_PREP(FBNIC_TWD_CSUM_OFFSET_MASK, skb->csum_offset / 2)); - if (shinfo->gso_size) { - if (fbnic_tx_lso(ring, skb, shinfo, meta, &l2len, &i3len)) + if (skb_is_gso(skb)) { + if (fbnic_tx_lso(ring, skb, meta, &l2len, &i3len)) return true; } else { *meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_CSO); @@ -310,6 +312,29 @@ fbnic_rx_csum(u64 rcd, struct sk_buff *skb, struct fbnic_ring *rcq, } } +static void fbnic_tx_doorbell(struct fbnic_ring *ring, __le64 *meta) +{ + *meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_COMPLETION); + ring->deferred_meta = -1; + + /* Force DMA writes to flush before writing to tail */ + dma_wmb(); + + writel(ring->tail, ring->doorbell); +} + +/* Packets handed to us with xmit_more set are left in the ring without a + * doorbell, and without a completion request, in the expectation that the + * packet ending the burst will ring for all of them. If that packet gets + * dropped instead we have to ring here, otherwise the descriptors sit in + * the ring until the next transmit, which may never come. + */ +static void fbnic_tx_flush_doorbell(struct fbnic_ring *ring) +{ + if (ring->deferred_meta >= 0) + fbnic_tx_doorbell(ring, &ring->desc[ring->deferred_meta]); +} + static bool fbnic_tx_map(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) { @@ -377,14 +402,10 @@ fbnic_tx_map(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) /* Verify there is room for another packet */ fbnic_maybe_stop_tx(skb->dev, ring, FBNIC_MAX_SKB_DESC); - if (fbnic_tx_sent_queue(skb, ring)) { - *meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_COMPLETION); - - /* Force DMA writes to flush before writing to tail */ - dma_wmb(); - - writel(tail, ring->doorbell); - } + if (fbnic_tx_sent_queue(skb, ring)) + fbnic_tx_doorbell(ring, meta); + else + ring->deferred_meta = meta - ring->desc; return false; dma_error: @@ -424,8 +445,10 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring) * otherwise try next time */ desc_needed = skb_shinfo(skb)->nr_frags + 10; - if (fbnic_maybe_stop_tx(skb->dev, ring, desc_needed)) + if (fbnic_maybe_stop_tx(skb->dev, ring, desc_needed)) { + fbnic_tx_flush_doorbell(ring); return NETDEV_TX_BUSY; + } *meta = cpu_to_le64(FBNIC_TWD_FLAG_DEST_MAC); @@ -446,6 +469,8 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring) err_free: dev_kfree_skb_any(skb); err_count: + fbnic_tx_flush_doorbell(ring); + u64_stats_update_begin(&ring->stats.syncp); ring->stats.dropped++; u64_stats_update_end(&ring->stats.syncp); @@ -927,7 +952,7 @@ static void fbnic_fill_bdq(struct fbnic_ring *bdq) /* Force DMA writes to flush before writing to tail */ dma_wmb(); - writel(i, bdq->doorbell); + writel(i * FBNIC_BD_FRAG_COUNT, bdq->doorbell); } } @@ -1598,7 +1623,7 @@ fbnic_alloc_qt_page_pools(struct fbnic_net *fbn, struct fbnic_q_triad *qt, return 0; err_destroy_sub0: - page_pool_destroy(pp); + page_pool_destroy(qt->sub0.page_pool); return PTR_ERR(pp); } @@ -1640,7 +1665,7 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn, return -EIO; /* Allocate NAPI vector and queue triads */ - nv = kzalloc(struct_size(nv, qt, qt_count), GFP_KERNEL); + nv = kzalloc_flex(*nv, qt, qt_count); if (!nv) return -ENOMEM; @@ -1767,7 +1792,7 @@ int fbnic_alloc_napi_vectors(struct fbnic_net *fbn) int err; /* Allocate 1 Tx queue per napi vector */ - if (num_napi < FBNIC_MAX_TXQS && num_napi == num_tx + num_rx) { + if (num_napi <= FBNIC_MAX_TXQS && num_napi == num_tx + num_rx) { while (num_tx) { err = fbnic_alloc_napi_vector(fbd, fbn, num_napi, v_idx, @@ -2255,6 +2280,22 @@ fbnic_nv_disable(struct fbnic_net *fbn, struct fbnic_napi_vector *nv) fbnic_wrfl(fbn->fbd); } +void fbnic_dbg_down(struct fbnic_net *fbn) +{ + int i; + + for (i = 0; i < fbn->num_napi; i++) + fbnic_dbg_nv_exit(fbn->napi[i]); +} + +void fbnic_dbg_up(struct fbnic_net *fbn) +{ + int i; + + for (i = 0; i < fbn->num_napi; i++) + fbnic_dbg_nv_init(fbn->napi[i]); +} + void fbnic_disable(struct fbnic_net *fbn) { struct fbnic_dev *fbd = fbn->fbd; @@ -2474,6 +2515,7 @@ static void fbnic_enable_twq0(struct fbnic_ring *twq) fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ0_CTL, FBNIC_QUEUE_TWQ_CTL_RESET); twq->tail = 0; twq->head = 0; + twq->deferred_meta = -1; /* Store descriptor ring address and size */ fbnic_ring_wr32(twq, FBNIC_QUEUE_TWQ0_BAL, lower_32_bits(twq->dma)); @@ -2548,7 +2590,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq) hpq->tail = 0; hpq->head = 0; - log_size = fls(hpq->size_mask); + log_size = fls(hpq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT); /* Store descriptor ring address and size */ fbnic_ring_wr32(hpq, FBNIC_QUEUE_BDQ_HPQ_BAL, lower_32_bits(hpq->dma)); @@ -2560,7 +2602,7 @@ static void fbnic_enable_bdq(struct fbnic_ring *hpq, struct fbnic_ring *ppq) if (!ppq->size_mask) goto write_ctl; - log_size = fls(ppq->size_mask); + log_size = fls(ppq->size_mask) + ilog2(FBNIC_BD_FRAG_COUNT); /* Add enabling of PPQ to BDQ control */ bdq_ctl |= FBNIC_QUEUE_BDQ_CTL_PPQ_ENABLE; @@ -2575,7 +2617,8 @@ write_ctl: } static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv, - struct fbnic_ring *rcq, bool tx_pause) + struct fbnic_ring *rcq, bool tx_pause, + bool hdr_split) { struct fbnic_net *fbn = netdev_priv(nv->napi.dev); u32 drop_mode, rcq_ctl; @@ -2588,22 +2631,26 @@ static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv, /* Specify packet layout */ rcq_ctl = FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_DROP_MODE_MASK, drop_mode) | FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_MIN_HROOM_MASK, FBNIC_RX_HROOM) | - FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_MIN_TROOM_MASK, FBNIC_RX_TROOM); + FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_MIN_TROOM_MASK, FBNIC_RX_TROOM) | + FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_EN_HDR_SPLIT, hdr_split); fbnic_ring_wr32(rcq, FBNIC_QUEUE_RDE_CTL0, rcq_ctl); } -void fbnic_config_drop_mode(struct fbnic_net *fbn, bool tx_pause) +void fbnic_config_drop_mode(struct fbnic_net *fbn, bool txp) { + bool hds; int i, t; + hds = fbn->hds_thresh < FBNIC_HDR_BYTES_MIN; + for (i = 0; i < fbn->num_napi; i++) { struct fbnic_napi_vector *nv = fbn->napi[i]; for (t = 0; t < nv->rxt_count; t++) { struct fbnic_q_triad *qt = &nv->qt[nv->txt_count + t]; - fbnic_config_drop_mode_rcq(nv, &qt->cmpl, tx_pause); + fbnic_config_drop_mode_rcq(nv, &qt->cmpl, txp, hds); } } } @@ -2654,20 +2701,18 @@ static void fbnic_enable_rcq(struct fbnic_napi_vector *nv, { struct fbnic_net *fbn = netdev_priv(nv->napi.dev); u32 log_size = fls(rcq->size_mask); - u32 hds_thresh = fbn->hds_thresh; u32 rcq_ctl = 0; - - fbnic_config_drop_mode_rcq(nv, rcq, fbn->tx_pause); + bool hdr_split; + u32 hds_thresh; /* Force lower bound on MAX_HEADER_BYTES. Below this, all frames should * be split at L4. It would also result in the frames being split at * L2/L3 depending on the frame size. */ - if (fbn->hds_thresh < FBNIC_HDR_BYTES_MIN) { - rcq_ctl = FBNIC_QUEUE_RDE_CTL0_EN_HDR_SPLIT; - hds_thresh = FBNIC_HDR_BYTES_MIN; - } + hdr_split = fbn->hds_thresh < FBNIC_HDR_BYTES_MIN; + fbnic_config_drop_mode_rcq(nv, rcq, fbn->tx_pause, hdr_split); + hds_thresh = max(fbn->hds_thresh, FBNIC_HDR_BYTES_MIN); rcq_ctl |= FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PADLEN_MASK, FBNIC_RX_PAD) | FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_MAX_HDR_MASK, hds_thresh) | FIELD_PREP(FBNIC_QUEUE_RDE_CTL1_PAYLD_OFF_MASK, @@ -2809,7 +2854,20 @@ void fbnic_napi_depletion_check(struct net_device *netdev) fbnic_wrfl(fbd); } -static int fbnic_queue_mem_alloc(struct net_device *dev, void *qmem, int idx) +/* Returns the napi vector servicing an Rx queue, or NULL if the datapath + * is torn down. The association is published by fbnic_set_netif_napi() + * and cleared by fbnic_reset_netif_napi(), both under the instance lock. + */ +static struct fbnic_napi_vector *fbnic_rxq_nv(struct net_device *dev, int idx) +{ + struct napi_struct *napi = __netif_get_rx_queue(dev, idx)->napi; + + return napi ? container_of(napi, struct fbnic_napi_vector, napi) : NULL; +} + +static int fbnic_queue_mem_alloc(struct net_device *dev, + struct netdev_queue_config *qcfg, + void *qmem, int idx) { struct fbnic_net *fbn = netdev_priv(dev); const struct fbnic_q_triad *real; @@ -2819,8 +2877,16 @@ static int fbnic_queue_mem_alloc(struct net_device *dev, void *qmem, int idx) if (!netif_running(dev)) return fbnic_alloc_qt_page_pools(fbn, qt, idx); + /* A failed PCIe recovery or resume can leave the datapath torn down + * while netif_running() is still true. This ndo runs before + * netdev_rx_queue_restart() checks netif_running(), so bail out + * rather than touching rings and vectors that are already freed. + */ + nv = fbnic_rxq_nv(dev, idx); + if (!nv) + return -ENETDOWN; + real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl); - nv = fbn->napi[idx % fbn->num_napi]; fbnic_ring_init(&qt->sub0, real->sub0.doorbell, real->sub0.q_idx, real->sub0.flags); @@ -2859,16 +2925,19 @@ static void __fbnic_nv_restart(struct fbnic_net *fbn, for (i = 0; i < nv->txt_count; i++) netif_wake_subqueue(fbn->netdev, nv->qt[i].sub0.q_idx); + fbnic_dbg_nv_init(nv); } -static int fbnic_queue_start(struct net_device *dev, void *qmem, int idx) +static int fbnic_queue_start(struct net_device *dev, + struct netdev_queue_config *qcfg, + void *qmem, int idx) { struct fbnic_net *fbn = netdev_priv(dev); struct fbnic_napi_vector *nv; struct fbnic_q_triad *real; real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl); - nv = fbn->napi[idx % fbn->num_napi]; + nv = fbnic_rxq_nv(dev, idx); fbnic_aggregate_ring_bdq_counters(fbn, &real->sub0); fbnic_aggregate_ring_bdq_counters(fbn, &real->sub1); @@ -2890,7 +2959,8 @@ static int fbnic_queue_stop(struct net_device *dev, void *qmem, int idx) int err; real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl); - nv = fbn->napi[idx % fbn->num_napi]; + nv = fbnic_rxq_nv(dev, idx); + fbnic_dbg_nv_exit(nv); napi_disable_locked(&nv->napi); fbnic_nv_irq_disable(nv); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h index 27776e844e29..f5899446dcc5 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h @@ -38,7 +38,7 @@ struct fbnic_net; #define FBNIC_MAX_XDPQS 128u /* These apply to TWQs, TCQ, RCQ */ -#define FBNIC_QUEUE_SIZE_MIN 16u +#define FBNIC_QUEUE_SIZE_MIN 64u #define FBNIC_QUEUE_SIZE_MAX SZ_64K #define FBNIC_TXQ_SIZE_DEFAULT 1024 @@ -66,7 +66,7 @@ struct fbnic_net; (4096 - FBNIC_RX_HROOM - FBNIC_RX_TROOM - FBNIC_RX_PAD) #define FBNIC_HDS_THRESH_DEFAULT \ (1536 - FBNIC_RX_PAD) -#define FBNIC_HDR_BYTES_MIN 128 +#define FBNIC_HDR_BYTES_MIN 256 struct fbnic_pkt_buff { struct xdp_buff buff; @@ -128,9 +128,14 @@ struct fbnic_ring { /* Rx BDQs only */ struct page_pool *page_pool; - /* Deferred_head is used to cache the head for TWQ1 if + /* TWQ0 only, index of the meta descriptor of the last packet + * placed in the ring without ringing the doorbell, -1 if the + * doorbell is in sync with the tail. + */ + s32 deferred_meta; + + /* TCQ only, used to cache the head for TWQ1 if * an attempt is made to clean TWQ1 with zero napi_budget. - * We do not use it for any other ring. */ s32 deferred_head; }; @@ -151,6 +156,7 @@ struct fbnic_napi_vector { struct napi_struct napi; struct device *dev; /* Device for DMA unmapping */ struct fbnic_dev *fbd; + struct dentry *dbg_nv; u16 v_idx; u8 txt_count; @@ -187,9 +193,12 @@ void fbnic_napi_disable(struct fbnic_net *fbn); void fbnic_config_drop_mode(struct fbnic_net *fbn, bool tx_pause); void fbnic_enable(struct fbnic_net *fbn); void fbnic_disable(struct fbnic_net *fbn); +void fbnic_dbg_up(struct fbnic_net *fbn); +void fbnic_dbg_down(struct fbnic_net *fbn); void fbnic_flush(struct fbnic_net *fbn); void fbnic_fill(struct fbnic_net *fbn); +u32 __iomem *fbnic_ring_csr_base(const struct fbnic_ring *ring); void fbnic_napi_depletion_check(struct net_device *netdev); int fbnic_wait_all_queues_idle(struct fbnic_dev *fbd, bool may_fail); @@ -198,4 +207,6 @@ static inline int fbnic_napi_idx(const struct fbnic_napi_vector *nv) return nv->v_idx - FBNIC_NON_NAPI_VECTORS; } +void fbnic_dbg_nv_init(struct fbnic_napi_vector *nv); +void fbnic_dbg_nv_exit(struct fbnic_napi_vector *nv); #endif /* _FBNIC_TXRX_H_ */ |
