static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->dirty_list); } } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->clean_list); } c->nextblock = NULL; }
static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1))) return ret; if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size))) return ret; jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } return 0; }
static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { int ret; if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1))) return ret; if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size))) return ret; /* Turned wasted size into dirty, since we apparently think it's recoverable now. */ jeb->dirty_size += jeb->wasted_size; c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } return 0; }
/* Called with alloc sem _and_ erase_completion_lock */ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) { struct jffs2_eraseblock *jeb = c->nextblock; restart: if (jeb && minsize > jeb->free_size) { /* Skip the end of this block and file it as having some dirty space */ /* If there's a pending write to it, flush now */ if (jffs2_wbuf_dirty(c)) { spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); jeb = c->nextblock; goto restart; } c->wasted_size += jeb->free_size; c->free_size -= jeb->free_size; jeb->wasted_size += jeb->free_size; jeb->free_size = 0; /* Check, if we have a dirty block now, or if it was dirty already */ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { c->dirty_size += jeb->wasted_size; c->wasted_size -= jeb->wasted_size; jeb->dirty_size += jeb->wasted_size; jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->very_dirty_list); } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->dirty_list); } } else { D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); list_add_tail(&jeb->list, &c->clean_list); } c->nextblock = jeb = NULL; } if (!jeb) { struct list_head *next; /* Take the next block off the 'free' list */ if (list_empty(&c->free_list)) { if (!c->nr_erasing_blocks && !list_empty(&c->erasable_list)) { struct jffs2_eraseblock *ejeb; ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); list_del(&ejeb->list); list_add_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } if (!c->nr_erasing_blocks && !list_empty(&c->erasable_pending_wbuf_list)) { D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); /* c->nextblock is NULL, no update to c->nextblock allowed */ spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf_pad(c); spin_lock(&c->erase_completion_lock); /* Have another go. It'll be on the erasable_list now */ return -EAGAIN; } if (!c->nr_erasing_blocks) { /* Ouch. We're in GC, or we wouldn't have got here. And there's no space left. At all. */ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); return -ENOSPC; } spin_unlock(&c->erase_completion_lock); /* Don't wait for it; just erase one right now */ jffs2_erase_pending_blocks(c, 1); spin_lock(&c->erase_completion_lock); /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ return -EAGAIN; } next = c->free_list.next; list_del(next); c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list); c->nr_free_blocks--; if (jeb->free_size != c->sector_size - c->cleanmarker_size) { printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); goto restart; } } /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has enough space */ *ofs = jeb->offset + (c->sector_size - jeb->free_size); *len = jeb->free_size; if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && !jeb->first_node->next_in_ino) { /* Only node in it beforehand was a CLEANMARKER node (we think). So mark it obsolete now that there's going to be another node in the block. This will reduce used_size to zero but We've already set c->nextblock so that jffs2_mark_node_obsolete() won't try to refile it to the dirty_list. */ spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, jeb->first_node); spin_lock(&c->erase_completion_lock); } D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); return 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; #ifndef __ECOS size_t pointlen; if (c->mtd->point) { ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf); 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, flashbuf, 0, c->mtd->size); 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 (jffs2_cleanmarker_oob(c)) buf_size = c->sector_size; else buf_size = PAGE_SIZE; D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); flashbuf = kmalloc(buf_size, GFP_KERNEL); if (!flashbuf) return -ENOMEM; } for (i=0; i<c->nr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); if (ret < 0) return ret; ACCT_PARANOIA_CHECK(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. */ /* Except that we want to remember the block with most free space, and stick it in the 'nextblock' position to start writing to it. Later when we do snapshots, this must be the most recent block, not the one with most free space. */ if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && (jffs2_can_mark_obsolete(c) || jeb->free_size > c->wbuf_pagesize) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ if (c->nextblock) { c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->free_size -= c->nextblock->free_size; c->wasted_size -= c->nextblock->wasted_size; c->nextblock->free_size = c->nextblock->wasted_size = 0; if (VERYDIRTY(c, c->nextblock->dirty_size)) { list_add(&c->nextblock->list, &c->very_dirty_list); } else { list_add(&c->nextblock->list, &c->dirty_list); } } c->nextblock = jeb; } else { jeb->dirty_size += jeb->free_size + jeb->wasted_size; c->dirty_size += jeb->free_size + jeb->wasted_size; c->free_size -= jeb->free_size; c->wasted_size -= jeb->wasted_size; jeb->free_size = jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } } 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; } if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { /* 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-1); D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", skip)); c->nextblock->wasted_size += skip; c->wasted_size += skip; c->nextblock->free_size -= skip; c->free_size -= skip; } if (c->nr_erasing_blocks) { if ( !c->used_size && ((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); return -EIO; } jffs2_erase_pending_trigger(c); } if (buf_size) kfree(flashbuf); #ifndef __ECOS else c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); #endif return 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, &flashbuf); 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, flashbuf, 0, c->mtd->size); 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 (jffs2_cleanmarker_oob(c)) 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 = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL); if (!s) { JFFS2_WARNING("Can't allocate memory for summary\n"); return -ENOMEM; } memset(s, 0, sizeof(struct jffs2_summary)); } for (i=0; i<c->nr_blocks; i++) { struct jffs2_eraseblock *jeb = &c->blocks[i]; /* 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) { c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; c->free_size -= c->nextblock->free_size; c->wasted_size -= c->nextblock->wasted_size; c->nextblock->free_size = c->nextblock->wasted_size = 0; if (VERYDIRTY(c, c->nextblock->dirty_size)) { list_add(&c->nextblock->list, &c->very_dirty_list); } else { list_add(&c->nextblock->list, &c->dirty_list); } /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } /* update collected summary infromation 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 { jeb->dirty_size += jeb->free_size + jeb->wasted_size; c->dirty_size += jeb->free_size + jeb->wasted_size; c->free_size -= jeb->free_size; c->wasted_size -= jeb->wasted_size; jeb->free_size = jeb->wasted_size = 0; if (VERYDIRTY(c, jeb->dirty_size)) { list_add(&jeb->list, &c->very_dirty_list); } else { list_add(&jeb->list, &c->dirty_list); } } 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(); } } if (jffs2_sum_active() && s) kfree(s); /* 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->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)); c->nextblock->wasted_size += skip; c->wasted_size += skip; c->nextblock->free_size -= skip; c->free_size -= 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; } jffs2_erase_pending_trigger(c); } ret = 0; out: if (buf_size) kfree(flashbuf); #ifndef __ECOS else c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); #endif return ret; }