diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index 3838c4a70766..648d9d24be85 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -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); + } +} diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index 5f677bcbaf02..db90e39a1442 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -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_ */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 6069fa460913..c4ab4b5caa0a 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -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, diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 637e635bbf03..fd3ee9820531 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -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); } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index 8e10f66dc50e..d34692462036 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -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; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index 306d9d160e17..6e6f3ed07271 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -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);