]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
bnxt_en: Report PCIe link properties with pcie_print_link_status()
authorBrian Maly <brian.maly@oracle.com>
Thu, 25 Oct 2018 20:50:38 +0000 (16:50 -0400)
committerBrian Maly <brian.maly@oracle.com>
Tue, 22 Jan 2019 19:24:35 +0000 (14:24 -0500)
Orabug: 28942099

Previously the driver used pcie_get_minimum_link() to warn when the NIC
is in a slot that can't supply as much bandwidth as the NIC could use.

pcie_get_minimum_link() can be misleading because it finds the slowest link
and the narrowest link (which may be different links) without considering
the total bandwidth of each link.  For a path with a 16 GT/s x1 link and a
2.5 GT/s x16 link, it returns 2.5 GT/s x1, which corresponds to 250 MB/s of
bandwidth, not the true available bandwidth of about 1969 MB/s for a
16 GT/s x1 link.

Use pcie_print_link_status() to report PCIe link speed and possible
limitations instead of implementing this in the driver itself.  This finds
the slowest link in the path to the device by computing the total bandwidth
of each link and compares that with the capabilities of the device.

The dmesg change is:

  - PCIe: Speed %s Width x%d
  + %u.%03u Gb/s available PCIe bandwidth (%s x%d link)

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
[backport of upstream commit af125b754e2f09e6061e65db8f4eda0f7730011d]

Signed-off-by: Brian Maly <brian.maly@oracle.com>
Reviewed-by: Jack Vogel <jack.vogel@oracle.com>
Reviewed-by: Jack Vogel <jack.vogel@oracle.com>
[backport of UEK5 commit 48b32a7f2b4dddafbf42cde882c3c84c556fb477]
Signed-off-by: Brian Maly <brian.maly@oracle.com>
Reviewed-by: Jack Vogel <jack.vogel@oracle.com>
Signed-off-by: Brian Maly <brian.maly@oracle.com>
Conflicts:
drivers/net/ethernet/broadcom/bnxt/bnxt_compat.c
drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h

Signed-off-by: Brian Maly <brian.maly@oracle.com>
drivers/net/ethernet/broadcom/bnxt/Makefile
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt_compat.c [new file with mode: 0644]
drivers/net/ethernet/broadcom/bnxt/bnxt_compat.h

index a7ca45b251cb91157c04b6150a4fcf065ace3174..0a179775f463b6b6a6b2dd1c1a509a75c1ef8da3 100644 (file)
@@ -1,3 +1,3 @@
 obj-$(CONFIG_BNXT) += bnxt_en.o
 
-bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o
+bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_compat.o
index b5d1d89874d521d34ba04600383903f22f7a19fd..fb90db6ebb26fba4a75c6515ec0b32556b5ce2af 100644 (file)
@@ -8319,22 +8319,6 @@ static int bnxt_init_mac_addr(struct bnxt *bp)
        return rc;
 }
 
-static void bnxt_parse_log_pcie_link(struct bnxt *bp)
-{
-       enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN;
-       enum pci_bus_speed speed = PCI_SPEED_UNKNOWN;
-
-       if (pcie_get_minimum_link(pci_physfn(bp->pdev), &speed, &width) ||
-           speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN)
-               netdev_info(bp->dev, "Failed to determine PCIe Link Info\n");
-       else
-               netdev_info(bp->dev, "PCIe: Speed %s Width x%d\n",
-                           speed == PCIE_SPEED_2_5GT ? "2.5GT/s" :
-                           speed == PCIE_SPEED_5_0GT ? "5.0GT/s" :
-                           speed == PCIE_SPEED_8_0GT ? "8.0GT/s" :
-                           "Unknown", width);
-}
-
 static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int version_printed;
@@ -8530,8 +8514,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        netdev_info(dev, "%s found at mem %lx, node addr %pM\n",
                    board_info[ent->driver_data].name,
                    (long)pci_resource_start(pdev, 0), dev->dev_addr);
