]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ionic: Get "link_down_count" ext link stat from firmware
authorEric Joyner <eric.joyner@amd.com>
Sun, 14 Jun 2026 20:53:02 +0000 (13:53 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 16 Jun 2026 01:15:08 +0000 (18:15 -0700)
The number of times that link has gone down at the port level is tracked
by the firmware and sent to the driver via regular DMA writes to an
instance of struct ionic_port_status in the driver's memory.

This statistic was never reported in favor of a driver-derived stat, but
doing it in the driver was never necessary since firmware had been
reporting it the whole time. Since it would be more accurate and true to
the description of the statistic to get this count at the PHY level,
replace the driver-calculated statistic with one derived from the
firmware one and remove the driver-calculated one entirely.

The stat reported by the ethtool .get_link_ext_stats() handler is
normalized to 0 on driver load and any device resets that require the
driver to rebuild state while also handling overflows.

Signed-off-by: Eric Joyner <eric.joyner@amd.com>
Link: https://patch.msgid.link/20260614205303.48088-5-eric.joyner@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/pensando/ionic/ionic_dev.c
drivers/net/ethernet/pensando/ionic/ionic_dev.h
drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
drivers/net/ethernet/pensando/ionic/ionic_lif.c
drivers/net/ethernet/pensando/ionic/ionic_lif.h
drivers/net/ethernet/pensando/ionic/ionic_main.c

index 3838c4a70766a0a87bfb671ff28a41cec3133ae1..648d9d24be856c9c0822739a87c08f443b5ee751 100644 (file)
@@ -1076,3 +1076,13 @@ bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos)
 
        return ((pos - tail) & mask) < ((head - tail) & mask);
 }
+
+void ionic_reset_link_down_count(struct ionic_dev *idev)
+{
+       if (!READ_ONCE(idev->link_down_count_init)) {
+               idev->link_down_count_total = 0;
+               idev->link_down_count_last =
+                       le16_to_cpu(idev->port_info->status.link_down_count);
+               WRITE_ONCE(idev->link_down_count_init, true);
+       }
+}
index 5f677bcbaf02778700eca996d722b544a01c56dc..db90e39a1442d24dd9ff309157163de8e6bd79e8 100644 (file)
@@ -185,6 +185,9 @@ struct ionic_dev {
        struct ionic_port_info *port_info;
        dma_addr_t port_info_pa;
        struct ionic_port_extra_stats port_extra_stats_cache;
+       bool link_down_count_init;
+       u16 link_down_count_last;
+       u32 link_down_count_total;
 
        struct ionic_devinfo dev_info;
 };
@@ -395,4 +398,6 @@ bool ionic_adminq_poke_doorbell(struct ionic_queue *q);
 bool ionic_txq_poke_doorbell(struct ionic_queue *q);
 bool ionic_rxq_poke_doorbell(struct ionic_queue *q);
 
+void ionic_reset_link_down_count(struct ionic_dev *idev);
+
 #endif /* _IONIC_DEV_H_ */
index 6069fa460913187261b4461695e6633d45a8cf99..c4ab4b5caa0aea426ebb8507aa9609c1bef0afd2 100644 (file)
@@ -115,16 +115,32 @@ static void ionic_get_link_ext_stats(struct net_device *netdev,
                                     struct ethtool_link_ext_stats *stats)
 {
        struct ionic_lif *lif = netdev_priv(netdev);
+       struct ionic *ionic = lif->ionic;
+       u64 link_down_count_total;
+       u16 link_down_count_fw;
 
-       if (lif->ionic->pdev->is_virtfn)
+       if (ionic->pdev->is_virtfn)
                return;
 
-       if (!lif->ionic->idev.port_info) {
+       if (!ionic->idev.port_info) {
                netdev_err_once(netdev, "port_info not initialized\n");
                return;
        }
 
-       stats->link_down_events = lif->link_down_count;
+       link_down_count_fw =
+           le16_to_cpu(ionic->idev.port_info->status.link_down_count);
+       link_down_count_total = ionic->idev.link_down_count_total +
+                               link_down_count_fw -
+                               ionic->idev.link_down_count_last;
+
+       /* The firmware counter is only 16 bits and can wraparound */
+       if (link_down_count_fw < ionic->idev.link_down_count_last)
+               link_down_count_total += BIT(16);
+
+       ionic->idev.link_down_count_last = link_down_count_fw;
+       ionic->idev.link_down_count_total = link_down_count_total;
+
+       stats->link_down_events = link_down_count_total;
 }
 
 static int ionic_get_link_ksettings(struct net_device *netdev,
index 637e635bbf03a458ff94961a2e8a58246dc5e569..fd3ee98205319a10bce9371bc4056685ef30da94 100644 (file)
@@ -140,6 +140,7 @@ void ionic_lif_deferred_enqueue(struct ionic_lif *lif,
 
 static void ionic_link_status_check(struct ionic_lif *lif)
 {
+       struct ionic_dev *idev = &lif->ionic->idev;
        struct net_device *netdev = lif->netdev;
        u16 link_status;
        bool link_up;
@@ -153,6 +154,8 @@ static void ionic_link_status_check(struct ionic_lif *lif)
                return;
        }
 
+       ionic_reset_link_down_count(idev);
+
        link_status = le16_to_cpu(lif->info->status.link_status);
        link_up = link_status == IONIC_PORT_OPER_STATUS_UP;
 
@@ -179,7 +182,6 @@ static void ionic_link_status_check(struct ionic_lif *lif)
                }
        } else {
                if (netif_carrier_ok(netdev)) {
-                       lif->link_down_count++;
                        netdev_info(netdev, "Link down\n");
                        netif_carrier_off(netdev);
                }
index 8e10f66dc50e902b2ce08c6263aba500416d6104..d3469246203636a01f1aa762d9b0f77263230eb0 100644 (file)
@@ -214,7 +214,6 @@ struct ionic_lif {
        bool registered;
        bool doorbell_wa;
        u16 lif_type;
-       unsigned int link_down_count;
        unsigned int nmcast;
        unsigned int nucast;
        unsigned int nvlans;
index 306d9d160e175232f472115521e27a62a8ba3121..6e6f3ed07271b3ebd642efd0371cf31b602c6241 100644 (file)
@@ -737,6 +737,8 @@ int ionic_port_init(struct ionic *ionic)
        memset(&idev->port_info->extra_stats, 0xff,
               sizeof(idev->port_info->extra_stats));
 
+       WRITE_ONCE(idev->link_down_count_init, false);
+
        sz = min(sizeof(ident->port.config), sizeof(idev->dev_cmd_regs->data));
 
        mutex_lock(&ionic->dev_cmd_lock);