[JFFS2] Fix block refiling
- block refiling when writing directly to flash a buffer which is bigger than wbuf - retry cases for flushing wbuf Signed-off-by: Estelle Hammache <estelle.hammache@st.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
committed by
Thomas Gleixner
parent
e4803c30d6
commit
7f716cf3f9
@@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
* For licensing information, see the file 'LICENCE' in this directory.
|
* For licensing information, see the file 'LICENCE' in this directory.
|
||||||
*
|
*
|
||||||
* $Id: wbuf.c,v 1.82 2004/11/20 22:08:31 dwmw2 Exp $
|
* $Id: wbuf.c,v 1.83 2005/01/24 21:24:15 hammache Exp $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -130,7 +130,10 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
#define REFILE_NOTEMPTY 0
|
||||||
|
#define REFILE_ANYWAY 1
|
||||||
|
|
||||||
|
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
|
||||||
{
|
{
|
||||||
D1(printk("About to refile bad block at %08x\n", jeb->offset));
|
D1(printk("About to refile bad block at %08x\n", jeb->offset));
|
||||||
|
|
||||||
@@ -144,7 +147,8 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
|||||||
D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
|
D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
|
||||||
list_add(&jeb->list, &c->bad_used_list);
|
list_add(&jeb->list, &c->bad_used_list);
|
||||||
} else {
|
} else {
|
||||||
BUG();
|
if (allow_empty == REFILE_NOTEMPTY)
|
||||||
|
BUG();
|
||||||
/* It has to have had some nodes or we couldn't be here */
|
/* It has to have had some nodes or we couldn't be here */
|
||||||
D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
|
D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
|
||||||
list_add(&jeb->list, &c->erase_pending_list);
|
list_add(&jeb->list, &c->erase_pending_list);
|
||||||
@@ -179,7 +183,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||||||
|
|
||||||
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
|
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
|
||||||
|
|
||||||
jffs2_block_refile(c, jeb);
|
jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
|
||||||
|
|
||||||
/* Find the first node to be recovered, by skipping over every
|
/* Find the first node to be recovered, by skipping over every
|
||||||
node which ends before the wbuf starts, or which is obsolete. */
|
node which ends before the wbuf starts, or which is obsolete. */
|
||||||
@@ -269,12 +273,12 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (end-start >= c->wbuf_pagesize) {
|
if (end-start >= c->wbuf_pagesize) {
|
||||||
/* Need to do another write immediately. This, btw,
|
/* Need to do another write immediately, but it's possible
|
||||||
means that we'll be writing from 'buf' and not from
|
that this is just because the wbuf itself is completely
|
||||||
the wbuf. Since if we're writing from the wbuf there
|
full, and there's nothing earlier read back from the
|
||||||
won't be more than a wbuf full of data, now will
|
flash. Hence 'buf' isn't necessarily what we're writing
|
||||||
there? :) */
|
from. */
|
||||||
|
unsigned char *rewrite_buf = buf?:c->wbuf;
|
||||||
uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
|
uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
|
||||||
|
|
||||||
D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
|
D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
|
||||||
@@ -292,14 +296,15 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||||||
#endif
|
#endif
|
||||||
if (jffs2_cleanmarker_oob(c))
|
if (jffs2_cleanmarker_oob(c))
|
||||||
ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
|
ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
|
||||||
buf, NULL, c->oobinfo);
|
rewrite_buf, NULL, c->oobinfo);
|
||||||
else
|
else
|
||||||
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, buf);
|
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
|
||||||
|
|
||||||
if (ret || retlen != towrite) {
|
if (ret || retlen != towrite) {
|
||||||
/* Argh. We tried. Really we did. */
|
/* Argh. We tried. Really we did. */
|
||||||
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
|
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
|
||||||
kfree(buf);
|
if (buf)
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
if (retlen) {
|
if (retlen) {
|
||||||
struct jffs2_raw_node_ref *raw2;
|
struct jffs2_raw_node_ref *raw2;
|
||||||
@@ -321,10 +326,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||||||
|
|
||||||
c->wbuf_len = (end - start) - towrite;
|
c->wbuf_len = (end - start) - towrite;
|
||||||
c->wbuf_ofs = ofs + towrite;
|
c->wbuf_ofs = ofs + towrite;
|
||||||
memcpy(c->wbuf, buf + towrite, c->wbuf_len);
|
memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
|
||||||
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
|
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
|
||||||
|
if (buf)
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
} else {
|
} else {
|
||||||
/* OK, now we're left with the dregs in whichever buffer we're using */
|
/* OK, now we're left with the dregs in whichever buffer we're using */
|
||||||
if (buf) {
|
if (buf) {
|
||||||
@@ -547,6 +552,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
|
|||||||
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
|
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
|
||||||
down_write(&c->wbuf_sem);
|
down_write(&c->wbuf_sem);
|
||||||
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||||
|
/* retry flushing wbuf in case jffs2_wbuf_recover
|
||||||
|
left some data in the wbuf */
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||||
|
}
|
||||||
up_write(&c->wbuf_sem);
|
up_write(&c->wbuf_sem);
|
||||||
} else while (old_wbuf_len &&
|
} else while (old_wbuf_len &&
|
||||||
old_wbuf_ofs == c->wbuf_ofs) {
|
old_wbuf_ofs == c->wbuf_ofs) {
|
||||||
@@ -561,6 +572,12 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
|
|||||||
down(&c->alloc_sem);
|
down(&c->alloc_sem);
|
||||||
down_write(&c->wbuf_sem);
|
down_write(&c->wbuf_sem);
|
||||||
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||||
|
/* retry flushing wbuf in case jffs2_wbuf_recover
|
||||||
|
left some data in the wbuf */
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||||
|
}
|
||||||
up_write(&c->wbuf_sem);
|
up_write(&c->wbuf_sem);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -580,6 +597,9 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
|
|||||||
|
|
||||||
down_write(&c->wbuf_sem);
|
down_write(&c->wbuf_sem);
|
||||||
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
|
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
|
||||||
|
/* retry - maybe wbuf recover left some data in wbuf. */
|
||||||
|
if (ret)
|
||||||
|
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
|
||||||
up_write(&c->wbuf_sem);
|
up_write(&c->wbuf_sem);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -762,9 +782,18 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
|
|||||||
|
|
||||||
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
|
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
|
||||||
/* At this point we have no problem,
|
/* At this point we have no problem,
|
||||||
c->wbuf is empty.
|
c->wbuf is empty. However refile nextblock to avoid
|
||||||
|
writing again to same address.
|
||||||
*/
|
*/
|
||||||
*retlen = donelen;
|
struct jffs2_eraseblock *jeb;
|
||||||
|
|
||||||
|
spin_lock(&c->erase_completion_lock);
|
||||||
|
|
||||||
|
jeb = &c->blocks[outvec_to / c->sector_size];
|
||||||
|
jffs2_block_refile(c, jeb, REFILE_ANYWAY);
|
||||||
|
|
||||||
|
*retlen = 0;
|
||||||
|
spin_unlock(&c->erase_completion_lock);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user