RE: [EXT] Re: [PATCH V1] mtd: core: Micron SLC NAND filling block

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Boris,

Are there  non UBI/UBIFS cases?
Can you please be specific?
Zoltan 

-----Original Message-----
From: Boris Brezillon [mailto:bbrezillon@xxxxxxxxxx] 
Sent: Mittwoch, 19. Dezember 2018 14:18
To: Bean Huo (beanhuo) <beanhuo@xxxxxxxxxx>
Cc: linux-mtd@xxxxxxxxxxxxxxxxxxx; Miquel Raynal <miquel.raynal@xxxxxxxxxxx>; Richard Weinberger <richard@xxxxxx>; Zoltan Szubbocsev (zszubbocsev) <zszubbocsev@xxxxxxxxxx>; tglx@xxxxxxxxxxxxx
Subject: [EXT] Re: [PATCH V1] mtd: core: Micron SLC NAND filling block

Hi Bean

On Wed, 19 Dec 2018 11:03:06 +0000
"Bean Huo (beanhuo)" <beanhuo@xxxxxxxxxx> wrote:

> On some legacy planar 2D Micron NAND devices when a block erase 
> command is issued, occasionally even though a block erase operation 
> successfully completes and returns a pass status, the flash block may 
> not be completely erased.
> Subsequent operations to this block on very rare cases can result in 
> subtle failures or corruption. These extremely rare cases should 
> nevertheless be considered.
> 
> These rare occurrences have been observed on partially written blocks. 
> Partially written blocks are not uncommon with UBI/UBIFS.
> 
> To avoid this rare occurrence, we make sure that at least 15 pages 
> have been programmed to a block before it is erased. In case we find 
> that less than 15 pages have been programmed, additional pages are 
> programmed in the block. The observation is that additional pages 
> rarely need to be written and most of the time UBI/UBIFS erases blocks 
> that contain more programmed pages.

Okay, so 15 is the right number :-). Can you tell us which parts are potentially impacted by this problem?

Regarding the patch itself, it's breaking the layering we have in MTD/UBI/UBIFS and does not address the non-UBI/UBIFS case, so I don't think we'll go for this approach. But that's good to have Micron's feedback on this issue.

Thanks,

Boris

