From patchwork Thu Mar 30 06:45:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masahiro Yamada X-Patchwork-Id: 96225 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp102028qgd; Wed, 29 Mar 2017 23:48:17 -0700 (PDT) X-Received: by 10.84.169.227 with SMTP id h90mr5187513plb.155.1490856497562; Wed, 29 Mar 2017 23:48:17 -0700 (PDT) Return-Path: Received: from bombadil.infradead.org (bombadil.infradead.org. [65.50.211.133]) by mx.google.com with ESMTPS id l69si1244830pfk.178.2017.03.29.23.48.17 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 29 Mar 2017 23:48:17 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-mtd-bounces+patch=linaro.org@lists.infradead.org designates 65.50.211.133 as permitted sender) client-ip=65.50.211.133; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.infradead.org; dkim=neutral (body hash did not verify) header.i=@nifty.com; spf=pass (google.com: best guess record for domain of linux-mtd-bounces+patch=linaro.org@lists.infradead.org designates 65.50.211.133 as permitted sender) smtp.mailfrom=linux-mtd-bounces+patch=linaro.org@lists.infradead.org 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:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=V+MJwsSG7DLUquZZ2ZXltetpBbOZsidScsm4u/Vixi8=; b=tOhrjedaqB8lCZztjyvgpIIZTu VsE+jcxTW47L70j4EvzEO0GlGuLiA44sOJToQqabTgUjY92IST/8Ug7h0qqALUVKncdkz4FkkxxH6 S8vGAdRYWuKa7YJMtghi8zxRWAoGEHdFUPwG7RYAbUfcTVB4Blss9PPOufIs8u3UVFlPQ/bA5OLkd XcBtOzHKlB1nz5Kb3xaXeXoVH2VBUxLLDrT+FQ6dMN4njnJHpGuTlOGfutYaJnBViVEPsD7kisbGN X3gZHzO0QVuSFJfpYgGQFnBuNn9BBkTtwD2AdWmXPvw79OQu+IeHquvD3DeXCCKLQsJXmk8Ot5ncy W2iaff1Q==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1ctTsW-0007p8-SM; Thu, 30 Mar 2017 06:48:16 +0000 Received: from conuserg-07.nifty.com ([210.131.2.74]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1ctTs2-0007Dr-HR for linux-mtd@lists.infradead.org; Thu, 30 Mar 2017 06:47:51 +0000 Received: from pug.e01.socionext.com (p14092-ipngnfx01kyoto.kyoto.ocn.ne.jp [153.142.97.92]) (authenticated) by conuserg-07.nifty.com with ESMTP id v2U6kUcT015463; Thu, 30 Mar 2017 15:46:37 +0900 DKIM-Filter: OpenDKIM Filter v2.10.3 conuserg-07.nifty.com v2U6kUcT015463 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nifty.com; s=dec2015msa; t=1490856398; bh=Vvvu7BULYp1j0K8eMvYARnzOWXYbp5KyGqkurIGuYmI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cUDaXbuJeFOE2/yjHebfkGKnA48LoDCPKPYcN/hfByP/4IZ9MAvnK0INBxTbuPujl HQomKw3vxmMAjykwKg5N7/TFGq0XwLRev8+2VIhR17topUQnvCi+cP/9PFvJKBJBr+ jidS3+A6IUCGNiOX9ccd0y7rPxQkJ9f8OSknrzFpKdPF0N2Wd5mMQB61hh0u1P0XiR ZxUMRrmoPp+viVkgn66PaCB/bgLC8X0owQylkzeoBthV4oANDkBO05FUOiF74s5iOX rTD/rn1N/bhKFowJRx1mk9jvbKffV95e048PiDR7oeevWKlDyRJnqA5Y+74BVumC8p 44Ev4fwwi+kPA== X-Nifty-SrcIP: [153.142.97.92] From: Masahiro Yamada To: linux-mtd@lists.infradead.org Subject: [PATCH v3 05/37] mtd: nand: denali: fix erased page checking Date: Thu, 30 Mar 2017 15:45:51 +0900 Message-Id: <1490856383-31560-6-git-send-email-yamada.masahiro@socionext.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1490856383-31560-1-git-send-email-yamada.masahiro@socionext.com> References: <1490856383-31560-1-git-send-email-yamada.masahiro@socionext.com> X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.2 (-) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-1.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.7 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 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 , Dinh Nguyen , Masahiro Yamada , Artem Bityutskiy , Cyrille Pitchen , linux-kernel@vger.kernel.org, Marek Vasut , Masami Hiramatsu , Chuanxiao Dong , Jassi Brar , Brian Norris , Enrico Jorns , David Woodhouse , Graham Moore MIME-Version: 1.0 Sender: "linux-mtd" Errors-To: linux-mtd-bounces+patch=linaro.org@lists.infradead.org This part is wrong in multiple ways: [1] is_erased() is called against "buf" twice, so the OOB area is not checked at all. The second call should check chip->oob_poi. [2] This code block is nested by double "if (check_erase_page)". The inner one is redundant. [3] The ECC_ERROR_ADDRESS register reports which sector(s) had uncorrectable ECC errors. It is pointless to check the whole page if only one sector contains errors. [4] Unfortunately, the Denali ECC correction engine has already manipulated the data buffer before it decides the bitflips are uncorrectable. That is, not all of the data are 0xFF after an erased page is processed by the ECC engine. The current is_erased() helper could report false-positive ECC errors. Actually, a certain mount of bitflips are allowed in an erased page. The core framework provides nand_check_erased_ecc_chunk() that takes the threshold into account. Let's use this. This commit reworks the code to solve those problems. Please note the erased page checking is implemented as a separate helper function instead of embedding it in the loop in handle_ecc(). The reason is that OOB data are needed for the erased page checking, but the controller can not start a new transaction until all ECC error information is read out from the registers. Signed-off-by: Masahiro Yamada --- Changes in v3: - Fix nand_check_erased_ecc_chunk call logic. Iterate over only needed ECC chunks. Changes in v2: - Squash some patches into one. - Use nand_check_erased_ecc_chunk() with threshold drivers/mtd/nand/denali.c | 77 ++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 27 deletions(-) -- 2.7.4 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/ diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index c5c150a..64a3bdc 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -818,19 +818,44 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) } } -/* - * this function examines buffers to see if they contain data that - * indicate that the buffer is part of an erased region of flash. - */ -static bool is_erased(uint8_t *buf, int len) +static int denali_check_erased_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + unsigned long uncor_ecc_flags, + unsigned int max_bitflips) { - int i; + uint8_t *ecc_code = chip->buffers->ecccode; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int i, ret, stat; + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + for (i = 0; i < ecc_steps; i++) { + if (!(uncor_ecc_flags & BIT(i))) + continue; + + stat = nand_check_erased_ecc_chunk(buf, ecc_size, + ecc_code, ecc_bytes, + NULL, 0, + chip->ecc.strength); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } - for (i = 0; i < len; i++) - if (buf[i] != 0xFF) - return false; - return true; + buf += ecc_size; + ecc_code += ecc_bytes; + } + + return max_bitflips; } + #define ECC_SECTOR_SIZE 512 #define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) @@ -841,7 +866,7 @@ static bool is_erased(uint8_t *buf, int len) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali, - uint8_t *buf, bool *check_erased_page) + uint8_t *buf, unsigned long *uncor_ecc_flags) { unsigned int bitflips = 0; unsigned int max_bitflips = 0; @@ -868,11 +893,10 @@ static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali, if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) { /* - * if the error is not correctable, need to look at the - * page to see if it is an erased page. if so, then - * it's not a real ECC error + * Check later if this is a real ECC error, or + * an erased sector. */ - *check_erased_page = true; + *uncor_ecc_flags |= BIT(err_sector); } else if (err_byte < ECC_SECTOR_SIZE) { /* * If err_byte is larger than ECC_SECTOR_SIZE, means error @@ -1045,7 +1069,6 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - unsigned int max_bitflips = 0; struct denali_nand_info *denali = mtd_to_denali(mtd); dma_addr_t addr = denali->buf.dma_buf; @@ -1053,7 +1076,8 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint32_t irq_status; uint32_t irq_mask = INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR; - bool check_erased_page = false; + unsigned long uncor_ecc_flags = 0; + int stat = 0; if (page != denali->page) { dev_err(denali->dev, @@ -1078,21 +1102,20 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, memcpy(buf, denali->buf.buf, mtd->writesize); if (irq_status & INTR__ECC_ERR) - max_bitflips = handle_ecc(mtd, denali, buf, &check_erased_page); + stat = handle_ecc(mtd, denali, buf, &uncor_ecc_flags); denali_enable_dma(denali, false); - if (check_erased_page) { + if (stat < 0) + return stat; + + if (uncor_ecc_flags) { read_oob_data(mtd, chip->oob_poi, denali->page); - /* check ECC failures that may have occurred on erased pages */ - if (check_erased_page) { - if (!is_erased(buf, mtd->writesize)) - mtd->ecc_stats.failed++; - if (!is_erased(buf, mtd->oobsize)) - mtd->ecc_stats.failed++; - } + stat = denali_check_erased_page(mtd, chip, buf, + uncor_ecc_flags, stat); } - return max_bitflips; + + return stat; } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,