nand_cet.c
来自「This is about correction error table in 」· C语言 代码 · 共 967 行 · 第 1/2 页
C
967 行
if (count == 0) {
/* The global cerr_count is in the 2nd page's OOB area */
from += this->pageSize;
if (nand_cet_read_oob(mtd, oobbuf, from)) {
printk(KERN_ERR "nandCET: %s %d Error reading OOB\n", __FUNCTION__, __LINE__);
cet->flags = NAND_CET_DISABLED;
return -1;
}
cet->cerr_count = *((uint32_t *) (oobbuf + cet->offs));
/* TODO - Fix this -> recreate */
if (cet->cerr_count == 0xffffffff) {
/* Reset it to 0 */
cet->cerr_count = 0;
cet->memtbl[0].isdirty = 1;
}
if (unlikely(gdebug)) {
printk(KERN_INFO "nandCET: correctable error count = %x\n", cet->cerr_count);
}
/* If force then go thru all CET blks even if cerr_count is 0 */
if (!force) {
if (cet->cerr_count == 0) {
cet->flags = NAND_CET_LAZY;
return 0;
}
}
}
cet->memtbl[ret].blk = cet->startblk + i*cet->sign;
count++;
#if 0
printk(KERN_INFO "DEBUG -> count = %d, nblks = %d blk = %d\n", count, cet->numblks, cet->memtbl[ret].blk);
#endif
if (count == cet->numblks) {
cet->flags = NAND_CET_LOADED;
return 0;
}
}
/* This should never happen */
cet->flags = NAND_CET_DISABLED;
return -1;
}
/*
* flush pending in-memory CET data to the flash. Called as part of a
* callback function from workqueue that is invoked every SYNC_FREQ seconds
*/
static int flush_memcet(struct mtd_info *mtd)
{
struct nand_chip *this = (struct nand_chip *) mtd->priv;
struct nand_cet_descr *cet = this->cet;
struct erase_info einfo;
int i, j, k = 0, ret, pg_idx = 0, gdebug = 0;
uint8_t oobbuf[mtd->oobsize];
loff_t from, to;
char *oobptr, count = 0;
/* If chip is locked reset timer for a later time */
if (spin_is_locked(&this->chip_lock)) {
printk(KERN_INFO "DEBUG -> nandCET: flash locked reseting timer\n");
return -1;
}
if (unlikely(gdebug)) {
printk(KERN_INFO "nandCET: Inside %s\n", __FUNCTION__);
}
/* For each in-mem dirty block, sync with flash
sync => erase -> write */
for (i = 0; i < cet->numblks; i++) {
if (cet->memtbl[i].isdirty && cet->memtbl[i].blk != -1) {
/* Erase */
from = __ll_LeftShift32(cet->memtbl[i].blk, this->bbt_erase_shift);
to = from;
memset(&einfo, 0, sizeof(einfo));
einfo.mtd = mtd;
einfo.addr = __ll_low(from);
einfo.len = mtd->erasesize;
ret = this->erase_bbt(mtd, &einfo, 1, 1);
if (unlikely(ret < 0)) {
printk(KERN_ERR "nandCET: %s Error erasing block %x\n", __FUNCTION__, cet->memtbl[i].blk);
return -1;
}
if (unlikely(gdebug)) {
printk(KERN_INFO "DEBUG -> nandCET: After erasing ...\n");
cet_printpg_oob(mtd, cet, 3);
}
pg_idx = 0;
/* Write pages i.e., flush */
for (j = 0; j < mtd->erasesize/this->pageSize; j++) {
memset(oobbuf, 0xff, mtd->oobsize);
oobptr = (char *) oobbuf;
if (j == 0) { /* Write CET# */
for (k = 0; k < cet->len-1; k++) {
oobptr[cet->offs + k] = cet->pattern[k];
}
oobptr[cet->offs + k] = count;
if (unlikely(gdebug)) {
print_oobbuf((const char *) oobbuf, mtd->oobsize);
}
}
if (j == 1 && count == 0) { /* Write cerr_count */
*((uint32_t *) (oobptr + cet->offs)) = cet->cerr_count;
}
ret = nand_cet_write(mtd, to, (size_t) this->pageSize, cet->memtbl[i].bitvec+pg_idx, oobbuf);
if (ret < 0) {
printk(KERN_ERR "nandCET: %s Error writing to page %x\n", __FUNCTION__, (unsigned int) to);
return ret;
}
to += mtd->writesize;
pg_idx += mtd->writesize;
}
cet->memtbl[i].isdirty = 0;
if (unlikely(gdebug)) {
printk(KERN_INFO "nandCET: flushing CET block %d\n", i);
}
}
count++;
}
return 0;
}
/*
* The callback function for kernel workq task
* Checks if there is any work to be done, if so calls flush_memcet
* Resets timer before returning in any case
*/
static void sync_cet(void *ptr_mtd)
{
int i;
struct mtd_info *mtd = (struct mtd_info *) ptr_mtd;
struct nand_chip *this = (struct nand_chip *) mtd->priv;
struct nand_cet_descr *cet = this->cet;
/* Check if all blocks are clean */
for (i = 0; i < cet->numblks; i++) {
if (cet->memtbl[i].isdirty) break;
}
/* Avoid function call cost if there are no dirty blocks */
if (i != cet->numblks)
flush_memcet(mtd);
schedule_delayed_work(&cet->cet_flush, CET_SYNC_FREQ);
return;
}
/*
* nand_create_cet - Create a CET (Correctable Error Table)
* @param mtd MTD device structure
*
* Called during mtd init. Checks if a CET already exists or needs
* to be created. Initializes in-memory CET.
*/
int nand_create_cet(struct mtd_info *mtd)
{
struct nand_chip *this = (struct nand_chip *) mtd->priv;
struct nand_cet_descr *cet;
int gdebug = 0, i, ret;
if (unlikely(gdebug)) {
printk(KERN_INFO "nandCET: Creating correctable error table ...\n");
}
this->cet = cet = &cet_descr;
cet->flags = 0x00;
if (NAND_IS_MLC(this)) {
printk(KERN_INFO "Disabling CET for MLC NAND\n");
cet->flags = NAND_CET_DISABLED;
return -1;
}
/* Check that BBT table and mirror exist */
if (unlikely(!this->bbt_td && !this->bbt_md)) {
printk(KERN_INFO "nandCET: BBT tables not found, disabling\n");
cet->flags = NAND_CET_DISABLED;
return -1;
}
/* Per chip not supported. We do not use per chip BBT, but this
is just a safety net */
if (unlikely(this->bbt_td->options & NAND_BBT_PERCHIP)) {
printk(KERN_INFO "nandCET: per chip CET not supported, disabling\n");
cet->flags = NAND_CET_DISABLED;
return -1;
}
/* Calculate max blocks based on 1-bit per page */
cet->numblks = (this->mtdSize/this->pageSize)/(8*this->blockSize);
if (((this->mtdSize/this->pageSize)/8)%this->blockSize) {
cet->numblks++;
}
/* Allocate twice the size in case we have bad blocks */
cet->maxblks = cet->numblks*2;
/* Determine the direction of CET based on reverse direction of BBT */
cet->sign = (this->bbt_td->options & NAND_BBT_LASTBLOCK) ? 1 : -1;
/* For flash size <= 512MB BBT and CET share the last 1MB
for flash size > 512MB CET is at the 512th MB of flash */
if (this->mtdSize < (1<<29)) {
if (cet->maxblks + BBT_MAX_BLKS > BBT_PARTITION/this->blockSize) {
printk(KERN_INFO "nandCET: Not enough space to store CET, disabling CET\n");
cet->flags = NAND_CET_DISABLED;
return -1;
}
if (cet->sign) {
cet->startblk = CET_START_BLK(this->mtdSize, this)
} else {
cet->startblk = __ll_low(__ll_RightShift(this->mtdSize, this->bbt_erase_shift)-1);
}
} else {
if (cet->maxblks > (BBT_PARTITION)/this->blockSize) {
printk(KERN_INFO "nandCET: Not enough space to store CET, disabling CET\n");
cet->flags = NAND_CET_DISABLED;
return -1;
}
cet->startblk = CET_START_BLK((1<<29), this)
}
if (gdebug) {
printk(KERN_INFO "nandCET: start blk = %x, numblks = %x\n", cet->startblk, cet->numblks);
}
/* Init memory based CET */
cet->memtbl = (struct nand_cet_memtable *) vmalloc(cet->numblks*sizeof(struct nand_cet_memtable));
if (cet->memtbl == NULL) {
printk(KERN_ERR "nandCET: vmalloc failed %s\n", __FUNCTION__);
cet->flags = NAND_CET_DISABLED;
return -1;
}
for (i = 0; i < cet->numblks; i++) {
cet->memtbl[i].isdirty = 0;
cet->memtbl[i].blk = -1;
cet->memtbl[i].bitvec = NULL;
}
ret = search_cet_blks(mtd, cet, 0);
if (unlikely(gClearCET == 1)) { /* kernel cmdline showcet */
cmdline_showcet(mtd, cet);
}
if (unlikely(gClearCET == 2)) { /* kernel cmdline resetcet */
if (cmdline_resetcet(mtd, cet) < 0) {
cet->flags = NAND_CET_DISABLED;
return -1;
}
}
if (unlikely(gClearCET == 3)) { /* kernel cmdline disable */
cet->flags = NAND_CET_DISABLED;
ret = -1;
}
//cet_printpg_oob(mtd, cet, 3);
switch(cet->flags) {
case NAND_CET_DISABLED:
printk(KERN_INFO "nandCET: Status -> Disabled\n");
break;
case NAND_CET_LAZY:
printk(KERN_INFO "nandCET: Status -> Deferred\n");
break;
case NAND_CET_LOADED:
printk(KERN_INFO "nandCET: Status -> Loaded\n");
break;
default:
printk(KERN_INFO "nandCET: Status -> Fatal error CET disabled\n");
cet->flags = NAND_CET_DISABLED;
break;
}
if (unlikely(gdebug)) {
cet_printpg_oob(mtd, cet, 3);
cet_printblk_oob(mtd, cet);
}
INIT_WORK(&cet->cet_flush, sync_cet, (void *) mtd);
schedule_delayed_work(&cet->cet_flush, CET_SYNC_FREQ);
return ret;
}
/*
* nand_cet_erasecallback: Called every time there is an erase due to
* userspace activity
*
* @param mtd MTD device structure
* @param addr Address of the block that was erased by fs/userspace
*
* Assumption: cet->flag != NAND_CET_DISABLED || NAND_CET_LAZY
* is checked by the caller
* flag == NAND_CET_DISABLED => CET not being used
* flag == NAND_CET_LAZY => correctable error count is 0 so need of callback
*
* TODO Optimize, add comments, check all return paths
*/
int nand_cet_erasecallback(struct mtd_info *mtd, u_int32_t addr)
{
struct nand_chip *this = (struct nand_chip *) mtd->priv;
struct nand_cet_descr *cet = this->cet;
uint32_t page = 0;
int blkbegin, blk, i, ret, retlen, pg_idx = 0, numzeros = 0, byte, gdebug = 0;
uint32_t *ptr;
unsigned int pos;
loff_t origaddr = addr;
/* Find out which entry in the memtbl does the addr map to */
page = __ll_RightShift(addr, this->page_shift);
blk = page/(this->blockSize<<3);
if (unlikely(cet->memtbl[blk].blk == -1)) {
printk(KERN_INFO "nandCET: %s invalid block# in CET\n", __FUNCTION__);
return -1;
}
blkbegin = cet->memtbl[blk].blk;
/* Start page of the block */
addr = __ll_LeftShift32(blkbegin, this->bbt_erase_shift);
if (cet->memtbl[blk].bitvec == NULL) {
if (gdebug) {
printk(KERN_INFO "DEBUG -> nandCET: bitvec is null, reloading\n");
}
cet->memtbl[blk].bitvec = (char *) vmalloc(this->blockSize);
if (cet->memtbl[blk].bitvec == NULL) {
printk(KERN_INFO "nandCET: %s vmalloc failed\n", __FUNCTION__);
return -1;
}
memset(cet->memtbl[blk].bitvec, 0xff, sizeof(this->blockSize));
/* Read an entire block */
for (i = 0; i < mtd->erasesize/mtd->writesize; i++) {
if (gdebug) {
printk(KERN_INFO "DEBUG -> nandCET: Reading page %d\n", i);
}
ret = mtd->read(mtd, addr, this->pageSize, &retlen, (uint8_t *) (cet->memtbl[blk].bitvec+pg_idx));
if (ret < 0 || (retlen != this->pageSize)) {
vfree(cet->memtbl[blk].bitvec);
return -1;
}
pg_idx += mtd->writesize;
addr += this->pageSize;
}
}
page = (uint32_t) ((origaddr & (~(mtd->erasesize-1))) >> this->page_shift);
pos = page % (this->blockSize<<3);
byte = pos / (1<<3);
ptr = (uint32_t *) ((char *)cet->memtbl[blk].bitvec+byte);
/* numpages/8bits per byte/4byte per uint32 */
for (i = 0; i < ((mtd->erasesize/mtd->writesize)>>3)>>2; i++) {
/* Count the number of 0s for in the bitvec */
numzeros += bitcount(~ptr[i]);
}
if (likely(numzeros == 0)) {
if (gdebug) {
printk(KERN_INFO "DEBUG -> nandCET: returning 0 numzeros = 0\n");
}
return 0;
}
if (cet->cerr_count < numzeros) {
if (gdebug) {
printk(KERN_ERR "nandCET: Erroneous correctable error count");
}
return -1;
}
cet->cerr_count -= numzeros;
/* Make bits corresponding to this block all 1s */
memset(cet->memtbl[blk].bitvec+byte, 0xff, (mtd->erasesize/mtd->writesize)>>3);
cet->memtbl[blk].isdirty = 1;
return 0;
}
/*
* nand_cet_update: Called every time a single correctable error is
* encountered.
* @param mtd MTD device structure
* @param from Page address at which correctable error occured
* @param status Return status
* 1 => This page had a correctable errror in past,
* therefore, return correctable error to filesystem
* 0 => First occurence of a correctable error for
* this page. return a success to the filesystem
*
* Check the in memory CET bitvector to see if this page (loff_t from)
* had a correctable error in past, if not set this page's bit to '0'
* in the bitvector.
*
*/
int nand_cet_update(struct mtd_info *mtd, loff_t from, int *status)
{
struct nand_chip *this = (struct nand_chip *) mtd->priv;
struct nand_cet_descr *cet = this->cet;
int gdebug = 0, ret, blk, byte, bit, retlen = 0, blkbegin, i;
uint32_t page = 0;
unsigned int pg_idx = 0, pos = 0;
unsigned char c, mask;
if (gdebug) {
printk(KERN_INFO "DEBUG -> nandCET: Inside %s\n", __FUNCTION__);
}
if (cet->flags == NAND_CET_LAZY) {
/* Force creation of the CET and the mem table */
ret = search_cet_blks(mtd, cet, 1);
if (ret < 0) {
cet->flags = NAND_CET_DISABLED;
return ret;
}
cet->flags = NAND_CET_LOADED;
}
/* Find out which entry in memtbl does the from address map to */
page = __ll_RightShift(from, this->page_shift);
/* each bit is one page << 3 for 8 bits per byte */
blk = page/(this->blockSize<<3);
if (unlikely(cet->memtbl[blk].blk == -1)) {
printk(KERN_INFO "nandCET: %s invalid block# in CET\n", __FUNCTION__);
return -1;
}
blkbegin = cet->memtbl[blk].blk;
/* Start page of the block */
from = __ll_LeftShift32(blkbegin, this->bbt_erase_shift);
/* If bitvec == NULL, load the block from flash */
if (cet->memtbl[blk].bitvec == NULL) {
if (gdebug) {
printk(KERN_INFO "DEBUG -> nandCET: bitvec null .... loading ...\n");
}
cet->memtbl[blk].bitvec = (char *) vmalloc(this->blockSize);
if (cet->memtbl[blk].bitvec == NULL) {
printk(KERN_ERR "nandCET: %s vmalloc failed\n", __FUNCTION__);
return -1;
}
memset(cet->memtbl[blk].bitvec, 0xff, this->blockSize);
/* Read an entire block */
if (gdebug) {
printk(KERN_INFO "DEBUG -> nandCET: Reading pages starting @ %x\n", (unsigned int) from);
}
for (i = 0; i < mtd->erasesize/mtd->writesize; i++) {
ret = mtd->read(mtd, from, this->pageSize, &retlen, (uint8_t *) (cet->memtbl[blk].bitvec+pg_idx));
if (ret < 0 || (retlen != this->pageSize)) {
vfree(cet->memtbl[blk].bitvec);
return -1;
}
pg_idx += mtd->writesize;
from += this->pageSize;
}
}
pos = page % (this->blockSize<<3);
byte = pos / (1<<3);
bit = pos % (1<<3);
c = cet->memtbl[blk].bitvec[byte];
mask = 1<<bit;
if ((c & mask) == mask) { /* First time error mark it but return a good status */
*status = 0;
c = (c & ~mask);
cet->memtbl[blk].bitvec[byte] = c;
cet->memtbl[blk].isdirty = 1;
} else {
*status = 1; /* This page had a previous error so return a bad status */
}
cet->cerr_count++;
#if 0
printk(KERN_INFO "DEBUG -> count = %d, byte = %d, bit = %d, blk = %x status = %d c = %d addr = %x\n", cet->cerr_count\
, byte, bit, blk, *status, cet->memtbl[blk].bitvec[byte], cet->memtbl[blk].bitvec+byte);
printk(KERN_INFO "DEBUG -> CET: Exiting %s\n", __FUNCTION__);
#endif
return 0;
}
/*
* nand_cet_prepare_reboot Call flush_memcet to flush any in-mem dirty data
*
* @param mtd MTD device structure
*
* Flush any pending in-mem CET blocks to flash before reboot
*/
int nand_cet_prepare_reboot(struct mtd_info *mtd)
{
int gdebug = 1;
struct nand_chip *this = (struct nand_chip *) mtd->priv;
struct nand_cet_descr *cet = this->cet;
// Disable for MLC
if (NAND_IS_MLC(this)) {
return 0;
}
if (unlikely(gdebug)) {
uart_puts(KERN_INFO "DEBUG -> nandCET: flushing pending CET\n");
}
if (unlikely(cet->flags == NAND_CET_DISABLED)) {
return 0;
}
flush_memcet(mtd);
return 0;
}
EXPORT_SYMBOL(nand_cet_update);
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?