From patchwork Thu Nov 29 21:12:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Gleixner X-Patchwork-Id: 1005647 X-Patchwork-Delegate: boris.brezillon@free-electrons.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linutronix.de Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="UHQ4X/Zh"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 435VcB1n7Yz9s9m for ; Fri, 30 Nov 2018 08:13:13 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:Subject:To:From :Date:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=d3H4yjf1CfLS6cGIPYiZmbfP5iQF0UfkvA2kqgGPSSI=; b=UHQ4X/ZhnIeM+F vzeIZoZKtc8IhU+/RZ7DxuC6iSBs+zixoKMk7zNmR8R62uC7/OMB8rXyTVPi/OzoubfUNtMpjl+gu cA9bqeEv+CLM2bORnZxb61dwkezKphyZHCGfSxQ+reVKpOvDZ5MtEMTDzLNNprUD4m1ykv+IbAQ54 b4ySNui+CQPBgrBPpIk8wF5gRvfQOehArMjdK9eGKy+YKlz+fsoLYZY8p55fJ5QYLMoV5AZ5O2rA1 lHELbInIb0f/CNFVtjODkVPtI097Zjib2jeB81FPIbDGdFmJx8hJB+C4vZYo/fR1GavrDnjw3Dlhd 6iH1a/OHsbg1gVNt2Dcw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gSTcT-000872-53; Thu, 29 Nov 2018 21:13:09 +0000 Received: from galois.linutronix.de ([2a01:7a0:2:106d:700::1]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gSTcQ-00086W-7y for linux-mtd@lists.infradead.org; Thu, 29 Nov 2018 21:13:08 +0000 Received: from p4fea46ac.dip0.t-ipconnect.de ([79.234.70.172] helo=nanos) by Galois.linutronix.de with esmtpsa (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1gSTcA-0000hF-Te; Thu, 29 Nov 2018 22:12:51 +0100 Date: Thu, 29 Nov 2018 22:12:50 +0100 (CET) From: Thomas Gleixner To: linux-mtd@lists.infradead.org Subject: [PATCH RFC] mtd: rawnand: Cure MICRON NAND partial erase issue Message-ID: User-Agent: Alpine 2.21 (DEB 202 2017-01-01) MIME-Version: 1.0 X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1, SHORTCIRCUIT=-0.0001 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181129_131306_425039_3F78408E X-CRM114-Status: GOOD ( 23.41 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [2a01:7a0:2:106d:700:0:0:1 listed in] [list.dnswl.org] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Boris Brezillon , Richard Weinberger , Miquel Raynal Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org On some Micron NAND chips block erase fails occasionaly despite the chip claiming that it succeeded. The flash block seems to be not completely erased and subsequent usage of the block results in hard to decode and very subtle failures or corruption. The exact reason is unknown, but experimentation has shown that it is only happening when erasing an erase block which is partially written. Partially written erase blocks are not uncommon with UBI/UBIFS. Note, that this does not always happen. It's a rare and random, but eventually fatal failure. For now, just blindly write 6 pages to 0. Again experimentation has shown that it's not sufficient to write pages at the beginning of the erase block. There need to be pages written in the second half of the erase block as well. So write 3 pages before and past the middle of the block. Less than 6 pages might be sufficient, but it might even be necessary to write more pages to make sure that it's completely cured. Two pages still failed, but the 6 held up in a stress test scenario. This should be optimized by keeping track of writes, but that needs proper information about the issue. As it's just observation and experimentation based, it's probably wise to hold off on this until there is proper clarification about the root cause of the problem. The patch is for reference so others can avoid to decode this again, but there is no guarantee that it actually fixes the issue completely. Therefore: Not-yet-signed-off-by: Thomas Gleixner Cc: Boris Brezillon Cc: Miquel Raynal Cc: Richard Weinberger --- P.S.: This was debugged on an older kernel version (sigh) and ported forward without actual testing on mainline. My MTD foo is a bit rusty, so I won't be surprised if there are better ways to do that. --- drivers/mtd/nand/raw/nand_base.c | 89 +++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/nand_micron.c | 6 ++ include/linux/mtd/rawnand.h | 3 + 3 files changed, 98 insertions(+) --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4122,6 +4122,91 @@ static int nand_erase(struct mtd_info *m return nand_erase_nand(mtd_to_nand(mtd), instr, 0); } +static bool page_empty(char *buf, int len) +{ + unsigned int *p = (unsigned int *) buf; + int i; + + for (i = 0; i < len >> 2; i++, p++) { + if (*p != UINT_MAX) + return false; + } + return true; +} + +#define NAND_ERASE_QUIRK_PAGES 6 + +/** + * nand_erase_quirk - [INTERN] Work around partial erase issues + * @chip: NAND chip object + * @page: Eraseblock base page number + * + * On some Micron NAND chips block erase fails occasionaly despite the chip + * claiming that it succeeded. The flash block seems to be not completely + * erased and subsequent usage of the block results in hard to decode and + * very subtle failures or corruption. + * + * The exact reason is unknown, but experimentation has shown that it is + * only happening when erasing an erase block which is only partially + * written. Partially written erase blocks are not uncommon with UBI/UBIFS. + * Note, that this does not always happen. It's a rare and random, but + * eventually fatal failure. + * + * For now, just blindly write 6 pages to 0. Again experimentation has + * shown that it's not sufficient to write pages at the beginning of the + * erase block. There need to be pages written in the second half of the + * erase block as well. So write 3 pages before and past the middle of the + * block. + * + * Less than 6 pages might be sufficient, but it might even be necessary to + * write more pages to make sure that it's completely cured. 2 pages still + * failed, but the 6 held up in a stress test scenario. + * + * FIXME: This should be optimized by keeping track of writes, but that + * needs proper information about the issue. + */ +static int nand_erase_quirk(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + unsigned int i, offs; + u8 *buf; + + if (!(chip->options & NAND_ERASE_QUIRK)) + return 0; + + buf = kmalloc(mtd->writesize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Start at (pages_per_block / 2) - 3 */ + offs = 1 << (chip->phys_erase_shift - chip->page_shift); + offs = (offs >> 1) - (NAND_ERASE_QUIRK_PAGES / 2); + page = page + offs; + + for (i = 0; i < NAND_ERASE_QUIRK_PAGES; i++, page++ ) { + struct mtd_oob_ops ops = { + .datbuf = buf, + .len = mtd->writesize, + }; + + /* + * Read the page back and check whether it is completely + * empty. + */ + nand_do_read_ops(mtd, page << chip->page_shift, &ops); + if (page_empty(buf, mtd->writesize)) + continue; + memset(buf, 0, mtd->writesize); + /* + * Fill page with zeros. Ignore write failure as there + * is no way to recover here. + */ + nand_do_write_ops(mtd, page << chip->page_shift, &ops); + } + kfree(buf); + return 0; +} + /** * nand_erase_nand - [INTERN] erase block(s) * @chip: NAND chip object @@ -4186,6 +4271,10 @@ int nand_erase_nand(struct nand_chip *ch (page + pages_per_block)) chip->pagebuf = -1; + ret = nand_erase_quirk(mtd, page); + if (ret) + goto erase_exit; + if (chip->legacy.erase) status = chip->legacy.erase(chip, page & chip->pagemask); --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -447,6 +447,12 @@ static int micron_nand_init(struct nand_ if (ret) goto err_free_manuf_data; + /* + * FIXME: Mark all Micron flash with the ERASE QUIRK bit for now as + * it is unclear which flash types are affected/ + */ + chip->options |= NAND_ERASE_QUIRK; + if (mtd->writesize == 2048) chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -163,6 +163,9 @@ enum nand_ecc_algo { /* Device needs 3rd row address cycle */ #define NAND_ROW_ADDR_3 0x00004000 +/* Device requires erase quirk */ +#define NAND_ERASE_QUIRK 0x00008000 + /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG