Пример #1
0
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
	int i, ret;
	uint32_t empty_blocks = 0, bad_blocks = 0;
	unsigned char *flashbuf = NULL;
	uint32_t buf_size = 0;
	struct jffs2_summary *s = NULL; /* summary info collected by the scan process */
#ifndef __ECOS
	size_t pointlen;

	if (c->mtd->point) {
		ret = c->mtd->point(c->mtd, 0, c->mtd->size, &pointlen,
				    (void **)&flashbuf, NULL);
		if (!ret && pointlen < c->mtd->size) {
			/* Don't muck about if it won't let us point to the whole flash */
			D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
			c->mtd->unpoint(c->mtd, 0, pointlen);
			flashbuf = NULL;
		}
		if (ret)
			D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
	}
#endif
	if (!flashbuf) {
		/* For NAND it's quicker to read a whole eraseblock at a time,
		   apparently */
		if (c->mtd->type == MTD_NANDFLASH)
			buf_size = c->sector_size;
		else
			buf_size = PAGE_SIZE;

		/* Respect kmalloc limitations */
		if (buf_size > 128*1024)
			buf_size = 128*1024;

		D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
		flashbuf = kmalloc(buf_size, GFP_KERNEL);
		if (!flashbuf)
			return -ENOMEM;
	}

	if (jffs2_sum_active()) {
		s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
		if (!s) {
			JFFS2_WARNING("Can't allocate memory for summary\n");
			ret = -ENOMEM;
			goto out;
		}
	}

	for (i=0; i<c->nr_blocks; i++) {
		struct jffs2_eraseblock *jeb = &c->blocks[i];

		cond_resched();

		/* reset summary info for next eraseblock scan */
		jffs2_sum_reset_collected(s);

		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
						buf_size, s);

		if (ret < 0)
			goto out;

		jffs2_dbg_acct_paranoia_check_nolock(c, jeb);

		/* Now decide which list to put it on */
		switch(ret) {
		case BLK_STATE_ALLFF:
			/*
			 * Empty block.   Since we can't be sure it
			 * was entirely erased, we just queue it for erase
			 * again.  It will be marked as such when the erase
			 * is complete.  Meanwhile we still count it as empty
			 * for later checks.
			 */
			empty_blocks++;
			list_add(&jeb->list, &c->erase_pending_list);
			c->nr_erasing_blocks++;
			break;

		case BLK_STATE_CLEANMARKER:
			/* Only a CLEANMARKER node is valid */
			if (!jeb->dirty_size) {
				/* It's actually free */
				list_add(&jeb->list, &c->free_list);
				c->nr_free_blocks++;
			} else {
				/* Dirt */
				D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset));
				list_add(&jeb->list, &c->erase_pending_list);
				c->nr_erasing_blocks++;
			}
			break;

		case BLK_STATE_CLEAN:
			/* Full (or almost full) of clean data. Clean list */
			list_add(&jeb->list, &c->clean_list);
			break;

		case BLK_STATE_PARTDIRTY:
			/* Some data, but not full. Dirty list. */
			/* We want to remember the block with most free space
			and stick it in the 'nextblock' position to start writing to it. */
			if (jeb->free_size > min_free(c) &&
					(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
				/* Better candidate for the next writes to go to */
				if (c->nextblock) {
					ret = file_dirty(c, c->nextblock);
					if (ret)
						goto out;
					/* deleting summary information of the old nextblock */
					jffs2_sum_reset_collected(c->summary);
				}
				/* update collected summary information for the current nextblock */
				jffs2_sum_move_collected(c, s);
				D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset));
				c->nextblock = jeb;
			} else {
				ret = file_dirty(c, jeb);
				if (ret)
					goto out;
			}
			break;

		case BLK_STATE_ALLDIRTY:
			/* Nothing valid - not even a clean marker. Needs erasing. */
			/* For now we just put it on the erasing list. We'll start the erases later */
			D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
			list_add(&jeb->list, &c->erase_pending_list);
			c->nr_erasing_blocks++;
			break;

		case BLK_STATE_BADBLOCK:
			D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
			list_add(&jeb->list, &c->bad_list);
			c->bad_size += c->sector_size;
			c->free_size -= c->sector_size;
			bad_blocks++;
			break;
		default:
			printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
			BUG();
		}
	}

	/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
	if (c->nextblock && (c->nextblock->dirty_size)) {
		c->nextblock->wasted_size += c->nextblock->dirty_size;
		c->wasted_size += c->nextblock->dirty_size;
		c->dirty_size -= c->nextblock->dirty_size;
		c->nextblock->dirty_size = 0;
	}
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
	if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) {
		/* If we're going to start writing into a block which already
		   contains data, and the end of the data isn't page-aligned,
		   skip a little and align it. */

		uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize;

		D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
			  skip));
		jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
		jffs2_scan_dirty_space(c, c->nextblock, skip);
	}
#endif
	if (c->nr_erasing_blocks) {
		if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
			printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
			printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
			ret = -EIO;
			goto out;
		}
		spin_lock(&c->erase_completion_lock);
		jffs2_garbage_collect_trigger(c);
		spin_unlock(&c->erase_completion_lock);
	}
	ret = 0;
 out:
	if (buf_size)
		kfree(flashbuf);
#ifndef __ECOS
	else
		c->mtd->unpoint(c->mtd, 0, c->mtd->size);
