summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/meta
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/meta')
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic.h36
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_csr.c128
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_csr.h60
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_debugfs.c407
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_devlink.c9
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c176
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw.c215
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw.h38
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw_log.c3
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h1
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_irq.c156
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mac.c135
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mac.h27
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mdio.c137
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.c52
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.h4
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_pci.c56
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_rpc.c9
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_tlv.c276
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_tlv.h27
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.c144
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.h19
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_ */