Skip to content

Commit

Permalink
net: fec: cache statistics while device is down
Browse files Browse the repository at this point in the history
Execution 'ethtool -S' on fec device that is down causes OOPS on Vybrid
board:

Unhandled fault: external abort on non-linefetch (0x1008) at 0xe0898200
pgd = ddecc000
[e0898200] *pgd=9e406811, *pte=400d1653, *ppte=400d1453
Internal error: : 1008 [#1] SMP ARM
...

Reason of OOPS is that fec_enet_get_ethtool_stats() accesses fec
registers while IPG clock is stopped by PM.

Fix that by caching statistics in fec_enet_private. Cache is initialized
at device probe time, and updated at statistics request time if device
is up, and also just before turning device off on down path.

Additional locking is not needed, since cached statistics is accessed
either before device is registered, or under rtnl_lock().

Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Nikita Yushchenko authored and David S. Miller committed Nov 30, 2016
1 parent 17b4636 commit 80cca77
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 4 deletions.
2 changes: 2 additions & 0 deletions drivers/net/ethernet/freescale/fec.h
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,8 @@ struct fec_enet_private {
unsigned int reload_period;
int pps_enable;
unsigned int next_counter;

u64 ethtool_stats[0];
};

void fec_ptp_init(struct platform_device *pdev);
Expand Down
23 changes: 19 additions & 4 deletions drivers/net/ethernet/freescale/fec_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2313,14 +2313,24 @@ static const struct fec_stat {
{ "IEEE_rx_octets_ok", IEEE_R_OCTETS_OK },
};

static void fec_enet_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
static void fec_enet_update_ethtool_stats(struct net_device *dev)
{
struct fec_enet_private *fep = netdev_priv(dev);
int i;

for (i = 0; i < ARRAY_SIZE(fec_stats); i++)
data[i] = readl(fep->hwp + fec_stats[i].offset);
fep->ethtool_stats[i] = readl(fep->hwp + fec_stats[i].offset);
}

static void fec_enet_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
struct fec_enet_private *fep = netdev_priv(dev);

if (netif_running(dev))
fec_enet_update_ethtool_stats(dev);

memcpy(data, fep->ethtool_stats, ARRAY_SIZE(fec_stats) * sizeof(u64));
}

static void fec_enet_get_strings(struct net_device *netdev,
Expand Down Expand Up @@ -2874,6 +2884,8 @@ fec_enet_close(struct net_device *ndev)
if (fep->quirks & FEC_QUIRK_ERR006687)
imx6q_cpuidle_fec_irqs_unused();

fec_enet_update_ethtool_stats(ndev);

fec_enet_clk_enable(ndev, false);
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
pm_runtime_mark_last_busy(&fep->pdev->dev);
Expand Down Expand Up @@ -3180,6 +3192,8 @@ static int fec_enet_init(struct net_device *ndev)

fec_restart(ndev);

fec_enet_update_ethtool_stats(ndev);

return 0;
}

Expand Down Expand Up @@ -3278,7 +3292,8 @@ fec_probe(struct platform_device *pdev)
fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);

/* Init network device */
ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private),
ndev = alloc_etherdev_mqs(sizeof(struct fec_enet_private) +
ARRAY_SIZE(fec_stats) * sizeof(u64),
num_tx_qs, num_rx_qs);
if (!ndev)
return -ENOMEM;
Expand Down

0 comments on commit 80cca77

Please sign in to comment.