> 
> Signed-off-by: beanhuo <beanhuo@xxxxxxxxxx>
> Reviewed-by:  ZOLTAN SZUBBOCSEV < zszubbocsev@xxxxxxxxxx>
> ---
>  drivers/mtd/mtdcore.c | 125 
> ++++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mtd/mtdcore.h |  16 +++++++
>  2 files changed, 141 insertions(+)
> 
> diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 
> 8bbbb75..b3879b5 100644
> --- a/drivers/mtd/mtdcore.c
> +++ b/drivers/mtd/mtdcore.c
> @@ -801,6 +801,127 @@ void __put_mtd_device(struct mtd_info *mtd)  }  
> EXPORT_SYMBOL_GPL(__put_mtd_device);
>  
> +static int mtd_check_pattern(const void *buf, uint8_t patt, int size) 
> +{
> +	int i;
> +
> +	for (i = 0; i < size; i++)
> +		if (((const uint8_t *)buf)[i] != patt)
> +			return 0;
> +	return 1;
> +}
> +
> +static int checkup_partial_filling(struct mtd_info *mtd, struct 
> +erase_info *instr) {
> +	int retlen;
> +	int ret;
> +	u_char * data_buf, * oob_buf;
> +	uint32_t empty_page_mask = 0;
> +	uint32_t programmed_page = 0;
> +	loff_t addr;
> +	int nextpage = LAST_CHECKPU_PAGE; /* We defined the maximum page to check is page14,
> +	                      first page is page0*/
> +	int cur_page = 0;
> +	ret = 0;
> +
> +	if ((mtd->type != MTD_NANDFLASH) && (mtd->type != MTD_MLCNANDFLASH))
> +		return 0; /* Only works on NAND */
> +
> +	data_buf = kmalloc(mtd->writesize, GFP_KERNEL);
> +	oob_buf = kmalloc(mtd->oobsize, GFP_KERNEL);
> +	addr = instr->addr; /* this is page0 address*/
> +
> +	if (data_buf)
> +	    memset(data_buf, 0xff, mtd->writesize);
> +	else
> +	    goto end;
> +	if (oob_buf)
> +	    memset(oob_buf, 0x00, mtd->oobsize);
> +	else
> +	    goto end;
> +
> +	for( ; nextpage > 0; ) {
> +		ret = mtd_read(mtd, addr, mtd->writesize, &retlen, data_buf);
> +		if ((ret) || (retlen != mtd->writesize))
> +			goto next; /* Read error, currently, we directly skip it */
> +
> +		if (mtd_check_pattern(data_buf, 0xff, retlen)) {
> +			if (cur_page == 0)
> +			    break; /* page 0 is empty, skip block filling checkup */
> +
> +			/* Mark page need to program later */
> +			empty_page_mask |= (1 << cur_page);
> +		} else {
> +			programmed_page |= (1 << cur_page);
> +			if (LAST_CHECKPU_PAGE != nextpage)
> +		/*
> +		 * Because we checked page0 firstly, at this time the nextpage
> +		 * is page13. And if page0 is programmed, then we start to
> +		 * check from page13,page11,page9...,page3,respectively.
> +		 * We olny check odd page. So if the nextpage is not page13
> +		 * and cur_page is programmed, we consider that the current
> +		 * cur_page is the last programmed page since the pages of PEB
> +		 * are programmed sequentially.
> +		*/
> +			break;
> +		}
> +next:
> +		addr = instr->addr + mtd->writesize * nextpage;
> +		cur_page = nextpage;
> +		if (nextpage > 1)
> +			nextpage = ((nextpage % 2) ? (nextpage - 2) : (nextpage - 1));
> +		else
> +			break;
> +	}
> +
> +	if (empty_page_mask == 0x00)
> +		goto end;
> +
> +	int i;
> +	struct ubifs_filling_head * filling = data_buf;
> +	filling->magic = 0x00000000;
> +	filling->sqnum = 0;
> +	filling->node_type = 0xCE;
> +	filling->group_type = 0;
> +	filling->padding[0] = filling->padding[1] = 0xAA;
> +	filling->len = cpu_to_le32(UBIFS_PEB_PAD_NODE_SZ);
> +	int pad  = mtd->writesize - UBIFS_PEB_PAD_NODE_SZ;
> +	filling->pad_len = cpu_to_le32(pad);
> +	filling->crc = 0x00000000;
> +	memset(data_buf + UBIFS_PEB_PAD_NODE_SZ, 0xAA, pad);
> +
> +    struct mtd_oob_ops ops;
> +    ops.mode = MTD_OPS_RAW;
> +    ops.len = 0;
> +    ops.ooblen = mtd->oobsize - 16;
> +    ops.ooboffs = 16;
> +    ops.datbuf = NULL;
> +    ops.oobbuf = oob_buf;
> + 
> +	empty_page_mask|=0x3;
> +	/* Always over-write EC and VID pages with a wrong EC/VID magic 
> +bytes. */
> +
> +	for ( i = 0; i <= LAST_CHECKPU_PAGE ; ) {
> +        /* Start to program  filling data into empty page,
> +	   we only program the odd page. */
> +		if (empty_page_mask & (1 << i)) {
> +			addr = instr->addr + mtd->writesize * i;
> +            if ((i == 0) || (i == 1)) {
> +            ret = mtd_write(mtd, addr, mtd->writesize, &retlen, data_buf);
> +            ret = mtd->_write_oob(mtd, addr, &ops); /* clear OOB */
> +            } else
> +            ret = mtd_write(mtd, addr, mtd->writesize, &retlen, data_buf);
> +		}
> +		if (i == 0)
> +		i = 1;
> +		else
> +		i += 2;
> +	}
> +end:
> +	kfree(data_buf);
> +	kfree(oob_buf);
> +	return 0;
> +}
>  /*
>   * Erase is an asynchronous operation.  Device drivers are supposed
>   * to call instr->callback() whenever the operation completes, even 
> @@ -820,6 +941,10 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
>  		mtd_erase_callback(instr);
>  		return 0;
>  	}
> +
> +#ifdef PARTIAL_FILLING_CHECKUP
> +    checkup_partial_filling(mtd, instr); #endif
>  	return mtd->_erase(mtd, instr);
>  }
>  EXPORT_SYMBOL_GPL(mtd_erase);
> diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index 
> 7b03533..0e5ad67 100644
> --- a/drivers/mtd/mtdcore.h
> +++ b/drivers/mtd/mtdcore.h
> @@ -5,6 +5,22 @@
>  
>  extern struct mutex mtd_table_mutex;
>  
> +#define PARTIAL_FILLING_CHECKUP 1 /* Used to turn on/off partial filling
> +		 block checkup before erasing this block.*/ #define 
> +LAST_CHECKPU_PAGE 13 struct ubifs_filling_head {
> +	__le32 magic;
> +	__le32 crc;
> +	__le64 sqnum;
> +	__le32 len;
> +	__u8 node_type;
> +	__u8 group_type;
> +	__u8 padding[2];
> +	__le32 pad_len;
> +} __packed;
> +
> +#define UBIFS_PEB_PAD_NODE_SZ  sizeof(struct ubifs_filling_head)
> +
>  struct mtd_info *__mtd_next_device(int i);  int add_mtd_device(struct 
> mtd_info *mtd);  int del_mtd_device(struct mtd_info *mtd);


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/



[Index of Archives]     [LARTC]     [Bugtraq]     [Yosemite Forum]     [Photo]

  Powered by Linux