bad blocks support for JFFS1
Aleksander Sanochkin
asanochkin at Lnxw.COM
Tue Jan 8 10:26:20 EST 2002
Hi!
Attached is a patch providing the bad block handling code for JFFS1.
It is not against the latest CVS actually (against a snapshot dated
mid September), but we are hoping some people may find it useful anyway.
Best regards.
-------------- next part --------------
diff -ur /work/tmp/jffs/fs/jffs/inode-v23.c /work/projects/jffs/integration/bad/mtd/fs/jffs/inode-v23.c
--- /work/tmp/jffs/fs/jffs/inode-v23.c Thu Oct 11 11:38:25 2001
+++ /work/projects/jffs/integration/bad/mtd/fs/jffs/inode-v23.c Thu Oct 11 11:01:14 2001
@@ -400,9 +400,9 @@
buf->f_bsize = PAGE_CACHE_SIZE;
buf->f_blocks = (fmc->flash_size / PAGE_CACHE_SIZE)
- (fmc->min_free_size / PAGE_CACHE_SIZE);
- buf->f_bfree = (jffs_free_size1(fmc) + jffs_free_size2(fmc) +
- fmc->dirty_size - fmc->min_free_size)
- >> PAGE_CACHE_SHIFT;
+ buf->f_bfree = (fmc->good_free_size + fmc->good_dirty_size) /
+ PAGE_CACHE_SIZE
+ - (fmc->min_free_size / PAGE_CACHE_SIZE);
/* statfs() is prevented from reporting negative sizes. */
if (buf->f_bfree < 0)
{
@@ -1616,7 +1616,7 @@
}
fst.size = fmc->flash_size;
fst.used = fmc->used_size;
- fst.dirty = fmc->dirty_size;
+ fst.dirty = fmc->good_dirty_size;
fst.begin = fmc->head ? fmc->head->offset : 0;
fst.end = fmc->tail ? fmc->tail->offset +
fmc->tail->size : 0;
@@ -1628,6 +1628,35 @@
&fst,
sizeof(struct jffs_flash_status))) {
ret = -EFAULT;
+ }
+ }
+ break;
+ case JFFS_GET_BAD_TABLE:
+ {
+ struct jffs_bad_table buf;
+
+ if (copy_from_user(&buf, (struct jffs_bad_table *) arg,
+ sizeof(struct jffs_bad_table)))
+ {
+ ret = -EFAULT;
+ break;
+ }
+ if (buf.num_sectors > c->fmc->num_sectors)
+ {
+ buf.num_sectors = c->fmc->num_sectors;
+ }
+ if (copy_to_user(buf.bad_block_table,
+ c->fmc->bad_block_table,
+ buf.num_sectors))
+ {
+ ret = -EFAULT;
+ break;
+ }
+ if (copy_to_user((struct jffs_bad_table *) arg, &buf,
+ sizeof(struct jffs_bad_table)))
+ {
+ ret = -EFAULT;
+ break;
}
}
break;
diff -ur /work/tmp/jffs/fs/jffs/intrep.c /work/projects/jffs/integration/bad/mtd/fs/jffs/intrep.c
--- /work/tmp/jffs/fs/jffs/intrep.c Thu Oct 11 11:42:57 2001
+++ /work/projects/jffs/integration/bad/mtd/fs/jffs/intrep.c Thu Oct 11 11:05:58 2001
@@ -194,9 +194,10 @@
static int
-flash_safe_write(struct mtd_info *mtd, loff_t to,
+flash_safe_write(struct jffs_fmcontrol *fmc, loff_t to,
const u_char *buf, size_t count)
{
+ struct mtd_info *mtd = fmc->mtd;
size_t retlen;
int res;
@@ -204,7 +205,10 @@
mtd, (unsigned int) to, buf, count));
res = MTD_WRITE(mtd, to, count, &retlen, buf);
- if (retlen != count) {
+ if (res == -EIO)
+ {
+ jffs_mark_sector(fmc, to, 1);
+ } else if (retlen != count) {
printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
}
return res?res:retlen;
@@ -212,18 +216,23 @@
static int
-flash_safe_writev(struct mtd_info *mtd, const struct iovec *vecs,
+flash_safe_writev(struct jffs_fmcontrol *fmc, const struct iovec *vecs,
unsigned long iovec_cnt, loff_t to)
{
+ struct mtd_info *mtd = fmc->mtd;
size_t retlen, retlen_a;
int i;
int res;
D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n",
- mtd, (unsigned int) to, vecs));
+ fmc, (unsigned int) to, vecs));
if (mtd->writev) {
res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen);
+ if (res == -EIO)
+ {
+ jffs_mark_sector(fmc, to, 1);
+ }
return res ? res : retlen;
}
/* Not implemented writev. Repeatedly use write - on the not so
@@ -233,16 +242,14 @@
retlen=0;
for (i=0; !res && i<iovec_cnt; i++) {
- res = MTD_WRITE(mtd, to, vecs[i].iov_len, &retlen_a, vecs[i].iov_base);
- if (retlen_a != vecs[i].iov_len) {
- printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res);
- if (i != iovec_cnt-1)
- return -EIO;
- }
- /* If res is non-zero, retlen_a is undefined, but we don't
- care because in that case it's not going to be
- returned anyway.
- */
+ res = flash_safe_write(fmc, to, vecs[i].iov_base, vecs[i].iov_len);
+ if (res < 0)
+ {
+ break;
+ }
+
+ retlen_a = res;
+ res = 0;
to += retlen_a;
retlen += retlen_a;
}
@@ -250,8 +257,14 @@
}
+/* We don't modify the following flash_memset() function because
+ * it is called only from jffs_clear_end_of_node() and from
+ * jffs_write_dummy_inode(). It doesn't write anything useful to Flash.
+ * So if it eventually failed due to a bad-block error, nothing
+ * dangerous would happen. */
+
static int
-flash_memset(struct mtd_info *mtd, loff_t to,
+flash_memset(struct jffs_fmcontrol *fmc, loff_t to,
const u_char c, size_t size)
{
static unsigned char pattern[64];
@@ -266,7 +279,8 @@
/* write as many 64-byte chunks as we can */
while (size >= 64) {
- err = flash_safe_write(mtd, to, pattern, 64);
+ /* Take errors into account. */
+ err = flash_safe_write(fmc, to, pattern, 64);
if (err)
{
D(printk("2flash_safe_write() failed\n"));
@@ -280,7 +294,8 @@
if(size)
{
- err = flash_safe_write(mtd, to, pattern, size);
+ /* Take errors into account. */
+ err = flash_safe_write(fmc, to, pattern, size);
if (err)
{
D(printk("2flash_safe_write() failed\n"));
@@ -304,9 +319,11 @@
static int
-flash_erase_region(struct mtd_info *mtd, loff_t start,
+flash_erase_region(struct jffs_fmcontrol *fmc, loff_t start,
size_t size)
{
+ struct mtd_info *mtd = fmc->mtd;
+ int err;
struct erase_info *erase;
DECLARE_WAITQUEUE(wait, current);
wait_queue_head_t wait_q;
@@ -327,13 +344,19 @@
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&wait_q, &wait);
- if (MTD_ERASE(mtd, erase) < 0) {
+ if ((err = MTD_ERASE(fmc->mtd, erase)) < 0) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&wait_q, &wait);
kfree(erase);
- printk(KERN_WARNING "flash: erase of region [0x%lx, 0x%lx] "
- "totally failed\n", (long)start, (long)start + size);
+ if (err == -EIO)
+ {
+ jffs_mark_sector(fmc, start, 1);
+ return -EIO;
+ }
+ else
+ printk(KERN_WARNING "flash: erase of region [0x%lx, 0x%lx] "
+ "totally failed\n", (long)start, (long)start + size);
return -1;
}
@@ -740,7 +763,7 @@
D1(printk("check_partly_erased_sector():erasing sector starting 0x%x.\n",
offset));
- if (flash_erase_region(fmc->mtd,
+ if (flash_erase_region(fmc,
offset, fmc->sector_size) < 0) {
printk(KERN_ERR "JFFS: Erase of flash failed. "
"offset = %u, erase_size = %d\n",
@@ -972,7 +995,7 @@
D1(printk("jffs_scan_flash():erasing sector starting 0x%x.\n",
offset));
- if (flash_erase_region(fmc->mtd,
+ if (flash_erase_region(fmc,
offset, fmc->sector_size) < 0) {
printk(KERN_ERR "JFFS: Erase of flash failed. "
"offset = %u, erase_size = %d\n",
@@ -1794,19 +1817,25 @@
raw_inode.chksum
= jffs_checksum(&raw_inode, sizeof(struct jffs_raw_inode));
- if ((err = flash_safe_write(fmc->mtd,
+ if ((err = flash_safe_write(fmc,
dirty_fm->offset,
(u_char *)&raw_inode,
sizeof(struct jffs_raw_inode)))
< 0) {
- printk(KERN_ERR "JFFS: jffs_write_dummy_node: "
- "flash_safe_write failed!\n");
+ if (err != -EIO)
+ printk(KERN_ERR "JFFS: jffs_write_dummy_node: "
+ "flash_safe_write failed!\n");
return err;
}
}
else {
flash_safe_acquire(fmc->mtd);
- flash_memset(fmc->mtd, dirty_fm->offset, 0, dirty_fm->size);
+ if ((err = flash_memset(fmc, dirty_fm->offset, 0, dirty_fm->size)) < 0)
+ {
+ D(printk(KERN_ERR "JFFS: jffs_write_dummy_node: "
+ "flash_memset failed!\n"));
+ return err;
+ }
flash_safe_release(fmc->mtd);
}
@@ -1872,7 +1901,8 @@
while (!fm) {
/* Deadlocks suck. */
- while(fmc->free_size < fmc->min_free_size + total_size + slack) {
+ while(fmc->good_free_size < fmc->min_free_size +
+ total_size + slack) {
jffs_fm_write_unlock(fmc);
if (!JFFS_ENOUGH_SPACE(c, total_size + slack))
return -ENOSPC;
@@ -1885,7 +1915,7 @@
if (err == -ENOSPC) {
/* Just out of space. GC and try again */
jffs_fm_write_unlock(fmc);
- if (fmc->dirty_size < fmc->sector_size) {
+ if (fmc->good_dirty_size < fmc->sector_size) {
D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) "
"failed, no dirty space to GC\n", fmc,
total_size));
@@ -1912,7 +1942,7 @@
if (!fm->nodes) {
/* The jffs_fm struct that we got is not good enough.
Make that space dirty and try again */
- if ((err = jffs_write_dummy_node(c, fm)) < 0) {
+ if ((err = jffs_write_dummy_node(c, fm)) < 0 && err != -EIO) {
jffs_fm_write_unlock(fmc);
D(printk("jffs_write_node(): "
"jffs_write_dummy_node(): Failed!\n"));
@@ -1989,12 +2019,18 @@
*/
}
- if ((err = flash_safe_writev(fmc->mtd, node_iovec, iovec_cnt,
+ if ((err = flash_safe_writev(fmc, node_iovec, iovec_cnt,
pos) < 0)) {
jffs_fmfree_partly(fmc, fm, 0);
jffs_fm_write_unlock(fmc);
printk(KERN_ERR "JFFS: jffs_write_node: Failed to write, "
"requested %i, wrote %i\n", total_size, err);
+ if (err == -EIO)
+ {
+ D(printk("jffs_write_node(): error writing node's data "
+ "at the offset %.8x count = %.8lx\n",
+ pos, iovec_cnt));
+ }
goto retry;
}
if (raw_inode->deleted)
@@ -2315,7 +2351,7 @@
n->range_next = new_node;
n->data_size = offset - n->data_offset;
if (new_node->fm)
- jffs_add_node(new_node);
+ jffs_add_node(f->c->fmc, new_node);
else {
D1(printk(KERN_WARNING "jffs_delete_data(): Splitting an empty node (file hold).\n!"));
D1(printk(KERN_WARNING "FIXME: Did dwmw2 do the right thing here?\n"));
@@ -2793,7 +2829,6 @@
/* The jffs_fm struct that we got is not big enough. */
/* This should never happen, because we deal with this case
in jffs_garbage_collect_next().*/
- printk(KERN_WARNING "jffs_rewrite_data(): Allocated node is too small (%d bytes of %d)\n", fm->size, total_size);
if ((err = jffs_write_dummy_node(c, fm)) < 0) {
D(printk("jffs_rewrite_data(): "
"jffs_write_dummy_node() Failed!\n"));
@@ -2801,6 +2836,10 @@
err = -ENOSPC;
}
jffs_fm_write_unlock(fmc);
+ if (err == -EIO)
+ {
+ goto retry;
+ }
return err;
}
@@ -2836,7 +2875,7 @@
"to pos 0x%ul.\n", pos));
D3(jffs_print_raw_inode(&raw_inode));
- if ((err = flash_safe_write(fmc->mtd, pos,
+ if ((err = flash_safe_write(fmc, pos,
(u_char *) &raw_inode,
sizeof(struct jffs_raw_inode)
- sizeof(__u32)
@@ -2856,7 +2895,7 @@
if (f->nsize) {
D3(printk("jffs_rewrite_data(): Writing name \"%s\" to "
"pos 0x%ul.\n", f->name, (unsigned int) pos));
- if ((err = flash_safe_write(fmc->mtd, pos,
+ if ((err = flash_safe_write(fmc, pos,
(u_char *)f->name,
f->nsize)) < 0) {
jffs_fmfree_partly(fmc, fm, total_data_size);
@@ -2894,7 +2933,7 @@
"failed! (r = %d)\n", r);
return -1;
}
- if ((err = flash_safe_write(fmc->mtd,
+ if ((err = flash_safe_write(fmc,
pos, page, r)) < 0) {
free_page((unsigned long)page);
jffs_fmfree_partly(fmc, fm, 0);
@@ -2920,7 +2959,7 @@
/* Add the checksum. */
if ((err
- = flash_safe_write(fmc->mtd, pos_dchksum,
+ = flash_safe_write(fmc, pos_dchksum,
&((u_char *)
&raw_inode)[JFFS_RAW_INODE_DCHKSUM_OFFSET],
sizeof(__u32) + sizeof(__u16)
@@ -3059,7 +3098,7 @@
increase the size of the node we're writing from the size
of the node we're obsoleting
*/
- if (space_needed > fmc->free_size) {
+ if (space_needed > fmc->good_free_size) {
/* If we've gone below min_free_size for some reason,
don't fuck up. This is why we have
min_free_size > sector_size. Whinge about it though,
@@ -3067,10 +3106,10 @@
*/
D1(printk(KERN_WARNING "jffs_garbage_collect_next(): "
"space_needed %d exceeded free_size %d\n",
- space_needed, fmc->free_size));
+ space_needed, fmc->good_free_size));
extra_available = 0;
} else {
- extra_available = fmc->free_size - space_needed;
+ extra_available = fmc->good_free_size - space_needed;
}
/* Check that we don't use up any more 'extra' space than
@@ -3167,14 +3206,14 @@
zero_offset_data = zero_offset + cutting_raw_inode;
zero_size_data = zero_size - cutting_raw_inode;
flash_safe_acquire(fmc->mtd);
- flash_memset(fmc->mtd, zero_offset_data, 0, zero_size_data);
+ flash_memset(fmc, zero_offset_data, 0, zero_size_data);
flash_safe_release(fmc->mtd);
/* Should we clear a part of the raw inode? */
if (cutting_raw_inode) {
/* I guess it is ok to clear the raw inode in this order. */
flash_safe_acquire(fmc->mtd);
- flash_memset(fmc->mtd, zero_offset, 0,
+ flash_memset(fmc, zero_offset, 0,
cutting_raw_inode);
flash_safe_release(fmc->mtd);
}
@@ -3215,8 +3254,9 @@
offset = fmc->head->offset;
/* Now, let's try to do the erase. */
- if ((err = flash_erase_region(fmc->mtd,
- offset, erase_size)) < 0) {
+ jffs_mark_sector(fmc, offset, 0);
+ if ((err = flash_erase_region(fmc,
+ offset, erase_size)) < 0 && err != -EIO) {
printk(KERN_ERR "JFFS: Erase of flash failed. "
"offset = %u, erase_size = %ld\n",
offset, erase_size);
@@ -3313,14 +3353,14 @@
if (erased)
break;
- if (fmc->free_size == 0) {
+ if (fmc->good_free_size == 0) {
/* Argh */
printk(KERN_ERR "jffs_garbage_collect_now(): free_size == 0. This is BAD.\n");
result = -ENOSPC;
break;
}
- if (fmc->dirty_size < fmc->sector_size) {
+ if (fmc->good_dirty_size < fmc->sector_size) {
/* Actually, we _may_ have been able to free some,
* if there are many overlapping nodes which aren't
* actually marked dirty because they still have
@@ -3370,7 +3410,7 @@
c->fmc->free_size, c->fmc->dirty_size, c->fmc->sector_size));
/* If there's not enough dirty space to free a block, there's no point. */
- if (c->fmc->dirty_size < c->fmc->sector_size) {
+ if (c->fmc->good_dirty_size < c->fmc->sector_size) {
D2(printk(KERN_NOTICE "thread_should_wake(): Not waking. Insufficient dirty space\n"));
return 0;
}
@@ -3386,12 +3426,12 @@
}
#endif
/* If there are fewer free bytes than the threshold, GC */
- if (c->fmc->free_size < c->gc_minfree_threshold) {
+ if (c->fmc->good_free_size < c->gc_minfree_threshold) {
D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to insufficent free space\n"));
return 1;
}
/* If there are more dirty bytes than the threshold, GC */
- if (c->fmc->dirty_size > c->gc_maxdirty_threshold) {
+ if (c->fmc->good_dirty_size > c->gc_maxdirty_threshold) {
D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to excessive dirty space\n"));
return 1;
}
@@ -3500,7 +3540,7 @@
if (erased)
goto gc_end;
- if (fmc->free_size == 0) {
+ if (fmc->good_free_size == 0) {
/* Argh. Might as well commit suicide. */
printk(KERN_ERR "jffs_garbage_collect_thread(): free_size == 0. This is BAD.\n");
send_sig(SIGQUIT, c->gc_task, 1);
diff -ur /work/tmp/jffs/fs/jffs/intrep.h /work/projects/jffs/integration/bad/mtd/fs/jffs/intrep.h
--- /work/tmp/jffs/fs/jffs/intrep.h Fri Jan 26 15:29:30 2001
+++ /work/projects/jffs/integration/bad/mtd/fs/jffs/intrep.h Thu Oct 11 11:06:12 2001
@@ -66,11 +66,11 @@
struct jffs_fmcontrol *fmc = c->fmc;
while (1) {
- if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size))
+ if (fmc->good_free_size
>= fmc->min_free_size + space) {
return 1;
}
- if (fmc->dirty_size < fmc->sector_size)
+ if (fmc->good_dirty_size < fmc->sector_size)
return 0;
if (jffs_garbage_collect_now(c)) {
diff -ur /work/tmp/jffs/fs/jffs/jffs_fm.c /work/projects/jffs/integration/bad/mtd/fs/jffs/jffs_fm.c
--- /work/tmp/jffs/fs/jffs/jffs_fm.c Thu Oct 11 11:39:42 2001
+++ /work/projects/jffs/integration/bad/mtd/fs/jffs/jffs_fm.c Thu Oct 11 11:07:45 2001
@@ -15,6 +15,8 @@
* Ported to Linux 2.3.x and MTD:
* Copyright (C) 2000 Alexander Larsson (alex at cendio.se), Cendio Systems AB
*
+ * Portions Copyright 2001 LynuxWorks
+ *
*/
#define __NO_VERSION__
#include <linux/slab.h>
@@ -29,12 +31,328 @@
extern kmem_cache_t *fm_cache;
extern kmem_cache_t *node_cache;
+struct jffs_sector_addr
+{
+ __u32 region;
+ __u32 region_s;
+ __u32 region_b;
+ __u32 sector;
+ __u32 offset;
+ __u32 erasesize;
+};
+
+/* Get information about a sector by it's address.
+ */
+static void jffs_sector_find(struct jffs_sector_addr *sector,
+ struct mtd_info *mtd,
+ u_long addr)
+{
+ struct mtd_erase_region_info *
+ region = mtd->eraseregions;
+ u_long i;
+ u_long j;
+ u_long pos;
+ u_long rsize;
+
+ ASSERT(if (addr > mtd->size)
+ {
+ panic("jffs_sector_find: Invalid addr !");
+ });
+
+ if (addr == mtd->size)
+ {
+ sector->region = 0xFFFFFFFF;
+ sector->region_s = 0xFFFFFFFF;
+ sector->region_b = addr;
+ sector->sector = 0;
+ sector->offset = 0;
+ sector->erasesize = 4;
+ return;
+ }
+
+ for (i = 0, pos = 0, j = 0;
+ i < mtd->numeraseregions;
+ j += region[i].numblocks, i++, pos += rsize, addr -= rsize)
+ {
+ rsize = region[i].erasesize * region[i].numblocks;
+ if (addr < rsize)
+ {
+ break;
+ }
+ }
+
+ sector->region = i;
+ sector->region_s = j;
+ sector->region_b = pos;
+ sector->sector = addr ? addr / region[i].erasesize : 0;
+ sector->offset = addr ? addr % region[i].erasesize : 0;
+ sector->erasesize = region[i].erasesize;
+}
+
+/* Given a sector get information about next sector.
+ */
+static void jffs_sector_next(struct jffs_sector_addr *sector,
+ struct mtd_info *mtd)
+{
+ struct mtd_erase_region_info *
+ region = mtd->eraseregions;
+
+ ASSERT(if (sector->region == mtd->numeraseregions)
+ {
+ panic("jffs_sector_next: Invalid sector !");
+ });
+
+ sector->sector++;
+ if (sector->sector == region[sector->region].numblocks)
+ {
+ sector->sector = 0;
+ sector->region_s += region[sector->region].numblocks;
+ sector->region_b += region[sector->region].numblocks *
+ region[sector->region].erasesize;
+ sector->region++;
+ }
+ sector->offset = 0;
+}
+
+/* Mark a sector bad/good.
+ */
+void jffs_mark_sector(struct jffs_fmcontrol *fmc, u_long offset, int bad)
+{
+ struct mtd_erase_region_info *
+ region = fmc->mtd->eraseregions;
+ struct jffs_sector_addr sector;
+ u_long index;
+ struct jffs_sector_info *
+ blk;
+ char * blkbad;
+ u_long blksize;
+
+ jffs_sector_find(& sector, fmc->mtd, offset);
+ index = sector.region_s + sector.sector;
+ blk = fmc->sector_table + index;
+ blkbad = fmc->bad_block_table + index;
+ blksize = region[sector.region].erasesize;
+
+ if (*blkbad == bad)
+ {
+ D3(if (bad)
+ {
+ printk("Attempt to mark bad a sector that has been "
+ "marked bad already!\n");
+ }
+ else
+ {
+ printk("Attempt to mark good a sector that has been "
+ "marked good already!\n");
+ });
+ }
+ else
+ {
+ *blkbad = bad;
+
+ if (bad)
+ {
+ fmc->bad_size += blksize;
+ fmc->good_free_size -= blk->free_size;
+ fmc->good_dirty_size -= blk->dirty_size;
+ }
+ else
+ {
+ fmc->bad_size -= blksize;
+ fmc->good_free_size += blk->free_size;
+ fmc->good_dirty_size += blk->dirty_size;
+ }
+
+ D3(if (bad)
+ {
+ printk("Block #%ld has been marked bad.\n", index);
+ }
+ else
+ {
+ printk("Block #%ld has been marked good.\n", index);
+ });
+ }
+}
+
+/* Mark Flash space free/dirty/used.
+ */
+void jffs_mark_space(struct jffs_fmcontrol *fmc, u_long addr,
+ u_long size, int dfree, int ddirty, int dused)
+{
+ struct mtd_erase_region_info *
+ region = fmc->mtd->eraseregions;
+ struct jffs_sector_addr sector;
+ struct jffs_sector_info *
+ blk;
+ u_long index;
+ u_long sz;
+ u_long blksize;
+
+ if (size == 0)
+ {
+ return;
+ }
+
+ jffs_sector_find(& sector, fmc->mtd, addr);
+
+ while (1)
+ {
+ index = sector.region_s + sector.sector;
+
+ blk = fmc->sector_table + index;
+ blksize = region[sector.region].erasesize;
+
+ sz = blksize - sector.offset > size ?
+ size : blksize - sector.offset;
+
+ blk->free_size += sz * dfree;
+ blk->dirty_size += sz * ddirty;
+
+ fmc->free_size += sz * dfree;
+ fmc->dirty_size += sz * ddirty;
+ fmc->used_size += sz * dused;
+ if (! fmc->bad_block_table[index])
+ {
+ fmc->good_free_size += sz * dfree;
+ fmc->good_dirty_size += sz * ddirty;
+ }
+
+ size -= sz;
+ if (size == 0)
+ {
+ break;
+ }
+
+ jffs_sector_next(& sector, fmc->mtd);
+ }
+}
+
+/* Return the size of maximum continous space of Flash not marked bad.
+ */
+__u32
+jffs_get_largest_size(struct jffs_fmcontrol *fmc,
+ __u32 start_offset,
+ __u32 end_offset)
+{
+ u_long size = end_offset - start_offset;
+ struct mtd_erase_region_info *
+ region = fmc->mtd->eraseregions;
+ struct jffs_sector_addr sector;
+ u_long index;
+ u_long sz;
+ u_long blksize;
+ u_long largest = 0;
+ u_long good = 0;
+
+ D3(printk("jffs_get_largest_size(start_offset = 0x%.8x "
+ "end_offset = 0x%.8x)\n", start_offset, end_offset);)
+
+ if (size == 0)
+ {
+ D3(printk("jffs_get_largest_size() returning %ld\n", largest));
+ return good;
+ }
+
+ jffs_sector_find(& sector, fmc->mtd, start_offset);
+
+ while (1)
+ {
+ index = sector.region_s + sector.sector;
+
+ blksize = region[sector.region].erasesize;
+
+ sz = blksize - sector.offset > size ?
+ size : blksize - sector.offset;
+
+ if (! fmc->bad_block_table[index])
+ {
+ good += sz;
+ }
+ else
+ {
+ if (good > largest)
+ {
+ largest = good;
+ }
+ good = 0;
+ D(printk("found bad block!!! #%ld\n", index));
+ }
+
+ size -= sz;
+ if (size == 0)
+ {
+ if (good > largest)
+ {
+ largest = good;
+ }
+ break;
+ }
+
+ jffs_sector_next(& sector, fmc->mtd);
+ }
+
+ D3(printk("jffs_get_largest_size() returning %ld\n", largest));
+ return largest;
+}
+
+/* Relocate a Flash block so it does not contain bad space.
+ */
+__u32
+jffs_find_good(struct jffs_fmcontrol *fmc,
+ __u32 offset,
+ __u32 size)
+{
+ struct mtd_erase_region_info *
+ region = fmc->mtd->eraseregions;
+ struct jffs_sector_addr sector;
+ u_long sz;
+ u_long blksize;
+ u_long start = offset;
+ u_long good = 0;
+
+ if (size == 0)
+ {
+ return start;
+ }
+
+ jffs_sector_find(& sector, fmc->mtd, offset);
+
+ while (1)
+ {
+ blksize = region[sector.region].erasesize;
+
+ sz = blksize - sector.offset;
+
+ if (! fmc->bad_block_table[sector.region_s + sector.sector])
+ {
+ good += sz;
+ }
+ else
+ {
+ start = sector.region_b +
+ blksize * sector.sector + blksize;
+ good = 0;
+ }
+
+ if (good >= size)
+ {
+ break;
+ }
+
+ jffs_sector_next(& sector, fmc->mtd);
+ }
+
+ return start;
+}
+
/* This function creates a new shiny flash memory control structure. */
struct jffs_fmcontrol *
jffs_build_begin(struct jffs_control *c, kdev_t dev)
{
struct jffs_fmcontrol *fmc;
struct mtd_info *mtd;
+ u_long i, j;
+ struct jffs_sector_info *pos;
D3(printk("jffs_build_begin()\n"));
fmc = (struct jffs_fmcontrol *)kmalloc(sizeof(struct jffs_fmcontrol),
@@ -58,6 +376,45 @@
fmc->flash_size = mtd->size;
D3(printk(" fmc->flash_size = %d bytes\n", fmc->flash_size));
+ fmc->num_sectors = 0;
+
+ for (i = 0; i < mtd->numeraseregions; i++)
+ {
+ fmc->num_sectors += mtd->eraseregions[i].numblocks;
+ }
+
+ fmc->good_dirty_size = 0;
+ fmc->good_free_size = mtd->size;
+ fmc->bad_size = 0;
+ if ((fmc->bad_block_table = kmalloc(fmc->num_sectors, GFP_KERNEL)) == 0)
+ {
+ printk(KERN_ERR
+ "Could not allocate space for the table of bad sectors\n");
+ kfree(fmc);
+ DJM(no_jffs_fmcontrol--);
+ return NULL;
+ }
+ memset(fmc->bad_block_table, 0, fmc->num_sectors);
+
+ if ((fmc->sector_table = kmalloc(fmc->num_sectors *
+ sizeof(struct jffs_sector_info), GFP_KERNEL)) == 0)
+ {
+ printk(KERN_ERR
+ "Could not allocate space for the table of bad sectors\n");
+ kfree(fmc->bad_block_table);
+ kfree(fmc);
+ DJM(no_jffs_fmcontrol--);
+ return NULL;
+ }
+ for (pos = fmc->sector_table, i = 0; i < mtd->numeraseregions; i++)
+ {
+ for (j = 0; j < mtd->eraseregions[i].numblocks; j++, pos++)
+ {
+ pos->free_size = mtd->eraseregions[i].erasesize;
+ pos->dirty_size = 0;
+ }
+ }
+
fmc->used_size = 0;
fmc->dirty_size = 0;
fmc->free_size = mtd->size;
@@ -142,8 +499,7 @@
cur->next->prev = fm;
cur->next = fm;
cur = fm->next;
- fmc->free_size -= fm->size;
- fmc->dirty_size += fm->size;
+ jffs_mark_space(fmc, fm->offset, fm->size, -1, 1, 0);
}
else if (cur->offset > cur->next->offset)
{
@@ -164,8 +520,7 @@
cur->next->prev = fm;
cur->next = fm;
cur = fm->next;
- fmc->free_size -= fm->size;
- fmc->dirty_size += fm->size;
+ jffs_mark_space(fmc, fm->offset, fm->size, -1, 1, 0);
}
else
{
@@ -186,8 +541,7 @@
fm->prev = cur->prev;
cur->prev->next = fm;
cur->prev = fm;
- fmc->free_size -= fm->size;
- fmc->dirty_size += fm->size;
+ jffs_mark_space(fmc, fm->offset, fm->size, -1, 1, 0);
}
}
else if (cur->offset + cur->size != cur->next->offset)
@@ -222,6 +576,8 @@
jffs_free_fm(cur);
}
put_mtd_device(fmc->mtd);
+ kfree(fmc->sector_table);
+ kfree(fmc->bad_block_table);
kfree(fmc);
DJM(no_jffs_fmcontrol--);
}
@@ -262,6 +618,39 @@
}
}
+__u32
+jffs_free_maxsize1(struct jffs_fmcontrol *fmc)
+{
+ __u32 head;
+ __u32 tail;
+ __u32 end = fmc->flash_size;
+
+ if (!fmc->head) {
+ /* There is nothing on the flash. */
+ return jffs_get_largest_size(fmc, 0, end);
+ }
+
+ /* Compute the beginning and ending of the contents of the flash. */
+ head = fmc->head->offset;
+ tail = fmc->tail->offset + fmc->tail->size;
+
+ if (tail == end) {
+ tail = 0;
+ }
+
+ ASSERT(else if (tail > end) {
+ printk(KERN_WARNING "jffs_free_size1_debug(): tail > end\n");
+ tail = 0;
+ });
+
+ if (head <= tail) {
+ return jffs_get_largest_size(fmc, tail, end);
+ }
+ else {
+ return jffs_get_largest_size(fmc, tail, head);
+ }
+}
+
/* This function will return something nonzero in case there are two free
areas on the flash. Like this:
@@ -290,6 +679,23 @@
return 0;
}
+__u32
+jffs_free_maxsize2(struct jffs_fmcontrol *fmc)
+{
+ if (fmc->head) {
+ __u32 head = fmc->head->offset;
+ __u32 tail = fmc->tail->offset + fmc->tail->size;
+ if (tail == fmc->flash_size) {
+ tail = 0;
+ }
+
+ if (tail >= head)
+ {
+ return jffs_get_largest_size(fmc, 0, head);
+ }
+ }
+ return 0;
+}
/* Allocate a chunk of flash memory. If there is enough space on the
device, a reference to the associated node is stored in the jffs_fm
@@ -323,7 +729,8 @@
"free_chunk_size2 = %u\n",
free_chunk_size1, free_chunk_size2));
- if (size <= free_chunk_size1) {
+ if (size <= jffs_free_maxsize1(fmc)) {
+ __u32 good_offset;
if (!(fm->nodes = (struct jffs_node_ref *)
kmalloc(sizeof(struct jffs_node_ref),
GFP_KERNEL))) {
@@ -352,10 +759,27 @@
fm->offset = 0;
}
fm->size = size;
- fmc->free_size -= size;
- fmc->used_size += size;
+ good_offset = jffs_find_good(fmc, fm->offset, fm->size);
+ if (good_offset != fm->offset)
+ {
+ /* fm contains bad blocks
+ */
+ fm->size = good_offset - fm->offset;
+ DJM(no_jffs_node_ref--);
+ kfree(fm->nodes);
+ fm->nodes = 0;
+ }
+
+ if (fm->nodes)
+ {
+ jffs_mark_space(fmc, fm->offset, fm->size, -1, 0, 1);
+ }
+ else
+ {
+ jffs_mark_space(fmc, fm->offset, fm->size, -1, 1, 0);
+ }
}
- else if (size > free_chunk_size2) {
+ else if (size > jffs_free_maxsize2(fmc)) {
printk(KERN_WARNING "JFFS: Tried to allocate a too "
"large flash memory chunk. (size = %u)\n", size);
jffs_free_fm(fm);
@@ -365,12 +789,7 @@
fm->offset = fmc->tail->offset + fmc->tail->size;
fm->size = free_chunk_size1;
fm->nodes = 0;
- fmc->free_size -= fm->size;
- fmc->dirty_size += fm->size; /* Changed by simonk. This seemingly fixes a
- bug that caused infinite garbage collection.
- It previously set fmc->dirty_size to size (which is the
- size of the requested chunk).
- */
+ jffs_mark_space(fmc, fm->offset, fm->size, -1, 1, 0);
}
fm->next = 0;
@@ -435,8 +854,7 @@
just mark it as obsolete. */
if (!fm->nodes) {
/* No node uses this chunk so let's remove it. */
- fmc->used_size -= fm->size;
- fmc->dirty_size += fm->size;
+ jffs_mark_space(fmc, fm->offset, fm->size, 0, 1, -1);
#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE
if (jffs_mark_obsolete(fmc, fm->offset) < 0) {
D1(printk("jffs_fmfree(): Failed to mark an on-flash "
@@ -489,13 +907,11 @@
DJM(no_jffs_node_ref++);
fm->nodes->node = node;
fm->nodes->next = 0;
- fmc->used_size += size;
- fmc->free_size -= size;
+ jffs_mark_space(fmc, offset, size, -1, 0, 1);
}
else {
/* If there is no node, then this is just a chunk of dirt. */
- fmc->dirty_size += size;
- fmc->free_size -= size;
+ jffs_mark_space(fmc, offset, size, -1, 1, 0);
}
if (fmc->head_extra) {
@@ -524,7 +940,7 @@
/* Add a new node to an already existing jffs_fm struct. */
int
-jffs_add_node(struct jffs_node *node)
+jffs_add_node(struct jffs_fmcontrol *fmc, struct jffs_node *node)
{
struct jffs_node_ref *ref;
@@ -535,6 +951,12 @@
if (!ref)
return -ENOMEM;
+ if (!node->fm->nodes)
+ {
+ jffs_mark_space(fmc, node->fm->offset, node->fm->size,
+ 0, -1, 1);
+ }
+
DJM(no_jffs_node_ref++);
ref->node = node;
ref->next = node->fm->nodes;
@@ -553,16 +975,21 @@
(!fm ? 0 : (!fm->nodes ? 0 : fm->nodes->node->ino)), size));
if (fm->nodes) {
+ jffs_mark_space(fmc, fm->offset, fm->size, 0, 0, -1);
+ } else {
+ jffs_mark_space(fmc, fm->offset, fm->size, 0, -1, 0);
+ }
+
+ if (fm->nodes) {
kfree(fm->nodes);
DJM(no_jffs_node_ref--);
fm->nodes = 0;
}
- fmc->used_size -= fm->size;
if (fm == fmc->tail) {
fm->size -= size;
- fmc->free_size += size;
+ jffs_mark_space(fmc, fm->offset + fm->size, size, 1, 0, 0);
}
- fmc->dirty_size += fm->size;
+ jffs_mark_space(fmc, fm->offset, fm->size, 0, 1, 0);
}
@@ -617,11 +1044,9 @@
return;
});
- fmc->dirty_size -= erased_size;
- fmc->free_size += erased_size;
-
for (fm = fmc->head; fm && (erased_size > 0);) {
if (erased_size >= fm->size) {
+ jffs_mark_space(fmc, fm->offset, fm->size, 1, -1, 0);
erased_size -= fm->size;
del = fm;
fm = fm->next;
@@ -630,6 +1055,7 @@
jffs_free_fm(del);
}
else {
+ jffs_mark_space(fmc, fm->offset, erased_size, 1, -1, 0);
fm->size -= erased_size;
fm->offset += erased_size;
break;
@@ -854,6 +1280,7 @@
D(printk(" %u, /* sector_size */\n", fmc->sector_size));
D(printk(" %u, /* min_free_size */\n", fmc->min_free_size));
D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size));
+ D(printk(" %u, /* bad_size */\n", fmc->bad_size));
D(printk(" 0x%p, /* mtd */\n", fmc->mtd));
D(printk(" 0x%p, /* head */ "
"(head->offset = 0x%08x)\n",
diff -ur /work/tmp/jffs/fs/jffs/jffs_fm.h /work/projects/jffs/integration/bad/mtd/fs/jffs/jffs_fm.h
--- /work/tmp/jffs/fs/jffs/jffs_fm.h Thu Oct 11 11:39:48 2001
+++ /work/projects/jffs/integration/bad/mtd/fs/jffs/jffs_fm.h Thu Oct 11 10:59:12 2001
@@ -85,6 +85,12 @@
struct jffs_node_ref *nodes; /* USED if != 0. */
};
+struct jffs_sector_info
+{
+ __u32 dirty_size;
+ __u32 free_size;
+};
+
struct jffs_fmcontrol
{
__u32 flash_size;
@@ -95,6 +101,12 @@
__u32 min_free_size; /* The minimum free space needed to be able
to perform garbage collections. */
__u32 max_chunk_size; /* The maximum size of a chunk of data. */
+ __u32 good_free_size;
+ __u32 good_dirty_size;
+ __u32 bad_size;
+ char* bad_block_table;
+ struct jffs_sector_info *sector_table;
+ int num_sectors;
struct mtd_info *mtd;
struct jffs_control *c;
struct jffs_fm *head;
@@ -133,13 +145,21 @@
__u32 jffs_free_size1(struct jffs_fmcontrol *fmc);
__u32 jffs_free_size2(struct jffs_fmcontrol *fmc);
+__u32 jffs_free_maxsize1(struct jffs_fmcontrol *fmc);
+__u32 jffs_free_maxsize2(struct jffs_fmcontrol *fmc);
+__u32 jffs_get_largest_size(struct jffs_fmcontrol * mtd,
+ __u32 start_offset,
+ __u32 end_offset);
+void jffs_mark_sector(struct jffs_fmcontrol *fmc, u_long offset, int bad);
+void jffs_mark_space(struct jffs_fmcontrol *fmc, u_long addr,
+ u_long size, int dfree, int ddirty, int dused);
void jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size);
struct jffs_fm *jffs_cut_node(struct jffs_fmcontrol *fmc, __u32 size);
struct jffs_node *jffs_get_oldest_node(struct jffs_fmcontrol *fmc);
long jffs_erasable_size(struct jffs_fmcontrol *fmc);
struct jffs_fm *jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset,
__u32 size, struct jffs_node *node);
-int jffs_add_node(struct jffs_node *node);
+int jffs_add_node(struct jffs_fmcontrol *fmc, struct jffs_node *node);
void jffs_fmfree_partly(struct jffs_fmcontrol *fmc, struct jffs_fm *fm,
__u32 size);
diff -ur /work/tmp/jffs/include/linux/jffs.h /work/projects/jffs/integration/bad/mtd/include/linux/jffs.h
--- /work/tmp/jffs/include/linux/jffs.h Thu Oct 11 11:37:12 2001
+++ /work/projects/jffs/integration/bad/mtd/include/linux/jffs.h Thu Oct 11 10:59:53 2001
@@ -49,11 +49,19 @@
value. */
#define JFFS_MAX_NAME_LEN 254
+struct jffs_bad_table
+{
+ char * bad_block_table;
+ int num_sectors;
+};
+
/* Commands for ioctl(). */
#define JFFS_IOCTL_MAGIC 't'
#define JFFS_PRINT_HASH _IO(JFFS_IOCTL_MAGIC, 90)
#define JFFS_PRINT_TREE _IO(JFFS_IOCTL_MAGIC, 91)
#define JFFS_GET_STATUS _IO(JFFS_IOCTL_MAGIC, 92)
+
+#define JFFS_GET_BAD_TABLE _IOWR(JFFS_IOCTL_MAGIC, 93, struct jffs_bad_table)
/* XXX: This is something that we should try to get rid of in the future. */
#define JFFS_MODIFY_INODE 0x01
More information about the linux-mtd
mailing list