-
-       bnxt_parse_log_pcie_link(bp);
+       pcie_print_link_status(pdev);
 
        return 0;
 
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_compat.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_compat.c
new file mode 100644 (file)
index 0000000..3a72455
--- /dev/null
@@ -0,0 +1,238 @@
+/* Broadcom NetXtreme-C/E network driver.
+ *  *
+ *   * Copyright (c) 2016-2017 Broadcom Limited
+ *    *
+ *     * This program is free software; you can redistribute it and/or modify
+ *      * it under the terms of the GNU General Public License as published by
+ *       * the Free Software Foundation.
+ *        */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/bpf.h>
+#include <linux/filter.h>
+
+#define  PCI_EXP_LNKCAP2_SLS_16_0GB    0x00000010 /* Supported Speed 16GT/s */
+#define  PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */
+#define  PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */
+
+#define pci_info(pdev, fmt, arg...)     dev_info(&(pdev)->dev, fmt, ##arg)
+
+const unsigned char pcie_link_speed[] = {
+       PCI_SPEED_UNKNOWN,              /* 0 */
+       PCIE_SPEED_2_5GT,               /* 1 */
+       PCIE_SPEED_5_0GT,               /* 2 */
+       PCIE_SPEED_8_0GT,               /* 3 */
+       PCI_SPEED_UNKNOWN,              /* 4 */
+       PCI_SPEED_UNKNOWN,              /* 5 */
+       PCI_SPEED_UNKNOWN,              /* 6 */
+       PCI_SPEED_UNKNOWN,              /* 7 */
+       PCI_SPEED_UNKNOWN,              /* 8 */
+       PCI_SPEED_UNKNOWN,              /* 9 */
+       PCI_SPEED_UNKNOWN,              /* A */
+       PCI_SPEED_UNKNOWN,              /* B */
+       PCI_SPEED_UNKNOWN,              /* C */
+       PCI_SPEED_UNKNOWN,              /* D */
+       PCI_SPEED_UNKNOWN,              /* E */
+       PCI_SPEED_UNKNOWN               /* F */
+};
+
+/* PCIe link information */
+#define PCIE_SPEED2STR(speed) \
+       ((speed) == PCIE_SPEED_8_0GT ? "8 GT/s" : \
+        (speed) == PCIE_SPEED_5_0GT ? "5 GT/s" : \
+        (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \
+        "Unknown speed")
+
+/* PCIe speed to Mb/s reduced by encoding overhead */
+#define PCIE_SPEED2MBS_ENC(speed) \
+       ((speed) == PCIE_SPEED_8_0GT  ?  8000*128/130 : \
+        (speed) == PCIE_SPEED_5_0GT  ?  5000*8/10 : \
+        (speed) == PCIE_SPEED_2_5GT  ?  2500*8/10 : \
+        0)
+
+/**
+ * pcie_get_width_cap - query for the PCI device's link width capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device width capability.  Return the maximum link width
+ * supported by the device.
+ */
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev)
+{
+       u32 lnkcap;
+
+       pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+       if (lnkcap)
+               return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
+
+       return PCIE_LNK_WIDTH_UNKNOWN;
+}
+
+/**
+ * pcie_get_speed_cap - query for the PCI device's link speed capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device speed capability.  Return the maximum link speed
+ * supported by the device.
+ */
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
+{
+       u32 lnkcap2, lnkcap;
+
+       /*
+        * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link
+        * Speeds Vector in Link Capabilities 2 when supported, falling
+        * back to Max Link Speed in Link Capabilities otherwise.
+        */
+       pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2);
+       if (lnkcap2) { /* PCIe r3.0-compliant */
+               if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
+                       return PCIE_SPEED_8_0GT;
+               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
+                       return PCIE_SPEED_5_0GT;
+               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
+                       return PCIE_SPEED_2_5GT;
+               return PCI_SPEED_UNKNOWN;
+       }
+
+       pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+       if (lnkcap) { 
+               if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB)
+                       return PCIE_SPEED_8_0GT;
+               else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
+                       return PCIE_SPEED_5_0GT;
+               else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
+                       return PCIE_SPEED_2_5GT;
+       }
+
+       return PCI_SPEED_UNKNOWN;
+}
+
+
+/**
+ * pcie_bandwidth_capable - calculate a PCI device's link bandwidth capability
+ * @dev: PCI device
+ * @speed: storage for link speed
+ * @width: storage for link width
+ *
+ * Calculate a PCI device's link bandwidth by querying for its link speed
+ * and width, multiplying them, and applying encoding overhead.  The result
+ * is in Mb/s, i.e., megabits/second of raw bandwidth.
+ */
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+                          enum pcie_link_width *width)
+{
+       *speed = pcie_get_speed_cap(dev);
+       *width = pcie_get_width_cap(dev);
+
+       if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN)
+               return 0;
+
+       return *width * PCIE_SPEED2MBS_ENC(*speed);
+}
+
+/**
+ * pcie_bandwidth_available - determine minimum link settings of a PCIe
+ *                           device and its bandwidth limitation
+ * @dev: PCI device to query
+ * @limiting_dev: storage for device causing the bandwidth limitation
+ * @speed: storage for speed of limiting device
+ * @width: storage for width of limiting device
+ *
+ * Walk up the PCI device chain and find the point where the minimum
+ * bandwidth is available.  Return the bandwidth available there and (if
+ * limiting_dev, speed, and width pointers are supplied) information about
+ * that point.  The bandwidth returned is in Mb/s, i.e., megabits/second of
+ * raw bandwidth.
+ */
+u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
+                            enum pci_bus_speed *speed,
+                            enum pcie_link_width *width)
+{
+       u16 lnksta;
+       enum pci_bus_speed next_speed;
+       enum pcie_link_width next_width;
+       u32 bw, next_bw;
+
+       if (speed)
+               *speed = PCI_SPEED_UNKNOWN;
+       if (width)
+               *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+       bw = 0;
+
+       while (dev) {
+               pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+
+               next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
+               next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
+                       PCI_EXP_LNKSTA_NLW_SHIFT;
+
+               next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
+
+               /* Check if current device limits the total bandwidth */
+               if (!bw || next_bw <= bw) {
+                       bw = next_bw;
+
+                       if (limiting_dev)
+                               *limiting_dev = dev;
+                       if (speed)
+                               *speed = next_speed;
+                       if (width)
+                               *width = next_width;
+               }
+
+               dev = pci_upstream_bridge(dev);
+       }
+
+       return bw;
+}
+
+/**
+ * __pcie_print_link_status - Report the PCI device's link speed and width
+ * @dev: PCI device to query
+ * @verbose: Print info even when enough bandwidth is available
+ *
+ * If the available bandwidth at the device is less than the device is
+ * capable of, report the device's maximum possible bandwidth and the
+ * upstream link that limits its performance.  If @verbose, always print
+ * the available bandwidth, even if the device isn't constrained.
+ */
+void __pcie_print_link_status(struct pci_dev *dev, bool verbose)
+{
+       enum pcie_link_width width, width_cap;
+       enum pci_bus_speed speed, speed_cap;
+       struct pci_dev *limiting_dev = NULL;
+       u32 bw_avail, bw_cap;
+
+       bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
+       bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
+
+       if (bw_avail >= bw_cap && verbose)
+               pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth (%s x%d link)\n",
+                        bw_cap / 1000, bw_cap % 1000,
+                        PCIE_SPEED2STR(speed_cap), width_cap);
+       else if (bw_avail < bw_cap)
+               pci_info(dev, "%u.%03u Gb/s available PCIe bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
+                        bw_avail / 1000, bw_avail % 1000,
+                        PCIE_SPEED2STR(speed), width,
+                        limiting_dev ? pci_name(limiting_dev) : "<unknown>",
+                        bw_cap / 1000, bw_cap % 1000,
+                        PCIE_SPEED2STR(speed_cap), width_cap);
+}
+
+/**
+ * pcie_print_link_status - Report the PCI device's link speed and width
+ * @dev: PCI device to query
+ *
+ * Report the available bandwidth at the device.
+ */
+void pcie_print_link_status(struct pci_dev *dev)
+{
+       __pcie_print_link_status(dev, true);
+}
+
index 816d0b6e536dca8684a04051b4fccdb1afd1b42c..1adeec340c985d409ea40e48cb019f306bf74147 100644 (file)
@@ -123,6 +123,9 @@ void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
 
 #ifndef XDP_PACKET_HEADROOM
 #define XDP_PACKET_HEADROOM     0
+
+void pcie_print_link_status(struct pci_dev *dev);
+
 #endif
 
 struct netdev_xdp;