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