From patchwork Tue Sep 26 08:46:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shawn Lin X-Patchwork-Id: 9971545 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 2873660365 for ; Tue, 26 Sep 2017 08:47:21 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 14B5A2878C for ; Tue, 26 Sep 2017 08:47:21 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 06C7628812; Tue, 26 Sep 2017 08:47:21 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-3.9 required=2.0 tests=BAYES_00, RCVD_IN_BL_SPAMCOP_NET, RCVD_IN_DNSWL_HI, RCVD_IN_SBL, RCVD_IN_SORBS_WEB autolearn=no version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9F1482878C for ; Tue, 26 Sep 2017 08:47:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S937084AbdIZIrR (ORCPT ); Tue, 26 Sep 2017 04:47:17 -0400 Received: from lucky1.263xmail.com ([211.157.147.132]:56497 "EHLO lucky1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S936810AbdIZIrL (ORCPT ); Tue, 26 Sep 2017 04:47:11 -0400 Received: from shawn.lin?rock-chips.com (unknown [192.168.167.201]) by lucky1.263xmail.com (Postfix) with ESMTP id 9214564C54; Tue, 26 Sep 2017 16:46:59 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 1 X-MAIL-DELIVERY: 0 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost.localdomain (localhost [127.0.0.1]) by smtp.263.net (Postfix) with ESMTPA id DEDF93C4; Tue, 26 Sep 2017 16:46:55 +0800 (CST) X-RL-SENDER: shawn.lin@rock-chips.com X-FST-TO: jh80.chung@samsung.com X-SENDER-IP: 58.22.7.114 X-LOGIN-NAME: shawn.lin@rock-chips.com X-UNIQUE-TAG: X-ATTACHMENT-NUM: 0 X-SENDER: lintao@rock-chips.com X-DNS-TYPE: 0 Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.263.net (Postfix) whith ESMTP id 20066YO138M; Tue, 26 Sep 2017 16:46:57 +0800 (CST) From: Shawn Lin To: Jaehoon Chung , Ulf Hansson Cc: linux-mmc@vger.kernel.org, Shawn Lin Subject: [PATCH v4] mmc: dw_mmc: fix potential system abort Date: Tue, 26 Sep 2017 16:46:06 +0800 Message-Id: <1506415566-115405-1-git-send-email-shawn.lin@rock-chips.com> X-Mailer: git-send-email 1.9.1 Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP dw_mmc allows the variant drivers to register the interrupt as a shared irq, which means dw_mmc should guarantee to properly handle the irq at any time, even if the irq isn't belong to itself. So there is a timing gap that the dw_mmc is being removed but the shared irq comes concurrently. The clock/reset controller/power domain for accessing the controller had been off before the irq comes in, so that the irq handler, dw_mci_interrupt, would still try to read the MINTSTS to see if the irq belongs to this controller. However, at least for rockchip platforms, it doesn't allow to access the controller w/o clock and power domain be in the active state. To quick reproduce that, we could enable CONFIG_DEBUG_SHIRQ. the irq tear down routine would still access the irq handler registed as a shard irq. Per the comment within the function of __free_irq, it says "It's a shared IRQ -- the driver ought to be prepared for an IRQ event to happen even now it's being freed". So whenever we fail to probe the driver or unbind the driver, the system abort. Synopsys Designware Multimedia Card Interface Driver dwmmc_rockchip fe320000.dwmmc: IDMAC supports 32-bit address mode. dwmmc_rockchip fe320000.dwmmc: Using internal DMA controller. dwmmc_rockchip fe320000.dwmmc: Version ID is 270a CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.13.0-rc3-next-20170807-00004-g93d3644-dirty #5 Hardware name: Firefly-RK3399 Board (DT) Call trace: [] dump_backtrace+0x0/0x300 [] show_stack+0x14/0x20 [] dump_stack+0xa4/0xc8 [] dw_mci_interrupt+0x3c/0x6a8 [] __free_irq+0x308/0x410 [] free_irq+0x54/0xb0 [] devm_irq_release+0x30/0x40 [] release_nodes+0x1e4/0x390 [] devres_release_all+0x50/0x78 [] driver_probe_device+0x128/0x3b8 [] __driver_attach+0xe4/0xe8 [] bus_for_each_dev+0xe0/0x138 [] driver_attach+0x30/0x40 [] bus_add_driver+0x1d0/0x328 [] driver_register+0xb4/0x198 [] __platform_driver_register+0x7c/0x88 [] dw_mci_rockchip_pltfm_driver_init+0x18/0x20 [] do_one_initcall+0x14c/0x1b8 [] kernel_init_freeable+0x238/0x2d8 [] kernel_init+0x10/0x110 [] ret_from_fork+0x10/0x50 Synchronous External Abort: synchronous external abort (0x96000010) at 0xffff20000aaa4040 Internal error: : 96000010 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted task: ffff80006ba28080 task.stack: ffff80006ba24000 PC is at dw_mci_interrupt+0x4c/0x6a8 LR is at dw_mci_interrupt+0x44/0x6a8 pc : [] lr : [] pstate: 600001c5 In order to fix this more visible, we introduce new bool variable 'driver_removed' to bail out early from the irq handler if the driver had been removed. Signed-off-by: Shawn Lin --- Changes in v4: - remove the driver core change from v3 and add new flag to solve this issue instead of adding devm_add_action_or_reset. drivers/mmc/host/dw_mmc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 860313b..bb4c608 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2595,6 +2595,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) u32 pending; struct dw_mci_slot *slot = host->slot; + if (host->driver_removed) + return IRQ_NONE; + pending = mci_readl(host, MINTSTS); /* read-only mask reg */ if (pending) { @@ -3221,6 +3224,8 @@ int dw_mci_probe(struct dw_mci *host) else host->fifo_reg = host->regs + DATA_240A_OFFSET; + host->driver_removed = false; + tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); @@ -3266,6 +3271,7 @@ int dw_mci_probe(struct dw_mci *host) err_clk_biu: clk_disable_unprepare(host->biu_clk); + host->driver_removed = true; return ret; } EXPORT_SYMBOL(dw_mci_probe); @@ -3291,6 +3297,8 @@ void dw_mci_remove(struct dw_mci *host) clk_disable_unprepare(host->ciu_clk); clk_disable_unprepare(host->biu_clk); + + host->driver_removed = true; } EXPORT_SYMBOL(dw_mci_remove);