aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2016-05-10 16:30:05 +0100
committerAmit Daniel Kachhap <amit.kachhap@arm.com>2018-07-10 15:33:06 +0530
commitacb260838f627dafd172ff412253326acd24c175 (patch)
tree9e6cc25fed3b83763de905b696d1a1e4287b2025
parent43e3f2cbbf8fdc3629c3264bf762fe962e7f43ee (diff)
sky2: Check power state before trying to read stats
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 697d9b374f5e..77012649cb4e 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;