aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2016-05-10 16:30:05 +0100
committerRyan Harkin <ryan.harkin@linaro.org>2017-07-13 20:59:59 +0100
commit9da5337b14a15958c7645b210516c142fca2d01c (patch)
tree05c06e955a650db28b23e38cc36cbd39ef9d1b40
parentc45afde3606a1018d797c488f5a20750419f4f80 (diff)
sky2: Check power state before trying to read statslatest-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 1145cde2274a..86816afead98 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -3909,6 +3909,18 @@ static void 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; /* 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;