#endif
	if (s)
		kfree(s);

	return ret;
}
Пример #2
0
int jffs2_scan_medium(struct jffs2_sb_info *c)
{
	int i, ret;
	uint32_t empty_blocks = 0, bad_blocks = 0;
	unsigned char *flashbuf = NULL;
	uint32_t buf_size = 0;
	struct jffs2_summary *s = NULL; 
#ifndef __ECOS
	size_t pointlen, try_size;

	ret = mtd_point(c->mtd, 0, c->mtd->size, &pointlen,
			(void **)&flashbuf, NULL);
	if (!ret && pointlen < c->mtd->size) {
		
		jffs2_dbg(1, "MTD point returned len too short: 0x%zx\n",
			  pointlen);
		mtd_unpoint(c->mtd, 0, pointlen);
		flashbuf = NULL;
	}
	if (ret && ret != -EOPNOTSUPP)
		jffs2_dbg(1, "MTD point failed %d\n", ret);
#endif
	if (!flashbuf) {
		if (jffs2_cleanmarker_oob(c))
			try_size = c->sector_size;
		else
			try_size = PAGE_SIZE;

		jffs2_dbg(1, "Trying to allocate readbuf of %zu "
			  "bytes\n", try_size);

		flashbuf = mtd_kmalloc_up_to(c->mtd, &try_size);
		if (!flashbuf)
			return -ENOMEM;

		jffs2_dbg(1, "Allocated readbuf of %zu bytes\n",
			  try_size);

		buf_size = (uint32_t)try_size;
	}

	if (jffs2_sum_active()) {
		s = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
		if (!s) {
			JFFS2_WARNING("Can't allocate memory for summary\n");
			ret = -ENOMEM;
			goto out;
		}
	}

	for (i=0; i<c->nr_blocks; i++) {
		struct jffs2_eraseblock *jeb = &c->blocks[i];

		cond_resched();

		
		jffs2_sum_reset_collected(s);

		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
						buf_size, s);

		if (ret < 0)
			goto out;

		jffs2_dbg_acct_paranoia_check_nolock(c, jeb);

		
		switch(ret) {
		case BLK_STATE_ALLFF:
			empty_blocks++;
			list_add(&jeb->list, &c->erase_pending_list);
			c->nr_erasing_blocks++;
			break;

		case BLK_STATE_CLEANMARKER:
			
			if (!jeb->dirty_size) {
				
				list_add(&jeb->list, &c->free_list);
				c->nr_free_blocks++;
			} else {
				
				jffs2_dbg(1, "Adding all-dirty block at 0x%08x to erase_pending_list\n",
					  jeb->offset);
				list_add(&jeb->list, &c->erase_pending_list);
				c->nr_erasing_blocks++;
			}
			break;

		case BLK_STATE_CLEAN:
			
			list_add(&jeb->list, &c->clean_list);
			break;

		case BLK_STATE_PARTDIRTY:
			
			if (jeb->free_size > min_free(c) &&
					(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
				
				if (c->nextblock) {
					ret = file_dirty(c, c->nextblock);
					if (ret)
						goto out;
					
					jffs2_sum_reset_collected(c->summary);
				}
				
				jffs2_sum_move_collected(c, s);
				jffs2_dbg(1, "%s(): new nextblock = 0x%08x\n",
					  __func__, jeb->offset);
				c->nextblock = jeb;
			} else {
				ret = file_dirty(c, jeb);
				if (ret)
					goto out;
			}
			break;

		case BLK_STATE_ALLDIRTY:
			
			
			jffs2_dbg(1, "Erase block at 0x%08x is not formatted. It will be erased\n",
				  jeb->offset);
			list_add(&jeb->list, &c->erase_pending_list);
			c->nr_erasing_blocks++;
			break;

		case BLK_STATE_BADBLOCK:
			jffs2_dbg(1, "Block at 0x%08x is bad\n", jeb->offset);
			list_add(&jeb->list, &c->bad_list);
			c->bad_size += c->sector_size;
			c->free_size -= c->sector_size;
			bad_blocks++;
			break;
		default:
			pr_warn("%s(): unknown block state\n", __func__);
			BUG();
		}
	}

	
	if (c->nextblock && (c->nextblock->dirty_size)) {
		c->nextblock->wasted_size += c->nextblock->dirty_size;
		c->wasted_size += c->nextblock->dirty_size;
		c->dirty_size -= c->nextblock->dirty_size;
		c->nextblock->dirty_size = 0;
	}
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
	if (!jffs2_can_mark_obsolete(c) && c->wbuf_pagesize && c->nextblock && (c->nextblock->free_size % c->wbuf_pagesize)) {

		uint32_t skip = c->nextblock->free_size % c->wbuf_pagesize;

		jffs2_dbg(1, "%s(): Skipping %d bytes in nextblock to ensure page alignment\n",
			  __func__, skip);
		jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
		jffs2_scan_dirty_space(c, c->nextblock, skip);
	}
#endif
	if (c->nr_erasing_blocks) {
		if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) {
			pr_notice("Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
			pr_notice("empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",
				  empty_blocks, bad_blocks, c->nr_blocks);
			ret = -EIO;
			goto out;
		}
		spin_lock(&c->erase_completion_lock);
		jffs2_garbage_collect_trigger(c);
		spin_unlock(&c->erase_completion_lock);
	}
	ret = 0;
 out:
	if (buf_size)
		kfree(flashbuf);
#ifndef __ECOS
	else
		mtd_unpoint(c->mtd, 0, c->mtd->size);
#endif
	kfree(s);
	return ret;
}