diff options
Diffstat (limited to 'drivers/net/ethernet/meta')
-rw-r--r-- | drivers/net/ethernet/meta/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_csr.h | 37 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_devlink.c | 75 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c | 75 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_fw.c | 13 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_fw.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c | 27 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h | 40 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_mac.c | 50 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_mac.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_netdev.c | 138 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_netdev.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_txrx.c | 59 | ||||
-rw-r--r-- | drivers/net/ethernet/meta/fbnic/fbnic_txrx.h | 10 |
16 files changed, 541 insertions, 9 deletions
diff --git a/drivers/net/ethernet/meta/Kconfig b/drivers/net/ethernet/meta/Kconfig index 86034ea4ba5b..85519690b837 100644 --- a/drivers/net/ethernet/meta/Kconfig +++ b/drivers/net/ethernet/meta/Kconfig @@ -20,9 +20,11 @@ if NET_VENDOR_META config FBNIC tristate "Meta Platforms Host Network Interface" depends on X86_64 || COMPILE_TEST - depends on S390=n + depends on !S390 depends on MAX_SKB_FRAGS < 22 depends on PCI_MSI + select NET_DEVLINK + select PAGE_POOL select PHYLINK help This driver supports Meta Platforms Host Network Interface. diff --git a/drivers/net/ethernet/meta/fbnic/Makefile b/drivers/net/ethernet/meta/fbnic/Makefile index 9373b558fdc9..ed4533a73c57 100644 --- a/drivers/net/ethernet/meta/fbnic/Makefile +++ b/drivers/net/ethernet/meta/fbnic/Makefile @@ -8,7 +8,9 @@ obj-$(CONFIG_FBNIC) += fbnic.o fbnic-y := fbnic_devlink.o \ + fbnic_ethtool.o \ fbnic_fw.o \ + fbnic_hw_stats.o \ fbnic_irq.o \ fbnic_mac.o \ fbnic_netdev.o \ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic.h b/drivers/net/ethernet/meta/fbnic/fbnic.h index ad2689bfd6cb..0f9e8d79461c 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic.h @@ -11,6 +11,7 @@ #include "fbnic_csr.h" #include "fbnic_fw.h" +#include "fbnic_hw_stats.h" #include "fbnic_mac.h" #include "fbnic_rpc.h" @@ -47,6 +48,9 @@ struct fbnic_dev { /* Number of TCQs/RCQs available on hardware */ u16 max_num_queues; + + /* Local copy of hardware statistics */ + struct fbnic_hw_stats hw_stats; }; /* Reserve entry 0 in the MSI-X "others" array until we have filled all @@ -132,6 +136,9 @@ void fbnic_free_irq(struct fbnic_dev *dev, int nr, void *data); void fbnic_free_irqs(struct fbnic_dev *fbd); int fbnic_alloc_irqs(struct fbnic_dev *fbd); +void fbnic_get_fw_ver_commit_str(struct fbnic_dev *fbd, char *fw_version, + const size_t str_sz); + enum fbnic_boards { fbnic_board_asic }; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h index a64360de0552..21db509acbc1 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h @@ -660,6 +660,43 @@ enum { #define FBNIC_SIG_PCS_INTR_MASK 0x11816 /* 0x46058 */ #define FBNIC_CSR_END_SIG 0x1184e /* CSR section delimiter */ +#define FBNIC_CSR_START_MAC_STAT 0x11a00 +#define FBNIC_MAC_STAT_RX_BYTE_COUNT_L 0x11a08 /* 0x46820 */ +#define FBNIC_MAC_STAT_RX_BYTE_COUNT_H 0x11a09 /* 0x46824 */ +#define FBNIC_MAC_STAT_RX_ALIGN_ERROR_L \ + 0x11a0a /* 0x46828 */ +#define FBNIC_MAC_STAT_RX_ALIGN_ERROR_H \ + 0x11a0b /* 0x4682c */ +#define FBNIC_MAC_STAT_RX_TOOLONG_L 0x11a0e /* 0x46838 */ +#define FBNIC_MAC_STAT_RX_TOOLONG_H 0x11a0f /* 0x4683c */ +#define FBNIC_MAC_STAT_RX_RECEIVED_OK_L \ + 0x11a12 /* 0x46848 */ +#define FBNIC_MAC_STAT_RX_RECEIVED_OK_H \ + 0x11a13 /* 0x4684c */ +#define FBNIC_MAC_STAT_RX_PACKET_BAD_FCS_L \ + 0x11a14 /* 0x46850 */ +#define FBNIC_MAC_STAT_RX_PACKET_BAD_FCS_H \ + 0x11a15 /* 0x46854 */ +#define FBNIC_MAC_STAT_RX_IFINERRORS_L 0x11a18 /* 0x46860 */ +#define FBNIC_MAC_STAT_RX_IFINERRORS_H 0x11a19 /* 0x46864 */ +#define FBNIC_MAC_STAT_RX_MULTICAST_L 0x11a1c /* 0x46870 */ +#define FBNIC_MAC_STAT_RX_MULTICAST_H 0x11a1d /* 0x46874 */ +#define FBNIC_MAC_STAT_RX_BROADCAST_L 0x11a1e /* 0x46878 */ +#define FBNIC_MAC_STAT_RX_BROADCAST_H 0x11a1f /* 0x4687c */ +#define FBNIC_MAC_STAT_TX_BYTE_COUNT_L 0x11a3e /* 0x468f8 */ +#define FBNIC_MAC_STAT_TX_BYTE_COUNT_H 0x11a3f /* 0x468fc */ +#define FBNIC_MAC_STAT_TX_TRANSMITTED_OK_L \ + 0x11a42 /* 0x46908 */ +#define FBNIC_MAC_STAT_TX_TRANSMITTED_OK_H \ + 0x11a43 /* 0x4690c */ +#define FBNIC_MAC_STAT_TX_IFOUTERRORS_L \ + 0x11a46 /* 0x46918 */ +#define FBNIC_MAC_STAT_TX_IFOUTERRORS_H \ + 0x11a47 /* 0x4691c */ +#define FBNIC_MAC_STAT_TX_MULTICAST_L 0x11a4a /* 0x46928 */ +#define FBNIC_MAC_STAT_TX_MULTICAST_H 0x11a4b /* 0x4692c */ +#define FBNIC_MAC_STAT_TX_BROADCAST_L 0x11a4c /* 0x46930 */ +#define FBNIC_MAC_STAT_TX_BROADCAST_H 0x11a4d /* 0x46934 */ /* PUL User Registers */ #define FBNIC_CSR_START_PUL_USER 0x31000 /* CSR section delimiter */ #define FBNIC_PUL_OB_TLP_HDR_AW_CFG 0x3103d /* 0xc40f4 */ diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c b/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c index e87049dfd223..ef05ae8f5039 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_devlink.c @@ -10,6 +10,56 @@ #define FBNIC_SN_STR_LEN 24 +static int fbnic_version_running_put(struct devlink_info_req *req, + struct fbnic_fw_ver *fw_ver, + char *ver_name) +{ + char running_ver[FBNIC_FW_VER_MAX_SIZE]; + int err; + + fbnic_mk_fw_ver_str(fw_ver->version, running_ver); + err = devlink_info_version_running_put(req, ver_name, running_ver); + if (err) + return err; + + if (strlen(fw_ver->commit) > 0) { + char commit_name[FBNIC_SN_STR_LEN]; + + snprintf(commit_name, FBNIC_SN_STR_LEN, "%s.commit", ver_name); + err = devlink_info_version_running_put(req, commit_name, + fw_ver->commit); + if (err) + return err; + } + + return 0; +} + +static int fbnic_version_stored_put(struct devlink_info_req *req, + struct fbnic_fw_ver *fw_ver, + char *ver_name) +{ + char stored_ver[FBNIC_FW_VER_MAX_SIZE]; + int err; + + fbnic_mk_fw_ver_str(fw_ver->version, stored_ver); + err = devlink_info_version_stored_put(req, ver_name, stored_ver); + if (err) + return err; + + if (strlen(fw_ver->commit) > 0) { + char commit_name[FBNIC_SN_STR_LEN]; + + snprintf(commit_name, FBNIC_SN_STR_LEN, "%s.commit", ver_name); + err = devlink_info_version_stored_put(req, commit_name, + fw_ver->commit); + if (err) + return err; + } + + return 0; +} + static int fbnic_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) @@ -17,6 +67,31 @@ static int fbnic_devlink_info_get(struct devlink *devlink, struct fbnic_dev *fbd = devlink_priv(devlink); int err; + err = fbnic_version_running_put(req, &fbd->fw_cap.running.mgmt, + DEVLINK_INFO_VERSION_GENERIC_FW); + if (err) + return err; + + err = fbnic_version_running_put(req, &fbd->fw_cap.running.bootloader, + DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER); + if (err) + return err; + + err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.mgmt, + DEVLINK_INFO_VERSION_GENERIC_FW); + if (err) + return err; + + err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.bootloader, + DEVLINK_INFO_VERSION_GENERIC_FW_BOOTLOADER); + if (err) + return err; + + err = fbnic_version_stored_put(req, &fbd->fw_cap.stored.undi, + DEVLINK_INFO_VERSION_GENERIC_FW_UNDI); + if (err) + return err; + if (fbd->dsn) { unsigned char serial[FBNIC_SN_STR_LEN]; u8 dsn[8]; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c new file mode 100644 index 000000000000..5d980e178941 --- /dev/null +++ b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c @@ -0,0 +1,75 @@ +#include <linux/ethtool.h> +#include <linux/netdevice.h> +#include <linux/pci.h> + +#include "fbnic.h" +#include "fbnic_netdev.h" +#include "fbnic_tlv.h" + +static void +fbnic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_dev *fbd = fbn->fbd; + + fbnic_get_fw_ver_commit_str(fbd, drvinfo->fw_version, + sizeof(drvinfo->fw_version)); +} + +static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter) +{ + if (counter->reported) + *stat = counter->value; +} + +static void +fbnic_get_eth_mac_stats(struct net_device *netdev, + struct ethtool_eth_mac_stats *eth_mac_stats) +{ + struct fbnic_net *fbn = netdev_priv(netdev); + struct fbnic_mac_stats *mac_stats; + struct fbnic_dev *fbd = fbn->fbd; + const struct fbnic_mac *mac; + + mac_stats = &fbd->hw_stats.mac; + mac = fbd->mac; + + mac->get_eth_mac_stats(fbd, false, &mac_stats->eth_mac); + + fbnic_set_counter(ð_mac_stats->FramesTransmittedOK, + &mac_stats->eth_mac.FramesTransmittedOK); + fbnic_set_counter(ð_mac_stats->FramesReceivedOK, + &mac_stats->eth_mac.FramesReceivedOK); + fbnic_set_counter(ð_mac_stats->FrameCheckSequenceErrors, + &mac_stats->eth_mac.FrameCheckSequenceErrors); + fbnic_set_counter(ð_mac_stats->AlignmentErrors, + &mac_stats->eth_mac.AlignmentErrors); + fbnic_set_counter(ð_mac_stats->OctetsTransmittedOK, + &mac_stats->eth_mac.OctetsTransmittedOK); + fbnic_set_counter(ð_mac_stats->FramesLostDueToIntMACXmitError, + &mac_stats->eth_mac.FramesLostDueToIntMACXmitError); + fbnic_set_counter(ð_mac_stats->OctetsReceivedOK, + &mac_stats->eth_mac.OctetsReceivedOK); + fbnic_set_counter(ð_mac_stats->FramesLostDueToIntMACRcvError, + &mac_stats->eth_mac.FramesLostDueToIntMACRcvError); + fbnic_set_counter(ð_mac_stats->MulticastFramesXmittedOK, + &mac_stats->eth_mac.MulticastFramesXmittedOK); + fbnic_set_counter(ð_mac_stats->BroadcastFramesXmittedOK, + &mac_stats->eth_mac.BroadcastFramesXmittedOK); + fbnic_set_counter(ð_mac_stats->MulticastFramesReceivedOK, + &mac_stats->eth_mac.MulticastFramesReceivedOK); + fbnic_set_counter(ð_mac_stats->BroadcastFramesReceivedOK, + &mac_stats->eth_mac.BroadcastFramesReceivedOK); + fbnic_set_counter(ð_mac_stats->FrameTooLongErrors, + &mac_stats->eth_mac.FrameTooLongErrors); +} + +static const struct ethtool_ops fbnic_ethtool_ops = { + .get_drvinfo = fbnic_get_drvinfo, + .get_eth_mac_stats = fbnic_get_eth_mac_stats, +}; + +void fbnic_set_ethtool_ops(struct net_device *dev) +{ + dev->ethtool_ops = &fbnic_ethtool_ops; +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c index 0c6e1b4c119b..8f7a2a19ddf8 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c @@ -789,3 +789,16 @@ void fbnic_mbx_flush_tx(struct fbnic_dev *fbd) count += (tx_mbx->head - head) % FBNIC_IPC_MBX_DESC_LEN; } while (count < FBNIC_IPC_MBX_DESC_LEN && --attempts); } + +void fbnic_get_fw_ver_commit_str(struct fbnic_dev *fbd, char *fw_version, + const size_t str_sz) +{ + struct fbnic_fw_ver *mgmt = &fbd->fw_cap.running.mgmt; + const char *delim = ""; + + if (mgmt->commit[0]) + delim = "_"; + + fbnic_mk_full_fw_ver_str(mgmt->version, delim, mgmt->commit, + fw_version, str_sz); +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h index c65bca613665..221faf8c6756 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.h @@ -53,10 +53,10 @@ 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); -#define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str) \ +#define fbnic_mk_full_fw_ver_str(_rev_id, _delim, _commit, _str, _str_sz) \ do { \ const u32 __rev_id = _rev_id; \ - snprintf(_str, sizeof(_str), "%02lu.%02lu.%02lu-%03lu%s%s", \ + snprintf(_str, _str_sz, "%02lu.%02lu.%02lu-%03lu%s%s", \ FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_MAJOR, __rev_id), \ FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_MINOR, __rev_id), \ FIELD_GET(FBNIC_FW_CAP_RESP_VERSION_PATCH, __rev_id), \ @@ -65,7 +65,7 @@ do { \ } while (0) #define fbnic_mk_fw_ver_str(_rev_id, _str) \ - fbnic_mk_full_fw_ver_str(_rev_id, "", "", _str) + fbnic_mk_full_fw_ver_str(_rev_id, "", "", _str, sizeof(_str)) #define FW_HEARTBEAT_PERIOD (10 * HZ) diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c b/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c new file mode 100644 index 000000000000..a0acc7606aa1 --- /dev/null +++ b/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c @@ -0,0 +1,27 @@ +#include "fbnic.h" + +u64 fbnic_stat_rd64(struct fbnic_dev *fbd, u32 reg, u32 offset) +{ + u32 prev_upper, upper, lower, diff; + + prev_upper = rd32(fbd, reg + offset); + lower = rd32(fbd, reg); + upper = rd32(fbd, reg + offset); + + diff = upper - prev_upper; + if (!diff) + return ((u64)upper << 32) | lower; + + if (diff > 1) + dev_warn_once(fbd->dev, + "Stats inconsistent, upper 32b of %#010x updating too quickly\n", + reg * 4); + + /* Return only the upper bits as we cannot guarantee + * the accuracy of the lower bits. We will add them in + * when the counter slows down enough that we can get + * a snapshot with both upper values being the same + * between reads. + */ + return ((u64)upper << 32); +} diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h b/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h new file mode 100644 index 000000000000..30348904b510 --- /dev/null +++ b/drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h @@ -0,0 +1,40 @@ +#include <linux/ethtool.h> + +#include "fbnic_csr.h" + +struct fbnic_stat_counter { + u64 value; + union { + u32 old_reg_value_32; + u64 old_reg_value_64; + } u; + bool reported; +}; + +struct fbnic_eth_mac_stats { + struct fbnic_stat_counter FramesTransmittedOK; + struct fbnic_stat_counter FramesReceivedOK; + struct fbnic_stat_counter FrameCheckSequenceErrors; + struct fbnic_stat_counter AlignmentErrors; + struct fbnic_stat_counter OctetsTransmittedOK; + struct fbnic_stat_counter FramesLostDueToIntMACXmitError; + struct fbnic_stat_counter OctetsReceivedOK; + struct fbnic_stat_counter FramesLostDueToIntMACRcvError; + struct fbnic_stat_counter MulticastFramesXmittedOK; + struct fbnic_stat_counter BroadcastFramesXmittedOK; + struct fbnic_stat_counter MulticastFramesReceivedOK; + struct fbnic_stat_counter BroadcastFramesReceivedOK; + struct fbnic_stat_counter FrameTooLongErrors; +}; + +struct fbnic_mac_stats { + struct fbnic_eth_mac_stats eth_mac; +}; + +struct fbnic_hw_stats { + struct fbnic_mac_stats mac; +}; + +u64 fbnic_stat_rd64(struct fbnic_dev *fbd, u32 reg, u32 offset); + +void fbnic_get_hw_stats(struct fbnic_dev *fbd); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c index 7920e7af82d9..7b654d0a6dac 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c @@ -403,6 +403,21 @@ static void fbnic_mac_init_regs(struct fbnic_dev *fbd) fbnic_mac_init_txb(fbd); } +static void __fbnic_mac_stat_rd64(struct fbnic_dev *fbd, bool reset, u32 reg, + struct fbnic_stat_counter *stat) +{ + u64 new_reg_value; + + new_reg_value = fbnic_stat_rd64(fbd, reg, 1); + if (!reset) + stat->value += new_reg_value - stat->u.old_reg_value_64; + stat->u.old_reg_value_64 = 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)) + static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause) { u32 rxb_pause_ctrl; @@ -637,12 +652,47 @@ static void fbnic_mac_link_up_asic(struct fbnic_dev *fbd, wr32(fbd, FBNIC_MAC_COMMAND_CONFIG, cmd_cfg); } +static void +fbnic_mac_get_eth_mac_stats(struct fbnic_dev *fbd, bool reset, + struct fbnic_eth_mac_stats *mac_stats) +{ + fbnic_mac_stat_rd64(fbd, reset, mac_stats->OctetsReceivedOK, + MAC_STAT_RX_BYTE_COUNT); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->AlignmentErrors, + MAC_STAT_RX_ALIGN_ERROR); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->FrameTooLongErrors, + MAC_STAT_RX_TOOLONG); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->FramesReceivedOK, + MAC_STAT_RX_RECEIVED_OK); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->FrameCheckSequenceErrors, + MAC_STAT_RX_PACKET_BAD_FCS); + fbnic_mac_stat_rd64(fbd, reset, + mac_stats->FramesLostDueToIntMACRcvError, + MAC_STAT_RX_IFINERRORS); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->MulticastFramesReceivedOK, + MAC_STAT_RX_MULTICAST); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->BroadcastFramesReceivedOK, + MAC_STAT_RX_BROADCAST); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->OctetsTransmittedOK, + MAC_STAT_TX_BYTE_COUNT); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->FramesTransmittedOK, + MAC_STAT_TX_TRANSMITTED_OK); + fbnic_mac_stat_rd64(fbd, reset, + mac_stats->FramesLostDueToIntMACXmitError, + MAC_STAT_TX_IFOUTERRORS); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->MulticastFramesXmittedOK, + MAC_STAT_TX_MULTICAST); + fbnic_mac_stat_rd64(fbd, reset, mac_stats->BroadcastFramesXmittedOK, + MAC_STAT_TX_BROADCAST); +} + static const struct fbnic_mac fbnic_mac_asic = { .init_regs = fbnic_mac_init_regs, .pcs_enable = fbnic_pcs_enable_asic, .pcs_disable = fbnic_pcs_disable_asic, .pcs_get_link = fbnic_pcs_get_link_asic, .pcs_get_link_event = fbnic_pcs_get_link_event_asic, + .get_eth_mac_stats = fbnic_mac_get_eth_mac_stats, .link_down = fbnic_mac_link_down_asic, .link_up = fbnic_mac_link_up_asic, }; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h index f53be6e6aef9..476239a9d381 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h @@ -78,6 +78,9 @@ struct fbnic_mac { bool (*pcs_get_link)(struct fbnic_dev *fbd); int (*pcs_get_link_event)(struct fbnic_dev *fbd); + void (*get_eth_mac_stats)(struct fbnic_dev *fbd, bool reset, + struct fbnic_eth_mac_stats *mac_stats); + void (*link_down)(struct fbnic_dev *fbd); void (*link_up)(struct fbnic_dev *fbd, bool tx_pause, bool rx_pause); }; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index b7ce6da68543..a400616a24d4 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -4,6 +4,7 @@ #include <linux/etherdevice.h> #include <linux/ipv6.h> #include <linux/types.h> +#include <net/netdev_queues.h> #include "fbnic.h" #include "fbnic_netdev.h" @@ -316,6 +317,74 @@ void fbnic_clear_rx_mode(struct net_device *netdev) __dev_mc_unsync(netdev, NULL); } +static void fbnic_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats64) +{ + u64 tx_bytes, tx_packets, tx_dropped = 0; + u64 rx_bytes, rx_packets, rx_dropped = 0; + struct fbnic_net *fbn = netdev_priv(dev); + struct fbnic_queue_stats *stats; + unsigned int start, i; + + stats = &fbn->tx_stats; + + tx_bytes = stats->bytes; + tx_packets = stats->packets; + tx_dropped = stats->dropped; + + stats64->tx_bytes = tx_bytes; + stats64->tx_packets = tx_packets; + stats64->tx_dropped = tx_dropped; + + for (i = 0; i < fbn->num_tx_queues; i++) { + struct fbnic_ring *txr = fbn->tx[i]; + + if (!txr) + continue; + + stats = &txr->stats; + do { + start = u64_stats_fetch_begin(&stats->syncp); + tx_bytes = stats->bytes; + tx_packets = stats->packets; + tx_dropped = stats->dropped; + } while (u64_stats_fetch_retry(&stats->syncp, start)); + + stats64->tx_bytes += tx_bytes; + stats64->tx_packets += tx_packets; + stats64->tx_dropped += tx_dropped; + } + + stats = &fbn->rx_stats; + + rx_bytes = stats->bytes; + rx_packets = stats->packets; + rx_dropped = stats->dropped; + + stats64->rx_bytes = rx_bytes; + stats64->rx_packets = rx_packets; + stats64->rx_dropped = rx_dropped; + + for (i = 0; i < fbn->num_rx_queues; i++) { + struct fbnic_ring *rxr = fbn->rx[i]; + + if (!rxr) + continue; + + stats = &rxr->stats; + do { + start = u64_stats_fetch_begin(&stats->syncp); + rx_bytes = stats->bytes; + rx_packets = stats->packets; + rx_dropped = stats->dropped; + } while (u64_stats_fetch_retry(&stats->syncp, start)); + + stats64->rx_bytes += rx_bytes; + stats64->rx_packets += rx_packets; + stats64->rx_dropped += rx_dropped; + } +} + static const struct net_device_ops fbnic_netdev_ops = { .ndo_open = fbnic_open, .ndo_stop = fbnic_stop, @@ -324,6 +393,72 @@ static const struct net_device_ops fbnic_netdev_ops = { .ndo_features_check = fbnic_features_check, .ndo_set_mac_address = fbnic_set_mac, .ndo_set_rx_mode = fbnic_set_rx_mode, + .ndo_get_stats64 = fbnic_get_stats64, +}; + +static void fbnic_get_queue_stats_rx(struct net_device *dev, int idx, + struct netdev_queue_stats_rx *rx) +{ + struct fbnic_net *fbn = netdev_priv(dev); + struct fbnic_ring *rxr = fbn->rx[idx]; + struct fbnic_queue_stats *stats; + unsigned int start; + u64 bytes, packets; + + if (!rxr) + return; + + stats = &rxr->stats; + do { + start = u64_stats_fetch_begin(&stats->syncp); + bytes = stats->bytes; + packets = stats->packets; + } while (u64_stats_fetch_retry(&stats->syncp, start)); + + rx->bytes = bytes; + rx->packets = packets; +} + +static void fbnic_get_queue_stats_tx(struct net_device *dev, int idx, + struct netdev_queue_stats_tx *tx) +{ + struct fbnic_net *fbn = netdev_priv(dev); + struct fbnic_ring *txr = fbn->tx[idx]; + struct fbnic_queue_stats *stats; + unsigned int start; + u64 bytes, packets; + + if (!txr) + return; + + stats = &txr->stats; + do { + start = u64_stats_fetch_begin(&stats->syncp); + bytes = stats->bytes; + packets = stats->packets; + } while (u64_stats_fetch_retry(&stats->syncp, start)); + + tx->bytes = bytes; + tx->packets = packets; +} + +static void fbnic_get_base_stats(struct net_device *dev, + struct netdev_queue_stats_rx *rx, + struct netdev_queue_stats_tx *tx) +{ + struct fbnic_net *fbn = netdev_priv(dev); + + tx->bytes = fbn->tx_stats.bytes; + tx->packets = fbn->tx_stats.packets; + + rx->bytes = fbn->rx_stats.bytes; + rx->packets = fbn->rx_stats.packets; +} + +static const struct netdev_stat_ops fbnic_stat_ops = { + .get_queue_stats_rx = fbnic_get_queue_stats_rx, + .get_queue_stats_tx = fbnic_get_queue_stats_tx, + .get_base_stats = fbnic_get_base_stats, }; void fbnic_reset_queues(struct fbnic_net *fbn, @@ -384,6 +519,9 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) fbd->netdev = netdev; netdev->netdev_ops = &fbnic_netdev_ops; + netdev->stat_ops = &fbnic_stat_ops; + + fbnic_set_ethtool_ops(netdev); fbn = netdev_priv(netdev); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h index 6bc0ebeb8182..6c27da09a612 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h @@ -40,6 +40,9 @@ struct fbnic_net { u32 rss_key[FBNIC_RPC_RSS_KEY_DWORD_LEN]; u32 rss_flow_hash[FBNIC_NUM_HASH_OPT]; + /* Storage for stats after ring destruction */ + struct fbnic_queue_stats tx_stats; + struct fbnic_queue_stats rx_stats; u64 link_down_events; struct list_head napis; @@ -55,6 +58,7 @@ int fbnic_netdev_register(struct net_device *netdev); void fbnic_netdev_unregister(struct net_device *netdev); void fbnic_reset_queues(struct fbnic_net *fbn, unsigned int tx, unsigned int rx); +void fbnic_set_ethtool_ops(struct net_device *dev); void __fbnic_set_rx_mode(struct net_device *netdev); void fbnic_clear_rx_mode(struct net_device *netdev); diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 0ed4c9fff5d8..6a6d7e22f1a7 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -273,6 +273,9 @@ fbnic_xmit_frame_ring(struct sk_buff *skb, struct fbnic_ring *ring) err_free: dev_kfree_skb_any(skb); err_count: + u64_stats_update_begin(&ring->stats.syncp); + ring->stats.dropped++; + u64_stats_update_end(&ring->stats.syncp); return NETDEV_TX_OK; } @@ -363,10 +366,19 @@ static void fbnic_clean_twq0(struct fbnic_napi_vector *nv, int napi_budget, txq = txring_txq(nv->napi.dev, ring); if (unlikely(discard)) { + u64_stats_update_begin(&ring->stats.syncp); + ring->stats.dropped += total_packets; + u64_stats_update_end(&ring->stats.syncp); + netdev_tx_completed_queue(txq, total_packets, total_bytes); return; } + u64_stats_update_begin(&ring->stats.syncp); + ring->stats.bytes += total_bytes; + ring->stats.packets += total_packets; + u64_stats_update_end(&ring->stats.syncp); + netif_txq_completed_wake(txq, total_packets, total_bytes, fbnic_desc_unused(ring), FBNIC_TX_DESC_WAKEUP); @@ -730,12 +742,12 @@ static bool fbnic_rcd_metadata_err(u64 rcd) static int fbnic_clean_rcq(struct fbnic_napi_vector *nv, struct fbnic_q_triad *qt, int budget) { + unsigned int packets = 0, bytes = 0, dropped = 0; struct fbnic_ring *rcq = &qt->cmpl; struct fbnic_pkt_buff *pkt; s32 head0 = -1, head1 = -1; __le64 *raw_rcd, done; u32 head = rcq->head; - u64 packets = 0; done = (head & (rcq->size_mask + 1)) ? cpu_to_le64(FBNIC_RCD_DONE) : 0; raw_rcd = &rcq->desc[head & rcq->size_mask]; @@ -780,9 +792,11 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv, fbnic_populate_skb_fields(nv, rcd, skb, qt); packets++; + bytes += skb->len; napi_gro_receive(&nv->napi, skb); } else { + dropped++; fbnic_put_pkt_buff(nv, pkt, 1); } @@ -799,6 +813,14 @@ static int fbnic_clean_rcq(struct fbnic_napi_vector *nv, } } + u64_stats_update_begin(&rcq->stats.syncp); + rcq->stats.packets += packets; + rcq->stats.bytes += bytes; + /* Re-add ethernet header length (removed in fbnic_build_skb) */ + rcq->stats.bytes += ETH_HLEN * packets; + rcq->stats.dropped += dropped; + u64_stats_update_end(&rcq->stats.syncp); + /* Unmap and free processed buffers */ if (head0 >= 0) fbnic_clean_bdq(nv, budget, &qt->sub0, head0); @@ -865,12 +887,36 @@ static irqreturn_t fbnic_msix_clean_rings(int __always_unused irq, void *data) return IRQ_HANDLED; } +static void fbnic_aggregate_ring_rx_counters(struct fbnic_net *fbn, + struct fbnic_ring *rxr) +{ + struct fbnic_queue_stats *stats = &rxr->stats; + + /* Capture stats from queues before dissasociating them */ + fbn->rx_stats.bytes += stats->bytes; + fbn->rx_stats.packets += stats->packets; + fbn->rx_stats.dropped += stats->dropped; +} + +static void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn, + struct fbnic_ring *txr) +{ + struct fbnic_queue_stats *stats = &txr->stats; + + /* Capture stats from queues before dissasociating them */ + fbn->tx_stats.bytes += stats->bytes; + fbn->tx_stats.packets += stats->packets; + fbn->tx_stats.dropped += stats->dropped; +} + static void fbnic_remove_tx_ring(struct fbnic_net *fbn, struct fbnic_ring *txr) { if (!(txr->flags & FBNIC_RING_F_STATS)) return; + fbnic_aggregate_ring_tx_counters(fbn, txr); + /* Remove pointer to the Tx ring */ WARN_ON(fbn->tx[txr->q_idx] && fbn->tx[txr->q_idx] != txr); fbn->tx[txr->q_idx] = NULL; @@ -882,6 +928,8 @@ static void fbnic_remove_rx_ring(struct fbnic_net *fbn, if (!(rxr->flags & FBNIC_RING_F_STATS)) return; + fbnic_aggregate_ring_rx_counters(fbn, rxr); + /* Remove pointer to the Rx ring */ WARN_ON(fbn->rx[rxr->q_idx] && fbn->rx[rxr->q_idx] != rxr); fbn->rx[rxr->q_idx] = NULL; @@ -974,6 +1022,7 @@ static int fbnic_alloc_nv_page_pool(struct fbnic_net *fbn, static void fbnic_ring_init(struct fbnic_ring *ring, u32 __iomem *doorbell, int q_idx, u8 flags) { + u64_stats_init(&ring->stats.syncp); ring->doorbell = doorbell; ring->q_idx = q_idx; ring->flags = flags; @@ -1012,14 +1061,14 @@ static int fbnic_alloc_napi_vector(struct fbnic_dev *fbd, struct fbnic_net *fbn, nv->fbd = fbd; nv->v_idx = v_idx; - /* Record IRQ to NAPI struct */ - netif_napi_set_irq(&nv->napi, - pci_irq_vector(to_pci_dev(fbd->dev), nv->v_idx)); - /* Tie napi to netdev */ list_add(&nv->napis, &fbn->napis); netif_napi_add(fbn->netdev, &nv->napi, fbnic_poll); + /* Record IRQ to NAPI struct */ + netif_napi_set_irq(&nv->napi, + pci_irq_vector(to_pci_dev(fbd->dev), nv->v_idx)); + /* Tie nv back to PCIe dev */ nv->dev = fbd->dev; diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h index 4a206c0e7192..2f91f68d11d5 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h @@ -7,6 +7,7 @@ #include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/types.h> +#include <linux/u64_stats_sync.h> #include <net/xdp.h> struct fbnic_net; @@ -51,6 +52,13 @@ struct fbnic_pkt_buff { u16 nr_frags; }; +struct fbnic_queue_stats { + u64 packets; + u64 bytes; + u64 dropped; + struct u64_stats_sync syncp; +}; + /* Pagecnt bias is long max to reserve the last bit to catch overflow * cases where if we overcharge the bias it will flip over to be negative. */ @@ -77,6 +85,8 @@ struct fbnic_ring { u32 head, tail; /* Head/Tail of ring */ + struct fbnic_queue_stats stats; + /* Slow path fields follow */ dma_addr_t dma; /* Phys addr of descriptor memory */ size_t size; /* Size of descriptor ring in memory */ |