MTD: spin_lock stuck problem about marking bad block for mtd driver [1/1]

PD#SWPL-5613

Problem:
boot fail because of the UBI write error

Solution:
fixed nested-calls spin-lock when update bbt

Verify:
AXG-S420

Change-Id: I56b603ebffa5557ab7949ead2fddd41323346d09
Signed-off-by: xianjun.liu <xianjun.liu@amlogic.com>
This commit is contained in:
xianjun.liu
2019-03-07 17:26:42 +08:00
committed by Dongjin Kim
parent 38c296498b
commit bcffc2c811
5 changed files with 139 additions and 101 deletions

View File

@@ -129,10 +129,12 @@ ssize_t dtb_read(struct file *file,
loff_t *ppos)
{
u8 *dtb_ptr = NULL;
/*struct nand_flash *flash = &aml_chip_dtb->flash;*/
struct nand_chip *chip = &aml_chip_dtb->chip;
struct mtd_info *mtd = aml_chip_dtb->mtd;
ssize_t read_size = 0;
int ret = 0;
loff_t addr;
int chipnr;
if (*ppos == aml_chip_dtb->dtbsize)
return 0;
@@ -146,8 +148,10 @@ ssize_t dtb_read(struct file *file,
if (dtb_ptr == NULL)
return -ENOMEM;
/*not need nand_get_device here, mtd->_read_xx will done with it*/
/*nand_get_device(mtd, FL_READING);*/
addr = *ppos;
nand_get_device(mtd, FL_READING);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
ret = amlnf_dtb_read((u8 *)dtb_ptr, aml_chip_dtb->dtbsize);
if (ret) {
pr_info("%s: read failed:%d\n", __func__, ret);
@@ -163,8 +167,8 @@ ssize_t dtb_read(struct file *file,
ret = copy_to_user(buf, (dtb_ptr + *ppos), read_size);
*ppos += read_size;
exit:
/*nand_release_device(mtd);*/
/* kfree(dtb_ptr); */
chip->select_chip(mtd, -1);
nand_release_device(mtd);
vfree(dtb_ptr);
return read_size;
}
@@ -175,9 +179,11 @@ ssize_t dtb_write(struct file *file,
{
u8 *dtb_ptr = NULL;
ssize_t write_size = 0;
/*struct nand_flash *flash = &aml_chip_dtb->flash;*/
struct mtd_info *mtd = aml_chip_dtb->mtd;
int ret = 0;
struct nand_chip *chip = &aml_chip_dtb->chip;
struct mtd_info *mtd = aml_chip_dtb->mtd;
loff_t addr;
int chipnr;
if (*ppos == aml_chip_dtb->dtbsize)
return 0;
@@ -191,8 +197,10 @@ ssize_t dtb_write(struct file *file,
if (dtb_ptr == NULL)
return -ENOMEM;
/*not need nand_get_device here, mtd->_read_xx will done with it*/
/*nand_get_device(mtd, FL_WRITING);*/
addr = *ppos;
nand_get_device(mtd, FL_WRITING);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
ret = amlnf_dtb_read((u8 *)dtb_ptr, aml_chip_dtb->dtbsize);
if (ret) {
pr_info("%s: read failed\n", __func__);
@@ -216,8 +224,8 @@ ssize_t dtb_write(struct file *file,
*ppos += write_size;
exit:
/*nand_release_device(mtd);*/
/* kfree(dtb_ptr); */
chip->select_chip(mtd, -1);
nand_release_device(mtd);
vfree(dtb_ptr);
return write_size;
}

View File

@@ -189,6 +189,10 @@ ssize_t uboot_env_read(struct file *file,
u8 *env_ptr = NULL;
ssize_t read_size = 0;
int ret = 0;
struct nand_chip *chip = &aml_chip_env->chip;
struct mtd_info *mtd = aml_chip_env->mtd;
loff_t addr;
int chipnr;
if (*ppos == CONFIG_ENV_SIZE)
return 0;
@@ -201,7 +205,11 @@ ssize_t uboot_env_read(struct file *file,
env_ptr = vmalloc(CONFIG_ENV_SIZE + 2048);
if (env_ptr == NULL)
return -ENOMEM;
mutex_lock(&env_mutex);
addr = *ppos;
nand_get_device(mtd, FL_READING);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
/*amlnand_get_device(aml_chip_env, CHIP_READING);*/
ret = amlnf_env_read((u8 *)env_ptr, CONFIG_ENV_SIZE);
@@ -220,7 +228,8 @@ ssize_t uboot_env_read(struct file *file,
ret = copy_to_user(buf, (env_ptr + *ppos), read_size);
*ppos += read_size;
exit:
mutex_unlock(&env_mutex);
chip->select_chip(mtd, -1);
nand_release_device(mtd);
/*amlnand_release_device(aml_chip_env);*/
vfree(env_ptr);
return read_size;
@@ -233,6 +242,10 @@ ssize_t uboot_env_write(struct file *file,
u8 *env_ptr = NULL;
ssize_t write_size = 0;
int ret = 0;
struct nand_chip *chip = &aml_chip_env->chip;
struct mtd_info *mtd = aml_chip_env->mtd;
loff_t addr;
int chipnr;
if (*ppos == CONFIG_ENV_SIZE)
return 0;
@@ -246,7 +259,10 @@ ssize_t uboot_env_write(struct file *file,
if (env_ptr == NULL)
return -ENOMEM;
mutex_lock(&env_mutex);
addr = *ppos;
nand_get_device(mtd, FL_WRITING);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
if ((*ppos + count) > CONFIG_ENV_SIZE)
write_size = CONFIG_ENV_SIZE - *ppos;
@@ -264,7 +280,8 @@ ssize_t uboot_env_write(struct file *file,
*ppos += write_size;
exit:
mutex_unlock(&env_mutex);
chip->select_chip(mtd, -1);
nand_release_device(mtd);
vfree(env_ptr);
return write_size;
}

View File

@@ -883,6 +883,9 @@ int boot_device_register(struct aml_nand_chip *aml_chip);
int add_mtd_partitions(struct mtd_info *mtd,
const struct mtd_partition *part, int num);
int nand_get_device(struct mtd_info *mtd, int new_state);
void nand_release_device(struct mtd_info *mtd);
#ifdef AML_NAND_UBOOT
extern int get_partition_from_dts(unsigned char *buffer);
#endif

View File

@@ -17,7 +17,6 @@
#include "aml_mtd.h"
uint8_t nand_boot_flag;
/*#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 13)*/
@@ -1913,9 +1912,10 @@ int aml_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *chip = mtd->priv;
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct mtd_oob_ops aml_oob_ops;
int blk_addr, mtd_erase_shift;
int8_t *buf = NULL;
int page, chipnr;
int ret = 0;
if (!mtd->erasesize)
return -EINVAL;
@@ -1935,19 +1935,18 @@ int aml_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
}
mark_bad:
/*no erase here, fixit*/
aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
aml_oob_ops.len = mtd->writesize;
aml_oob_ops.ooblen = mtd->oobavail;
/*aml_oob_ops.ooboffs = chip->ecc.layout->oobfree[0].offset;*/
aml_oob_ops.ooboffs = 0;
aml_oob_ops.datbuf = chip->buffers->databuf;
aml_oob_ops.oobbuf = chip->oob_poi;
chip->pagebuf = -1;
memset((unsigned char *)aml_oob_ops.datbuf, 0x0, mtd->writesize);
memset((unsigned char *)aml_oob_ops.oobbuf, 0x0, aml_oob_ops.ooblen);
return mtd->_write_oob(mtd, ofs, &aml_oob_ops);
memset((unsigned char *)chip->buffers->databuf, 0x0, mtd->writesize);
memset((unsigned char *)chip->oob_poi, 0x0, mtd->oobavail);
chipnr = (int)(ofs >> chip->chip_shift);
page = (int)(ofs >> chip->page_shift);
chip->select_chip(mtd, chipnr);
ret = chip->write_page(mtd, chip, 0, mtd->writesize,
chip->buffers->databuf,
1, page, 0, 0);
chip->select_chip(mtd, -1);
return ret;
}
static uint8_t aml_platform_read_byte(struct mtd_info *mtd)
@@ -2195,3 +2194,4 @@ exit_error:
aml_chip->block_status = NULL;
return err;
}

View File

@@ -418,9 +418,10 @@ int aml_nand_read_rsv_info(struct mtd_info *mtd,
loff_t addr = 0;
size_t amount_loaded = 0;
size_t len;
struct mtd_oob_ops aml_oob_ops;
unsigned char *data_buf;
unsigned char oob_buf[sizeof(struct oobinfo_t)];
int page, realpage, chipnr;
struct nand_chip *chip = mtd_to_nand(mtd);
READ_RSV_AGAIN:
addr = nandrsv_info->valid_node->phy_blk_addr;
@@ -433,20 +434,19 @@ READ_RSV_AGAIN:
oobinfo = (struct oobinfo_t *)oob_buf;
while (amount_loaded < nandrsv_info->size) {
aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
aml_oob_ops.len = mtd->writesize;
aml_oob_ops.ooblen = sizeof(struct oobinfo_t);
/*aml_oob_ops.ooboffs = mtd->ecclayout->oobfree[0].offset;*/
aml_oob_ops.ooboffs = 0;
aml_oob_ops.datbuf = data_buf;
aml_oob_ops.oobbuf = oob_buf;
memset((unsigned char *)aml_oob_ops.datbuf,
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
memset((unsigned char *)aml_oob_ops.oobbuf,
0x0, aml_oob_ops.ooblen);
memset((unsigned char *)oob_buf,
0x0, sizeof(struct oobinfo_t));
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
realpage = (int)(addr >> chip->page_shift);
page = realpage & chip->pagemask;
error = mtd->_read_oob(mtd, addr, &aml_oob_ops);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
error = chip->ecc.read_page(mtd, chip, data_buf,
1, page);
chip->select_chip(mtd, -1);
if ((error != 0) && (error != -EUCLEAN)) {
pr_info("blk good but read failed: %llx, %d\n",
(uint64_t)addr, error);
@@ -456,11 +456,12 @@ READ_RSV_AGAIN:
goto READ_RSV_AGAIN;
}
memcpy(oob_buf, chip->oob_poi, mtd->oobavail);
if (memcmp(oobinfo->name, nandrsv_info->name, 4))
pr_info("invalid nand info %s magic: %llx\n",
nandrsv_info->name, (uint64_t)addr);
addr += mtd->writesize;
page++;
len = min_t(uint32_t, mtd->writesize,
(nandrsv_info->size - amount_loaded));
memcpy(buf + amount_loaded, data_buf, len);
@@ -487,8 +488,6 @@ READ_RSV_AGAIN:
}
}
#endif
/* if(data_buf) */
/* kfree(data_buf); */
return 0;
}
@@ -544,18 +543,17 @@ static int aml_nand_write_rsv(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info, loff_t offset, u_char *buf)
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
struct oobinfo_t *oobinfo;
int error = 0;
loff_t addr = 0;
size_t amount_saved = 0;
size_t len;
struct mtd_oob_ops aml_oob_ops;
unsigned char *data_buf;
unsigned char oob_buf[sizeof(struct oobinfo_t)];
int page, chipnr;
data_buf = aml_chip->rsv_data_buf;
/* if (data_buf == NULL) */
/* return -ENOMEM; */
addr = offset;
pr_info("%s:%d,write info to %llx\n", __func__, __LINE__, addr);
@@ -584,21 +582,21 @@ static int aml_nand_write_rsv(struct mtd_info *mtd,
}
#endif
while (amount_saved < nandrsv_info->size) {
aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
aml_oob_ops.len = mtd->writesize;
aml_oob_ops.ooblen = sizeof(struct oobinfo_t);
aml_oob_ops.ooboffs = 0;/*mtd->ecclayout->oobfree[0].offset;*/
aml_oob_ops.datbuf = data_buf;
aml_oob_ops.oobbuf = oob_buf;
memset((unsigned char *)aml_oob_ops.datbuf,
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
len = min_t(uint32_t, mtd->writesize,
nandrsv_info->size - amount_saved);
memcpy((unsigned char *)aml_oob_ops.datbuf,
memcpy((unsigned char *)data_buf,
buf + amount_saved, len);
error = mtd->_write_oob(mtd, addr, &aml_oob_ops);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
memset(chip->oob_poi, 0xff, mtd->oobsize);
memcpy(chip->oob_poi, oob_buf, mtd->oobsize);
chip->select_chip(mtd, chipnr);
error = chip->write_page(mtd, chip, 0, len, data_buf,
1, page, 0, 0);
chip->select_chip(mtd, -1);
if (error) {
pr_info("blk check good but write failed: %llx, %d\n",
(uint64_t)addr, error);
@@ -609,17 +607,19 @@ static int aml_nand_write_rsv(struct mtd_info *mtd,
}
if (amount_saved < nandrsv_info->size)
return 1;
/* kfree(data_buf); */
return 0;
}
int aml_nand_save_rsv_info(struct mtd_info *mtd,
struct aml_nandrsv_info_t *nandrsv_info, u_char *buf)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct free_node_t *free_node = NULL, *tmp_node = NULL;
int error = 0, pages_per_blk, valid_page_addr, i = 1;
loff_t addr = 0;
struct erase_info erase_info;
int chipnr = 0;
int page;
pages_per_blk = mtd->erasesize / mtd->writesize;
/*solve these abnormals caused by power off and ecc error*/
@@ -639,13 +639,12 @@ RE_SEARCH:
if ((valid_page_addr - i) == pages_per_blk) {
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
memset(&erase_info,
0, sizeof(struct erase_info));
erase_info.mtd = mtd;
erase_info.addr = addr;
erase_info.len = mtd->erasesize;
_aml_rsv_disprotect();
error = mtd->_erase(mtd, &erase_info);
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
error = chip->erase(mtd, page & chip->pagemask);
chip->select_chip(mtd, -1);
_aml_rsv_protect();
nandrsv_info->valid_node->ec++;
pr_info("---erase bad env block:%llx\n", addr);
@@ -691,7 +690,10 @@ RE_SEARCH:
pr_info("%s:%d,save info to %llx\n", __func__, __LINE__, addr);
if (nandrsv_info->valid_node->phy_page_addr == 0) {
error = mtd->_block_isbad(mtd, addr);
chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
error = chip->block_bad(mtd, addr);
chip->select_chip(mtd, -1);
if (error != 0) {
/*
*bad block here, need fix it
@@ -706,16 +708,18 @@ RE_SEARCH:
goto RE_SEARCH;
}
memset(&erase_info, 0, sizeof(struct erase_info));
erase_info.mtd = mtd;
erase_info.addr = addr;
erase_info.len = mtd->erasesize;
_aml_rsv_disprotect();
error = mtd->_erase(mtd, &erase_info);
page = (int)(addr >> chip->page_shift);
//chipnr = (int)(addr >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
error = chip->erase(mtd, page & chip->pagemask);
chip->select_chip(mtd, -1);
_aml_rsv_protect();
if (error) {
pr_info("env free blk erase failed %d\n", error);
mtd->_block_markbad(mtd, addr);
chip->select_chip(mtd, chipnr);
chip->block_markbad(mtd, addr);
chip->select_chip(mtd, -1);
return error;
}
nandrsv_info->valid_node->ec++;
@@ -941,20 +945,21 @@ int aml_nand_free_rsv_info(struct mtd_info *mtd,
struct free_node_t *tmp_node, *next_node = NULL;
int error = 0;
loff_t addr = 0;
struct erase_info erase_info;
int page, chipnr;
struct nand_chip *chip = mtd_to_nand(mtd);
pr_info("free %s:\n", nandrsv_info->name);
if (nandrsv_info->valid) {
addr = nandrsv_info->valid_node->phy_blk_addr;
addr *= mtd->erasesize;
memset(&erase_info,
0, sizeof(struct erase_info));
erase_info.mtd = mtd;
erase_info.addr = addr;
erase_info.len = mtd->erasesize;
page = (int)(addr >> chip->page_shift);
chipnr = (int)(addr >> chip->chip_shift);
_aml_rsv_disprotect();
error = mtd->_erase(mtd, &erase_info);
chip->select_chip(mtd, chipnr);
error = chip->erase(mtd, page & chip->pagemask);
chip->select_chip(mtd, -1);
_aml_rsv_protect();
pr_info("erasing valid info block: %llx\n", addr);
nandrsv_info->valid_node->phy_blk_addr = -1;
@@ -980,7 +985,6 @@ int aml_nand_scan_rsv_info(struct mtd_info *mtd,
{
struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd);
struct nand_chip *chip = &aml_chip->chip;
struct mtd_oob_ops aml_oob_ops;
struct oobinfo_t *oobinfo;
struct free_node_t *free_node, *tmp_node = NULL;
unsigned char oob_buf[sizeof(struct oobinfo_t)];
@@ -990,6 +994,7 @@ int aml_nand_scan_rsv_info(struct mtd_info *mtd,
int phys_erase_shift, pages_per_blk, page_num;
int error = 0, ret = 0;
uint32_t remainder;
int page, realpage, chipnr;
data_buf = aml_chip->rsv_data_buf;
oobinfo = (struct oobinfo_t *)oob_buf;
@@ -1007,19 +1012,20 @@ RE_RSV_INFO_EXT:
offset *= start_blk;
scan_status = 0;
RE_RSV_INFO:
aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
aml_oob_ops.len = mtd->writesize;
aml_oob_ops.ooblen = sizeof(struct oobinfo_t);
aml_oob_ops.ooboffs = 0;/*mtd->ecclayout->oobfree[0].offset;*/
aml_oob_ops.datbuf = data_buf;
aml_oob_ops.oobbuf = oob_buf;
memset((unsigned char *)aml_oob_ops.datbuf,
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
memset((unsigned char *)aml_oob_ops.oobbuf,
0x0, aml_oob_ops.ooblen);
memset((unsigned char *)oob_buf,
0x0, sizeof(struct oobinfo_t));
error = mtd->_read_oob(mtd, offset, &aml_oob_ops);
realpage = (int)(offset >> chip->page_shift);
page = realpage & chip->pagemask;
chipnr = (int)(offset >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
error = chip->ecc.read_page(mtd, chip, data_buf,
1, page);
chip->select_chip(mtd, -1);
if ((error != 0) && (error != -EUCLEAN)) {
pr_info("blk check good but read failed: %llx, %d\n",
(uint64_t)offset, error);
@@ -1033,6 +1039,7 @@ RE_RSV_INFO:
goto RE_RSV_INFO;
}
memcpy(oob_buf, chip->oob_poi, mtd->oobavail);
nandrsv_info->init = 1;
nandrsv_info->valid_node->status = 0;
if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) {
@@ -1121,23 +1128,25 @@ RE_RSV_INFO:
if (nandrsv_info->valid == 1) {
pr_info("%s %d\n", __func__, __LINE__);
aml_oob_ops.mode = MTD_OPS_AUTO_OOB;
aml_oob_ops.len = mtd->writesize;
aml_oob_ops.ooblen = sizeof(struct oobinfo_t);
aml_oob_ops.ooboffs = 0;/*mtd->ecclayout->oobfree[0].offset;*/
aml_oob_ops.datbuf = data_buf;
aml_oob_ops.oobbuf = oob_buf;
for (i = 0; i < pages_per_blk; i++) {
memset((unsigned char *)aml_oob_ops.datbuf,
memset((unsigned char *)data_buf,
0x0, mtd->writesize);
memset((unsigned char *)aml_oob_ops.oobbuf,
0x0, aml_oob_ops.ooblen);
memset((unsigned char *)oob_buf,
0x0, sizeof(struct oobinfo_t));
offset = nandrsv_info->valid_node->phy_blk_addr;
offset *= mtd->erasesize;
offset += i * mtd->writesize;
error = mtd->_read_oob(mtd, offset, &aml_oob_ops);
realpage = (int)(offset >> chip->page_shift);
page = realpage & chip->pagemask;
chipnr = (int)(offset >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
error = chip->ecc.read_page(mtd, chip, data_buf,
1, page);
chip->select_chip(mtd, -1);
if ((error != 0) && (error != -EUCLEAN)) {
pr_info("blk good but read failed:%llx,%d\n",
(uint64_t)offset, error);
@@ -1146,6 +1155,7 @@ RE_RSV_INFO:
continue;
}
memcpy(oob_buf, chip->oob_poi, mtd->oobavail);
if (!memcmp(oobinfo->name, nandrsv_info->name, 4)) {
good_addr[i] = 1;
nandrsv_info->valid_node->phy_page_addr = i;