From patchwork Wed Nov 6 08:53:27 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Huang Shijie X-Patchwork-Id: 3145701 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4B7AFBEEB2 for ; Wed, 6 Nov 2013 09:14:53 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 710212039A for ; Wed, 6 Nov 2013 09:14:48 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1E34220395 for ; Wed, 6 Nov 2013 09:14:47 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VdzCB-0001VJ-44; Wed, 06 Nov 2013 09:14:39 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VdzC7-0000KA-CG; Wed, 06 Nov 2013 09:14:35 +0000 Received: from va3ehsobe006.messaging.microsoft.com ([216.32.180.16] helo=va3outboundpool.messaging.microsoft.com) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VdzC4-0000JU-GP; Wed, 06 Nov 2013 09:14:33 +0000 Received: from mail203-va3-R.bigfish.com (10.7.14.237) by VA3EHSOBE002.bigfish.com (10.7.40.22) with Microsoft SMTP Server id 14.1.225.22; Wed, 6 Nov 2013 09:14:10 +0000 Received: from mail203-va3 (localhost [127.0.0.1]) by mail203-va3-R.bigfish.com (Postfix) with ESMTP id B158D3401DD; Wed, 6 Nov 2013 09:14:10 +0000 (UTC) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-SpamScore: 3 X-BigFish: VS3(zzzz1f42h2148h208ch1ee6h1de0h1fdah2073h2146h1202h1e76h1d1ah1d2ah1fc6h1082kzz1de098h8275bh8275dh1de097hz2dh2a8h839hd24he5bhf0ah1288h12a5h12a9h12bdh12e5h137ah139eh13b6h1441h1504h1537h162dh1631h1758h1898h18e1h1946h19b5h1ad9h1b0ah1b2fh2222h224fh1fb3h1d0ch1d2eh1d3fh1dc1h1dfeh1dffh1e23h1fe8h1ff5h2218h2216h1155h) Received: from mail203-va3 (localhost.localdomain [127.0.0.1]) by mail203-va3 (MessageSwitch) id 1383729248442041_17177; Wed, 6 Nov 2013 09:14:08 +0000 (UTC) Received: from VA3EHSMHS023.bigfish.com (unknown [10.7.14.234]) by mail203-va3.bigfish.com (Postfix) with ESMTP id 5BEFFC0003F; Wed, 6 Nov 2013 09:14:08 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by VA3EHSMHS023.bigfish.com (10.7.99.33) with Microsoft SMTP Server (TLS) id 14.16.227.3; Wed, 6 Nov 2013 09:14:02 +0000 Received: from az84smr01.freescale.net (10.64.34.197) by 039-SN1MMR1-001.039d.mgd.msft.net (10.84.1.13) with Microsoft SMTP Server (TLS) id 14.3.158.2; Wed, 6 Nov 2013 09:14:01 +0000 Received: from shlinux2.ap.freescale.net (shlinux2.ap.freescale.net [10.192.224.44]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id rA69Dugm012722; Wed, 6 Nov 2013 02:13:59 -0700 From: Huang Shijie To: Subject: [PATCH bugfix] mtd: gpmi: serialize all the dma operations Date: Wed, 6 Nov 2013 16:53:27 +0800 Message-ID: <1383728007-15564-1-git-send-email-b32955@freescale.com> X-Mailer: git-send-email 1.7.2.rc3 MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-FOPE-CONNECTOR: Id%0$Dn%*$RO%0$TLS%0$FQDN%$TlsDn% X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131106_041432_635946_B512A464 X-CRM114-Status: GOOD ( 18.81 ) X-Spam-Score: -4.2 (----) Cc: dedekind1@gmail.com, stable@vger.kernel.org, Huang Shijie , linux-mtd@lists.infradead.org, computersforpeace@gmail.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP [1] The gpmi uses the nand_command_lp to issue the commands to NAND chips. It will issue a DMA operation when it handles a NAND_CMD_NONE control command. So when we read a page(NAND_CMD_READ0) from the NAND, we may send two DMA operations back-to-back. If we do not serialize the two DMA operations, we will meet a bug when 1.1) we enable CONFIG_DMA_API_DEBUG, CONFIG_DMADEVICES_DEBUG, and CONFIG_DEBUG_SG. 1.2) Use the following commands in an UART console and a SSH console: cmd 1: while true;do dd if=/dev/mtd0 of=/dev/null;done cmd 1: while true;do dd if=/dev/mmcblk0 of=/dev/null;done The kernel log shows below: ----------------------------------------------------------------- kernel BUG at lib/scatterlist.c:28! Unable to handle kernel NULL pointer dereference at virtual address 00000000 ......................... [<80044a0c>] (__bug+0x18/0x24) from [<80249b74>] (sg_next+0x48/0x4c) [<80249b74>] (sg_next+0x48/0x4c) from [<80255398>] (debug_dma_unmap_sg+0x170/0x1a4) [<80255398>] (debug_dma_unmap_sg+0x170/0x1a4) from [<8004af58>] (dma_unmap_sg+0x14/0x6c) [<8004af58>] (dma_unmap_sg+0x14/0x6c) from [<8027e594>] (mxs_dma_tasklet+0x18/0x1c) [<8027e594>] (mxs_dma_tasklet+0x18/0x1c) from [<8007d444>] (tasklet_action+0x114/0x164) ----------------------------------------------------------------- 1.3) Assume the two DMA operations is X (first) and Y (second). The root cause of the bug: X's tasklet mxs_dma_tasklet trid to unmap the scatterlist, while Y is trying to set up a new DMA operation with the _SAME_ scatterlist in another ARM core. [2] This patch adds a wait queue and two helpers gpmi_enter_dma/gpmi_exit_dma to serialize all the DMA operations. Signed-off-by: Huang Shijie Cc: stable@vger.kernel.org --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 24 ++++++++++++++++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 2 ++ 2 files changed, 26 insertions(+), 0 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 71df69e..b849b92 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -392,6 +392,20 @@ void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) } } +static void gpmi_enter_dma(struct gpmi_nand_data *this) +{ + /* Wait until the previous DMA is finished. */ + wait_event(this->dma_wait, !this->dma_is_working); + + this->dma_is_working = true; +} + +static void gpmi_exit_dma(struct gpmi_nand_data *this) +{ + this->dma_is_working = false; + wake_up(&this->dma_wait); +} + /* This will be called after the DMA operation is finished. */ static void dma_irq_callback(void *param) { @@ -424,6 +438,7 @@ static void dma_irq_callback(void *param) default: pr_err("in wrong DMA operation.\n"); } + gpmi_exit_dma(this); } int start_dma_without_bch_irq(struct gpmi_nand_data *this, @@ -906,6 +921,8 @@ static void gpmi_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) if (!this->command_length) return; + gpmi_enter_dma(this); + ret = gpmi_send_command(this); if (ret) pr_err("Chip: %u, Error %d\n", this->current_chip, ret); @@ -943,6 +960,8 @@ static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) this->upper_buf = buf; this->upper_len = len; + gpmi_enter_dma(this); + gpmi_read_data(this); } @@ -955,6 +974,8 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) this->upper_buf = (uint8_t *)buf; this->upper_len = len; + gpmi_enter_dma(this); + gpmi_send_data(this); } @@ -1031,6 +1052,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, int ret; pr_debug("page number is : %d\n", page); + gpmi_enter_dma(this); ret = read_page_prepare(this, buf, mtd->writesize, this->payload_virt, this->payload_phys, nfc_geo->payload_size, @@ -1107,6 +1129,7 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, int ret; pr_debug("ecc write page.\n"); + gpmi_enter_dma(this); if (this->swap_block_mark) { /* * If control arrives here, we're doing block mark swapping. @@ -1745,6 +1768,7 @@ static int gpmi_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, this); this->pdev = pdev; this->dev = &pdev->dev; + init_waitqueue_head(&this->dma_wait); ret = acquire_resources(this); if (ret) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index a7685e3..9597615 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -160,6 +160,8 @@ struct gpmi_nand_data { /* for DMA operations */ bool direct_dma_map_ok; + bool dma_is_working; + wait_queue_head_t dma_wait; struct scatterlist cmd_sgl; char *cmd_buffer;