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>
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);
+ }
+}
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;
};
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_ */
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,
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;
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;
}
} else {
if (netif_carrier_ok(netdev)) {
- lif->link_down_count++;
netdev_info(netdev, "Link down\n");
netif_carrier_off(netdev);
}
bool registered;
bool doorbell_wa;
u16 lif_type;
- unsigned int link_down_count;
unsigned int nmcast;
unsigned int nucast;
unsigned int nvlans;
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);