aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2016-05-10 16:30:05 +0100
committerJon Medhurst <tixy@linaro.org>2017-02-20 09:38:00 +0000
commitf75c38970f1100e46773fd60490625d971ea94c7 (patch)
treef3bc6e53c63349552645d46e302dccdd5b759291
parent7b58b94f6622f7bd846e13c9b81d124174a4f040 (diff)
sky2: Check power state before trying to read statsprevious-armlt-misc
When the device is shutdown, the state change can trigger a notifier which tries to gather network statistics by calling sky2_get_stats. Unfortunately, this attempts to read registers in hardware which may have been powered down leading to a bus abort (see below). Partially workaround this by checking device power state in sky2_get_stats. Unhandled fault: synchronous external abort (0x96000210) at 0xffffff800017a918 Internal error: : 96000210 [#1] PREEMPT SMP CPU: 0 PID: 229 Comm: kworker/0:2 Not tainted 3.18.31-00004-g60c1593-dirty #2 Hardware name: ARM Juno development board (r2) (DT) Workqueue: events linkwatch_event task: ffffffc97665c0c0 ti: ffffffc9766e0000 task.ti: ffffffc9766e0000 PC is at sky2_get_stats+0x74/0x40c LR is at sky2_get_stats+0x408/0x40c Call trace: [<ffffffc0005eb3b4>] sky2_get_stats+0x74/0x40c [<ffffffc0007a8978>] dev_get_stats+0x68/0xd0 [<ffffffc0007b651c>] rtnl_fill_ifinfo+0x388/0x8e0 [<ffffffc0007b9764>] rtmsg_ifinfo+0x78/0x10c [<ffffffc0007a34b4>] netdev_state_change+0x48/0x54 [<ffffffc0007bacfc>] linkwatch_do_dev+0x50/0x88 [<ffffffc0007bb004>] __linkwatch_run_queue+0x164/0x198 [<ffffffc0007bb068>] linkwatch_event+0x30/0x3c [<ffffffc0000b7084>] process_one_work+0x150/0x458 [<ffffffc0000b74d8>] worker_thread+0x14c/0x47c [<ffffffc0000bd1f0>] kthread+0xe0/0xf4 Signed-off-by: Jon Medhurst <tixy@linaro.org>
-rw-r--r--drivers/net/ethernet/marvell/sky2.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index b60ad0e56a9f..237452247fc2 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -3897,6 +3897,18 @@ static struct rtnl_link_stats64 *sky2_get_stats(struct net_device *dev,
unsigned int start;
u64 _bytes, _packets;
+ /* Try and check if device if off. If it is, abort gathering stats as
+ * any attempt to read hardware registers will generate a bus fault.
+ * This test is hacky and racy as there's nothing stopping the device
+ * being powered off immediately after the test.
+ */
+ if (hw->pdev->pm_cap) {
+ u16 pmcsr;
+ int ret = pci_read_config_word(hw->pdev, hw->pdev->pm_cap + PCI_PM_CTRL, &pmcsr);
+ if (ret || (pmcsr & PCI_PM_CTRL_STATE_MASK) > PCI_D2)
+ return stats; /* Can't read power state or it's state D3 (off) */
+ }
+
do {
start = u64_stats_fetch_begin_irq(&sky2->rx_stats.syncp);
_bytes = sky2->rx_stats.bytes;