Write-while-program support
PATCH 1/1 Write-while-program support
This is from Adrian hunter and modified for write_ops operation.
It's similar with load-while-read method. but it's write side performance improvement.
Here's brief performance results.
Note: Measured from OMAP3 with async OneNAND mode. (not sync mode)
=================================================================
speedtest: dev = 3
speedtest: Size=16777216 EB size=131072 Write size=2048 EB count=128
Pages per EB=64 Page size=2048
speedtest: scanning for bad blocks
speedtest: scanned 0
speedtest: scanned 128, found 0 bad
speedtest: erasing
speedtest: erased 0
speedtest: erased 128
speedtest: Testing eraseblock write speed eraseblock write speed is 2424 KiB/s
speedtest: Testing eraseblock read speed eraseblock read speed is 11985 KiB/s
speedtest: Testing page write speed page write speed is 2493 KiB/s
speedtest: Testing page read speed page read speed is 11586 KiB/s
speedtest: Testing 2 page write speed 2 page write speed is 2681 KiB/s
speedtest: Testing 2 page read speed 2 page read speed is 10922 KiB/s
speedtest: Testing erase speed erase speed is 192752 KiB/s
speedtest: speedtest finished
=================================================================
Also it's passed all nand-tests.
---
drivers/mtd/onenand/onenand_base.c +64 -49
@@ -1455,7 +1455,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) {struct onenand_chip *this = mtd->priv; -int written =0 , column, thislen, subpage; +int written =0 , column, thislen =0 , subpage =0 ; +int prev =0 , prevlen =0 , prev_subpage =0 , first =1 ;int oobwritten =0 , oobcolumn, thisooblen, oobsize;size_t len = ops->len;size_t ooblen = ops->ooblen;
@@ -1492,76 +1493,90 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, column = to & (mtd->writesize -1 );/* Loop until all data write */ -while (written < len) { -u_char *wbuf = (u_char *) buf; +while (1 ) { +if (written < len) { +u_char *wbuf = (u_char *) buf; - thislen =min_t (int, mtd->writesize - column, len - written); - thisooblen =min_t (int, oobsize - oobcolumn, ooblen - oobwritten); + thislen =min_t (int, mtd->writesize - column, len - written); + thisooblen =min_t (int, oobsize - oobcolumn, ooblen - oobwritten); -cond_resched (); +cond_resched (); - this->command (mtd,ONENAND_CMD_BUFFERRAM , to, thislen); + this->command (mtd,ONENAND_CMD_BUFFERRAM , to, thislen); -/* Partial page write */ - subpage = thislen < mtd->writesize; -if (subpage) { -memset (this->page_buf,0xff , mtd->writesize); -memcpy (this->page_buf + column, buf, thislen); - wbuf = this->page_buf; - } +/* Partial page write */ + subpage = thislen < mtd->writesize; +if (subpage) { +memset (this->page_buf,0xff , mtd->writesize); +memcpy (this->page_buf + column, buf, thislen); + wbuf = this->page_buf; + } - this->write_bufferram (mtd,ONENAND_DATARAM , wbuf,0 , mtd->writesize); + this->write_bufferram (mtd,ONENAND_DATARAM , wbuf,0 , mtd->writesize); -if (oob) { - oobbuf = this->oob_buf; +if (oob) { + oobbuf = this->oob_buf; -/* We send data to spare ram with oobsize -* to prevent byte access */ -memset (oobbuf,0xff , mtd->oobsize); -if (ops->mode ==MTD_OOB_AUTO ) -onenand_fill_auto_oob (mtd, oobbuf, oob, oobcolumn, thisooblen); -else -memcpy (oobbuf + oobcolumn, oob, thisooblen); +/* We send data to spare ram with oobsize +* to prevent byte access */ +memset (oobbuf,0xff , mtd->oobsize); +if (ops->mode ==MTD_OOB_AUTO ) +onenand_fill_auto_oob (mtd, oobbuf, oob, oobcolumn, thisooblen); +else +memcpy (oobbuf + oobcolumn, oob, thisooblen); - oobwritten += thisooblen; - oob += thisooblen; - oobcolumn =0 ; + oobwritten += thisooblen; + oob += thisooblen; + oobcolumn =0 ; + }else + oobbuf = (u_char *) ffchars; + + this->write_bufferram (mtd,ONENAND_SPARERAM , oobbuf,0 , mtd->oobsize); }else - oobbuf = (u_char *) ffchars; +ONENAND_SET_NEXT_BUFFERRAM (this); - this->write_bufferram (mtd,ONENAND_SPARERAM , oobbuf,0 , mtd->oobsize); +if (!first) { +ONENAND_SET_PREV_BUFFERRAM (this); - this->command (mtd,ONENAND_CMD_PROG , to, mtd->writesize); + ret = this->wait (mtd,FL_WRITING ); - ret = this->wait (mtd,FL_WRITING ); +/* In partial page write we don't update bufferram */ +onenand_update_bufferram (mtd, prev, !ret && !prev_subpage); +if (ONENAND_IS_2PLANE (this)) { +ONENAND_SET_BUFFERRAM1 (this); +onenand_update_bufferram (mtd, prev + this->writesize, !ret && !prev_subpage); + } -/* In partial page write we don't update bufferram */ -onenand_update_bufferram (mtd, to, !ret && !subpage); -if (ONENAND_IS_2PLANE (this)) { -ONENAND_SET_BUFFERRAM1 (this); -onenand_update_bufferram (mtd, to + this->writesize, !ret && !subpage); - } +if (ret) { +printk (KERN_ERR "onenand_write_ops_nolock: write filaed %d\n" , ret); +break ; + } -if (ret) { -printk (KERN_ERR "onenand_write_ops_nolock: write filaed %d\n" , ret); -break ; - } +if (written == len) { +/* Only check verify write turn on */ + ret =onenand_verify (mtd, buf - len, to - len, len); +if (ret) { +printk (KERN_ERR "onenand_write_ops_nolock: verify failed %d\n" , ret); +break ; + } + } +ONENAND_SET_NEXT_BUFFERRAM (this); -/* Only check verify write turn on */ - ret =onenand_verify (mtd, buf, to, thislen); -if (ret) { -printk (KERN_ERR "onenand_write_ops_nolock: verify failed %d\n" , ret); -break ; +if (written == len) +break ; } - written += thislen; + this->command (mtd,ONENAND_CMD_PROG , to, mtd->writesize); -if (written == len) -break ; + written += thislen; column =0 ; + prev_subpage = subpage; + prev = to; + prevlen = thislen; to += thislen; buf += thislen; + first =0 ; } ops->retlen = written;
Quoted 1 line · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.
Thanks for looking at write-while-program. I have a few questions though. I have copied the whole function below because the diff is too confusing:
Quoted 31 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write main and/or out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param ops oob operation description structure
*
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
Quoted 9 lines
return -EINVAL;
}
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
return -EINVAL;
}The loop cannot handle the case when len is zero so I had:
if (!len) return 0;
Quoted 52 lines
if (ops->mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
oobsize = mtd->oobsize;
oobcolumn = to & (mtd->oobsize - 1);
column = to & (mtd->writesize - 1);
/* Loop until all data write */
while (1) {
if (written < len) {
u_char *wbuf = (u_char *) buf;
thislen = min_t(int, mtd->writesize - column, len - written);
thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
cond_resched();
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
/* Partial page write */
subpage = thislen < mtd->writesize;
if (subpage) {
memset(this->page_buf, 0xff, mtd->writesize);
memcpy(this->page_buf + column, buf, thislen);
wbuf = this->page_buf;
}
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
if (oob) {
oobbuf = this->oob_buf;
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
if (ops->mode == MTD_OOB_AUTO)
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob, thisooblen);
oobwritten += thisooblen;
oob += thisooblen;
oobcolumn = 0;
} else
oobbuf = (u_char *) ffchars;
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
} else
ONENAND_SET_NEXT_BUFFERRAM(this);Quoted 8 lines
if (!first) {
ONENAND_SET_PREV_BUFFERRAM(this);
ret = this->wait(mtd, FL_WRITING);
/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
if (ONENAND_IS_2PLANE(this)) {I do not understand how 2PLANE is compatible with write-while-program because 2PLANE uses both buffers so we cannot start transferring to the second buffer while the program is ongoing. Does it work?
Won't MLC and FlexOneNAND have that problem too?
Quoted 5 lines
ONENAND_SET_BUFFERRAM1(this);
onenand_update_bufferram(mtd, prev + this->writesize, !ret && !prev_subpage);
}
if (ret) {My original patch had "written -= prevlen" here.
Quoted 11 lines
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
break;
}
if (written == len) {
/* Only check verify write turn on */
ret = onenand_verify(mtd, buf - len, to - len, len);
if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
break;
}Why not just break here?
Quoted 13 lines
} ONENAND_SET_NEXT_BUFFERRAM(this); if (written == len) break; } this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); written += thislen; column = 0; prev_subpage = subpage;
Quoted 8 lines
prev = to; prevlen = thislen; to += thislen; buf += thislen; first = 0; } ops->retlen = written;
Is ops->oobretlen needed here also?
Quoted 3 lines
return ret; }
Hi Adrian
FlexOneNand read dual data ram buffer and so we can not use ----- Original Message -----
From: "Adrian Hunter" <[email protected]>
To: "Kyungmin Park" <[email protected]>
Cc: <[email protected]>
Sent: Friday, November 14, 2008 4:02 PM
Subject: Re: [PATCH][OneNAND] Write-while-program support
Quoted 129 lines
Quoted 2 lines · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.Thanks for looking at write-while-program. I have a few questions though.
I have copied the whole function below because the diff is too confusing:Quoted 38 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write
main and/or out-of-band
* @param mtd MTD device structure
* @param to offset to write to
* @param ops oob operation description structure *
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops)
{
structonenand_chip*this =mtd->priv;
int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0,prev_subpage= 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize;size_tlen =ops->len;size_tooblen =ops->ooblen;
constu_char*buf =ops->datbuf;
constu_char*oob =ops->oobbuf;u_char*oobbuf;
int ret = 0;DEBUG(MTD_DEBUG_LEVEL3, "
onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);/* Initialize retlen, in case of early exit */
ops->retlen= 0;ops->oobretlen= 0;/* Do not allow writes past end of device */
if (unlikely((to + len) > mtd->size)) {printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
return-EINVAL;
}Quoted 9 lines
/* Reject writes, which are not page aligned */
if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; }The loop cannot handle the case when len is zero so I had:
if (!len)
return 0;Quoted 60 lines
if (
ops->mode==MTD_OOB_AUTO)
oobsize =this->ecclayout->oobavail;
else
oobsize =mtd->oobsize;oobcolumn = to & (
mtd->oobsize- 1);column = to & (
mtd->writesize- 1);/* Loop until all data write */
while (1) {
if (written < len) {u_char*wbuf = (u_char*) buf;thislen =
min_t(int, mtd->writesize - column, len - written);
thisooblen =min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
cond_resched();
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);/* Partial page write */
subpage = thislen <mtd->writesize;
if (subpage) {memset(this->page_buf, 0xff, mtd->writesize);memcpy(this->page_buf + column, buf, thislen);
wbuf =this->page_buf;
}
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);if (oob) {
oobbuf =this->oob_buf;/* We send data to spare ram with oobsize
* to prevent byte access */memset(oobbuf, 0xff, mtd->oobsize);
if (ops->mode==MTD_OOB_AUTO)onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
elsememcpy(oobbuf + oobcolumn, oob, thisooblen);oobwritten += thisooblen;
oob += thisooblen;
oobcolumn = 0;
} else
oobbuf = (u_char*) ffchars;
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
} elseONENAND_SET_NEXT_BUFFERRAM(this);if (!first) {
ONENAND_SET_PREV_BUFFERRAM(this);Quoted 6 lines
ret =
this->wait(mtd, FL_WRITING);/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
if (ONENAND_IS_2PLANE(this)) {
Quoted 7 lines
I do not understand how 2PLANE is compatible with write-while-program because
2PLANE uses both buffers so we cannot start transferring to the second buffer
while the program is ongoing. Does it work?Won't MLC and FlexOneNAND have that problem too?
Amit : FlexOneNand use dual data ram buffer and so we can not use it and other point is for OneNand we have a plan to use dual buufer
because OneNand page size will increase to 4k.
Quoted 24 lines
Quoted 7 lines
ONENAND_SET_BUFFERRAM1(this);onenand_update_bufferram(mtd, prev + this->writesize, !ret && !prev_subpage);
}if (ret) {
My original patch had "written -= prevlen" here.
Quoted 5 lines
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
break;
}Quoted 8 lines
if (written == len) {
/* Only check verify write turn on */
ret =onenand_verify(mtd, buf - len, to - len, len); if (ret) {printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
break;
}
Quoted 2 lines
Why not just break here?
Amit : same as kyungmin comment.
Quoted 34 lines
Quoted 20 lines
}
ONENAND_SET_NEXT_BUFFERRAM(this);if (written == len)
break;
}
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);written += thislen;
column = 0;
prev_subpage= subpage;
prev = to;
prevlen = thislen;
to += thislen;
buf += thislen;
first = 0;
}Quoted 1 line
ops->retlen= written;Is
ops->oobretlenneeded here also?Quoted 3 lines
return ret;
}______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
HI,
Quoted 52 lines · Adrian Hunter
Quoted 2 lines · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.Thanks for looking at write-while-program. I have a few questions though. I have copied the whole function below because the diff is too confusing:
Quoted 35 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write main and/or
out-of-band* @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure*
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len= %i\n", (unsigned int) to, (int) len);
/* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL;Quoted 9 lines
} /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; }
Quoted 4 lines
The loop cannot handle the case when len is zero so I had:
if (!len) return 0;
Yes there's length check. I wonder is it possible to write with zero? Is it handle at driver level or upper level such as filesystem? But no problem to just add length check here at this time.
Quoted 73 lines
Quoted 65 lines
if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = to & (mtd->oobsize - 1); column = to & (mtd->writesize - 1); /* Loop until all data write */ while (1) { if (written < len) { u_char *wbuf = (u_char *) buf; thislen = min_t(int, mtd->writesize - column, len -written);
thisooblen = min_t(int, oobsize - oobcolumn, ooblen
- oobwritten);cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to,thislen);
/* Partial page write */ subpage = thislen < mtd->writesize; if (subpage) { memset(this->page_buf, 0xff, mtd->writesize); memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf,0,
mtd->writesize);if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf,oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob,
thisooblen);oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); } else ONENAND_SET_NEXT_BUFFERRAM(this); if (!first) { ONENAND_SET_PREV_BUFFERRAM(this);Quoted 7 lines
ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ONENAND_IS_2PLANE(this)) {
Quoted 7 lines
I do not understand how 2PLANE is compatible with write-while-program because
2PLANE uses both buffers so we cannot start transferring to the second buffer
while the program is ongoing. Does it work?Won't MLC and FlexOneNAND have that problem too?
Agree, I'm not yet tested the 2PLANE features and it doesn't support write-while-program.
If the 2plane and flex-onenand, it used the previous one.
Quoted 9 lines
Quoted 7 lines
ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, prev +this->writesize, !ret && !prev_subpage); } if (ret) {My original patch had "written -= prevlen" here.
Agreed.
Quoted 16 lines
Quoted 15 lines
printk(KERN_ERR "
onenand_write_ops_nolock:write filaed %d\n", ret); break; } if (written == len) { /* Only check verify write turn on */ ret = onenand_verify(mtd, buf - len, to - len, len); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; }
Quoted 1 line
Why not just break here?
E.g., In original version, write bufferram 0 and it changes bufferam 1 automatically.
Same as if break here, it points out the previous bufferram.
Quoted 21 lines
Quoted 21 lines
} ONENAND_SET_NEXT_BUFFERRAM(this); if (written == len) break; } this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); written += thislen; column = 0; prev_subpage = subpage; prev = to; prevlen = thislen; to += thislen; buf += thislen; first = 0; }
Quoted 3 lines
Quoted 1 line
ops->retlen = written;Is
ops->oobretlenneeded here also?
Okay I will added. I'm not sure YAFFS handle this one correctly. Maybe it used the retlen in ops operation (data + spare).
Quoted 4 lines
Quoted 4 lines
return ret; }
Thank you,
Kyungmin Park
Quoted 148 lines · Kyungmin Park
Quoted 49 lines · Adrian Hunter
Quoted 1 line · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.Thanks for looking at write-while-program. I have a few questions though. I have copied the whole function below because the diff is too confusing:
Quoted 35 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write main and/or
out-of-band* @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure*
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len= %i\n", (unsigned int) to, (int) len);
/* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL;Quoted 9 lines
} /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; }Quoted 4 lines
The loop cannot handle the case when len is zero so I had:
if (!len) return 0;Yes there's length check. I wonder is it possible to write with zero? Is it handle at driver level or upper level such as filesystem? But no problem to just add length check here at this time.
Quoted 71 lines
Quoted 64 lines
if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = to & (mtd->oobsize - 1); column = to & (mtd->writesize - 1); /* Loop until all data write */ while (1) { if (written < len) { u_char *wbuf = (u_char *) buf; thislen = min_t(int, mtd->writesize - column, len -written);
thisooblen = min_t(int, oobsize - oobcolumn, ooblen
- oobwritten);cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to,thislen);
/* Partial page write */ subpage = thislen < mtd->writesize; if (subpage) { memset(this->page_buf, 0xff, mtd->writesize); memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf,0,
mtd->writesize);if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf,oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob,
thisooblen);oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); } else ONENAND_SET_NEXT_BUFFERRAM(this); if (!first) { ONENAND_SET_PREV_BUFFERRAM(this);Quoted 7 lines
ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ONENAND_IS_2PLANE(this)) {Quoted 7 lines
I do not understand how 2PLANE is compatible with write-while-program because
2PLANE uses both buffers so we cannot start transferring to the second buffer
while the program is ongoing. Does it work?Won't MLC and FlexOneNAND have that problem too?
Agree, I'm not yet tested the 2PLANE features and it doesn't support write-while-program.
If the 2plane and flex-onenand, it used the previous one.Quoted 5 lines
Quoted 5 lines
ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, prev +this->writesize, !ret && !prev_subpage); }
Quoted 5 lines
Quoted 2 lines
Quoted 1 line
if (ret) {My original patch had "written -= prevlen" here.
Agreed.
Also we need to invalidate the other bufferram because we transferred the data but did not write it. i.e.
if (written < len) onenand_update_bufferram(mtd, to, 0);
Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
printk(KERN_ERR "
onenand_write_ops_nolock:write filaed %d\n", ret); break; } if (written == len) { /* Only check verify write turn on */ ret = onenand_verify(mtd, buf - len, to - len, len); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; }
Quoted 5 lines
Quoted 1 line
Why not just break here?
E.g., In original version, write bufferram 0 and it changes bufferam 1 automatically.
Same as if break here, it points out the previous bufferram.
The previous bufferram is the last one that was used, so it should point to the previous one.
Quoted 32 lines
Quoted 20 lines
Quoted 20 lines
} ONENAND_SET_NEXT_BUFFERRAM(this); if (written == len) break; } this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); written += thislen; column = 0; prev_subpage = subpage; prev = to; prevlen = thislen; to += thislen; buf += thislen; first = 0; }Quoted 2 lines
Quoted 1 line
ops->retlen = written;Is
ops->oobretlenneeded here also?Okay I will added. I'm not sure YAFFS handle this one correctly. Maybe it used the retlen in ops operation (data + spare).
Quoted 2 lines
Quoted 2 lines
return ret; }Thank you,
Kyungmin Park
Quoted 164 lines · Adrian Hunter
Quoted 163 lines · Kyungmin Park
Quoted 56 lines · Adrian Hunter
Quoted 2 lines · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.Thanks for looking at write-while-program. I have a few questions though.
I have copied the whole function below because the diff is too confusing:Quoted 39 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write main and/or
out-of-band* @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure*
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x,len
= %i\n", (unsigned int) to, (int) len);/* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL; }Quoted 8 lines
/* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; }Quoted 4 lines
The loop cannot handle the case when len is zero so I had:
if (!len) return 0;Yes there's length check. I wonder is it possible to write with zero? Is it handle at driver level or upper level such as filesystem? But no problem to just add length check here at this time.
Quoted 74 lines
Quoted 66 lines
if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = to & (mtd->oobsize - 1); column = to & (mtd->writesize - 1); /* Loop until all data write */ while (1) { if (written < len) { u_char *wbuf = (u_char *) buf; thislen = min_t(int, mtd->writesize - column, len-
written);
thisooblen = min_t(int, oobsize - oobcolumn,
ooblen
- oobwritten);cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to,thislen);
/* Partial page write */ subpage = thislen < mtd->writesize; if (subpage) { memset(this->page_buf, 0xff, mtd->writesize); memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf,0,
mtd->writesize);if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf,oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob,
thisooblen);oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); } else ONENAND_SET_NEXT_BUFFERRAM(this); if (!first) { ONENAND_SET_PREV_BUFFERRAM(this);Quoted 7 lines
ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ONENAND_IS_2PLANE(this)) {Quoted 7 lines
I do not understand how 2PLANE is compatible with write-while-program because
2PLANE uses both buffers so we cannot start transferring to the second buffer
while the program is ongoing. Does it work?Won't MLC and FlexOneNAND have that problem too?
Agree, I'm not yet tested the 2PLANE features and it doesn't support write-while-program.
If the 2plane and flex-onenand, it used the previous one.Quoted 8 lines
Quoted 6 lines
ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, prev +this->writesize, !ret && !prev_subpage); } if (ret) {My original patch had "written -= prevlen" here.
Quoted 8 lines
Quoted 2 lines
Agreed.
Also we need to invalidate the other bufferram because we transferred the data but did not write it. i.e.
if (written < len)
onenand_update_bufferram(mtd, to, 0);
For clarity, clear all bufferram at error case.
Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
printk(KERN_ERR "
onenand_write_ops_nolock:write filaed %d\n", ret); break; } if (written == len) { /* Only check verify write turn on */ ret = onenand_verify(mtd, buf - len, to - len, len); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; }
Quoted 9 lines
Quoted 5 lines
Quoted 1 line
Why not just break here?
E.g., In original version, write bufferram 0 and it changes bufferam 1 automatically.
Same as if break here, it points out the previous bufferram.The previous bufferram is the last one that was used, so it should point to the previous one.
then next time, it overwrite the previous bufferram, but it expects it write next bufferram.
Thank you,
Kyungmin Park
Quoted 159 lines · Kyungmin Park
Quoted 157 lines · Adrian Hunter
Quoted 156 lines · Kyungmin Park
Quoted 53 lines · Adrian Hunter
Quoted 1 line · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.Thanks for looking at write-while-program. I have a few questions though.
I have copied the whole function below because the diff is too confusing:Quoted 39 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write main and/or
out-of-band* @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure*
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x,len
= %i\n", (unsigned int) to, (int) len);/* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL; }Quoted 8 lines
/* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; }Quoted 4 lines
The loop cannot handle the case when len is zero so I had:
if (!len) return 0;Yes there's length check. I wonder is it possible to write with zero? Is it handle at driver level or upper level such as filesystem? But no problem to just add length check here at this time.
Quoted 73 lines
Quoted 66 lines
if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = to & (mtd->oobsize - 1); column = to & (mtd->writesize - 1); /* Loop until all data write */ while (1) { if (written < len) { u_char *wbuf = (u_char *) buf; thislen = min_t(int, mtd->writesize - column, len-
written);
thisooblen = min_t(int, oobsize - oobcolumn,
ooblen
- oobwritten);cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to,thislen);
/* Partial page write */ subpage = thislen < mtd->writesize; if (subpage) { memset(this->page_buf, 0xff, mtd->writesize); memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf,0,
mtd->writesize);if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd, oobbuf,oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob,
thisooblen);oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); } else ONENAND_SET_NEXT_BUFFERRAM(this); if (!first) { ONENAND_SET_PREV_BUFFERRAM(this);Quoted 7 lines
ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ONENAND_IS_2PLANE(this)) {Quoted 7 lines
I do not understand how 2PLANE is compatible with write-while-program because
2PLANE uses both buffers so we cannot start transferring to the second buffer
while the program is ongoing. Does it work?Won't MLC and FlexOneNAND have that problem too?
Agree, I'm not yet tested the 2PLANE features and it doesn't support write-while-program.
If the 2plane and flex-onenand, it used the previous one.Quoted 7 lines
Quoted 6 lines
ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, prev +this->writesize, !ret && !prev_subpage); } if (ret) {My original patch had "written -= prevlen" here.
Agreed.
Quoted 7 lines
Quoted 5 lines
Also we need to invalidate the other bufferram because we transferred the data but did not write it. i.e.
if (written < len)
onenand_update_bufferram(mtd, to, 0);For clarity, clear all bufferram at error case.
OK
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 14 lines
Quoted 14 lines
printk(KERN_ERR "
onenand_write_ops_nolock:write filaed %d\n", ret); break; } if (written == len) { /* Only check verify write turn on */ ret = onenand_verify(mtd, buf - len, to - len, len); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; }Quoted 1 line
Why not just break here?
E.g., In original version, write bufferram 0 and it changes bufferam 1 automatically.
Same as if break here, it points out the previous bufferram.
Quoted 6 lines
Quoted 3 lines
The previous bufferram is the last one that was used, so it should point to the previous one.
then next time, it overwrite the previous bufferram, but it expects it write next bufferram.
The current MTD write is finished because written == len. The next one starts by changing the bufferram with this->command(mtd, ONENAND_CMD_BUFFERRAM, ...)
Quoted 210 lines · Adrian Hunter
Quoted 173 lines · Kyungmin Park
Quoted 170 lines · Adrian Hunter
Quoted 168 lines · Kyungmin Park
Quoted 57 lines · Adrian Hunter
Quoted 2 lines · Kyungmin Park
This is from Adrian hunter and modified for
write_opsoperation.Thanks for looking at write-while-program. I have a few questions though.
I have copied the whole function below because the diff is too confusing:Quoted 39 lines
/**
*onenand_write_ops_nolock- [OneNAND Interface] write main and/or
out-of-band* @param mtd MTD device structure * @param to offset to write to * @param ops oob operation description structure*
* Write main and/or oob with ECC
*/
static int onenand_write_ops_nolock(structmtd_info*mtd,loff_tto,
structmtd_oob_ops*ops){ struct onenand_chip *this = mtd->priv; int written = 0, column, thislen = 0, subpage = 0; int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; int oobwritten = 0, oobcolumn, thisooblen, oobsize; size_t len = ops->len; size_t ooblen = ops->ooblen; const u_char *buf = ops->datbuf; const u_char *oob = ops->oobbuf; u_char *oobbuf; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x,len
= %i\n", (unsigned int) to, (int) len);/* Initialize retlen, in case of early exit */ ops->retlen = 0; ops->oobretlen = 0; /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); return -EINVAL; }Quoted 8 lines
/* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); return -EINVAL; }Quoted 4 lines
The loop cannot handle the case when len is zero so I had:
if (!len) return 0;Yes there's length check. I wonder is it possible to write with zero? Is it handle at driver level or upper level such as filesystem? But no problem to just add length check here at this time.
Quoted 76 lines
Quoted 67 lines
if (ops->mode == MTD_OOB_AUTO) oobsize = this->ecclayout->oobavail; else oobsize = mtd->oobsize; oobcolumn = to & (mtd->oobsize - 1); column = to & (mtd->writesize - 1); /* Loop until all data write */ while (1) { if (written < len) { u_char *wbuf = (u_char *) buf; thislen = min_t(int, mtd->writesize - column, len-
written);
thisooblen = min_t(int, oobsize - oobcolumn,
ooblen
- oobwritten);cond_resched(); this->command(mtd, ONENAND_CMD_BUFFERRAM, to,thislen);
/* Partial page write */ subpage = thislen < mtd->writesize; if (subpage) { memset(this->page_buf, 0xff, mtd->writesize); memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; } this->write_bufferram(mtd, ONENAND_DATARAM, wbuf,0,
mtd->writesize);if (oob) { oobbuf = this->oob_buf; /* We send data to spare ram with oobsize * to prevent byte access */ memset(oobbuf, 0xff, mtd->oobsize); if (ops->mode == MTD_OOB_AUTO) onenand_fill_auto_oob(mtd,oobbuf,
oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob,
thisooblen);oobwritten += thisooblen; oob += thisooblen; oobcolumn = 0; } else oobbuf = (u_char *) ffchars; this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); } else ONENAND_SET_NEXT_BUFFERRAM(this); if (!first) { ONENAND_SET_PREV_BUFFERRAM(this);Quoted 8 lines
ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ONENAND_IS_2PLANE(this)) {Quoted 7 lines
I do not understand how 2PLANE is compatible with write-while-program because
2PLANE uses both buffers so we cannot start transferring to the second buffer
while the program is ongoing. Does it work?Won't MLC and FlexOneNAND have that problem too?
Agree, I'm not yet tested the 2PLANE features and it doesn't support write-while-program.
If the 2plane and flex-onenand, it used the previous one.Quoted 8 lines
Quoted 6 lines
ONENAND_SET_BUFFERRAM1(this); onenand_update_bufferram(mtd, prev +this->writesize, !ret && !prev_subpage); } if (ret) {My original patch had "written -= prevlen" here.
Agreed.
Quoted 7 lines
Quoted 5 lines
Also we need to invalidate the other bufferram because we transferred the data but did not write it. i.e.
if (written < len)
onenand_update_bufferram(mtd, to, 0);For clarity, clear all bufferram at error case.
OK
Quoted 26 lines
Quoted 25 lines
Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
printk(KERN_ERR
"onenand_write_ops_nolock:write filaed %d\n", ret); break; } if (written == len) { /* Only check verify write turn on */ ret = onenand_verify(mtd, buf - len, to - len, len); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; }Quoted 5 lines
Quoted 1 line
Why not just break here?
E.g., In original version, write bufferram 0 and it changes bufferam 1 automatically.
Same as if break here, it points out the previous bufferram.The previous bufferram is the last one that was used, so it should point to the previous one.
Quoted 7 lines
Quoted 2 lines
then next time, it overwrite the previous bufferram, but it expects it write next bufferram.
The current MTD write is finished because written == len. The next one starts by changing the bufferram with
this->command(mtd, ONENAND_CMD_BUFFERRAM, ...)
Ah sorry, you're right. I'm confused.
I will repost it.
Thank you,
Kyungmin Park