static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t infosize, uint32_t datasize, int padsize) { struct jffs2_raw_summary isum; union jffs2_sum_mem *temp; struct jffs2_sum_marker *sm; struct kvec vecs[2]; uint32_t sum_ofs; void *wpage; int ret; size_t retlen; if (padsize + datasize > MAX_SUMMARY_SIZE) { /* It won't fit in the buffer. Abort summary for this jeb */ jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Summary too big (%d data, %d pad) in eraseblock at %08x\n", datasize, padsize, jeb->offset); /* Non-fatal */ return 0; } /* Is there enough space for summary? */ if (padsize < 0) { /* don't try to write out summary for this jeb */ jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize); /* Non-fatal */ return 0; } memset(c->summary->sum_buf, 0xff, datasize); memset(&isum, 0, sizeof(isum)); isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); isum.totlen = cpu_to_je32(infosize); isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); isum.padded = cpu_to_je32(c->summary->sum_padded); isum.cln_mkr = cpu_to_je32(c->cleanmarker_size); isum.sum_num = cpu_to_je32(c->summary->sum_num); wpage = c->summary->sum_buf; while (c->summary->sum_num) { temp = c->summary->sum_list_head; switch (je16_to_cpu(temp->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *sino_ptr = wpage; sino_ptr->nodetype = temp->i.nodetype; sino_ptr->inode = temp->i.inode; sino_ptr->version = temp->i.version; sino_ptr->offset = temp->i.offset; sino_ptr->totlen = temp->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; sdrnt_ptr->nodetype = temp->d.nodetype; sdrnt_ptr->totlen = temp->d.totlen; sdrnt_ptr->offset = temp->d.offset; sdrnt_ptr->pino = temp->d.pino; sdrnt_ptr->version = temp->d.version; sdrnt_ptr->ino = temp->d.ino; sdrnt_ptr->nsize = temp->d.nsize; sdrnt_ptr->type = temp->d.type; memcpy(sdrnt_ptr->name, temp->d.name, temp->d.nsize); wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize); break; } #ifdef CONFIG_JFFS2_FS_XATTR case JFFS2_NODETYPE_XATTR: { struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; temp = c->summary->sum_list_head; sxattr_ptr->nodetype = temp->x.nodetype; sxattr_ptr->xid = temp->x.xid; sxattr_ptr->version = temp->x.version; sxattr_ptr->offset = temp->x.offset; sxattr_ptr->totlen = temp->x.totlen; wpage += JFFS2_SUMMARY_XATTR_SIZE; break; } case JFFS2_NODETYPE_XREF: { struct jffs2_sum_xref_flash *sxref_ptr = wpage; temp = c->summary->sum_list_head; sxref_ptr->nodetype = temp->r.nodetype; sxref_ptr->offset = temp->r.offset; wpage += JFFS2_SUMMARY_XREF_SIZE; break; } #endif default : { if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_RWCOMPAT_COPY) { dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n", je16_to_cpu(temp->u.nodetype)); jffs2_sum_disable_collecting(c->summary); } else { BUG(); /* unknown node in summary information */ } } } c->summary->sum_list_head = temp->u.next; kfree(temp); c->summary->sum_num--; } jffs2_sum_reset_collected(c->summary); wpage += padsize; sm = wpage; sm->offset = cpu_to_je32(c->sector_size - jeb->free_size); sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC); isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize)); isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); vecs[0].iov_base = &isum; vecs[0].iov_len = sizeof(isum); vecs[1].iov_base = c->summary->sum_buf; vecs[1].iov_len = datasize; sum_ofs = jeb->offset + c->sector_size - jeb->free_size; dbg_summary("writing out data to flash to pos : 0x%08x\n", sum_ofs); ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0); if (ret || (retlen != infosize)) { JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n", infosize, sum_ofs, ret, retlen); if (retlen) { /* Waste remaining space */ spin_lock(&c->erase_completion_lock); jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL); spin_unlock(&c->erase_completion_lock); } c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; return 0; } spin_lock(&c->erase_completion_lock); jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL); spin_unlock(&c->erase_completion_lock); 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, (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; }
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; }
static int jffs2_find_nextblock(struct jffs2_sb_info *c) { 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_find_nextblock: 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_find_nextblock: 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 = list_entry(next, struct jffs2_eraseblock, list); c->nr_free_blocks--; jffs2_sum_reset_collected(c->summary); /* reset collected summary */ D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset)); return 0; }
static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t infosize, uint32_t datasize, int padsize) { struct jffs2_raw_summary isum; union jffs2_sum_mem *temp; struct jffs2_sum_marker *sm; struct kvec vecs[2]; void *wpage; int ret; size_t retlen; memset(c->summary->sum_buf, 0xff, datasize); memset(&isum, 0, sizeof(isum)); isum.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); isum.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY); isum.totlen = cpu_to_je32(infosize); isum.hdr_crc = cpu_to_je32(crc32(0, &isum, sizeof(struct jffs2_unknown_node) - 4)); isum.padded = cpu_to_je32(c->summary->sum_padded); isum.cln_mkr = cpu_to_je32(c->cleanmarker_size); isum.sum_num = cpu_to_je32(c->summary->sum_num); wpage = c->summary->sum_buf; while (c->summary->sum_num) { switch (je16_to_cpu(c->summary->sum_list_head->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *sino_ptr = wpage; sino_ptr->nodetype = c->summary->sum_list_head->i.nodetype; sino_ptr->inode = c->summary->sum_list_head->i.inode; sino_ptr->version = c->summary->sum_list_head->i.version; sino_ptr->offset = c->summary->sum_list_head->i.offset; sino_ptr->totlen = c->summary->sum_list_head->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; break; } case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; sdrnt_ptr->nodetype = c->summary->sum_list_head->d.nodetype; sdrnt_ptr->totlen = c->summary->sum_list_head->d.totlen; sdrnt_ptr->offset = c->summary->sum_list_head->d.offset; sdrnt_ptr->pino = c->summary->sum_list_head->d.pino; sdrnt_ptr->version = c->summary->sum_list_head->d.version; sdrnt_ptr->ino = c->summary->sum_list_head->d.ino; sdrnt_ptr->nsize = c->summary->sum_list_head->d.nsize; sdrnt_ptr->type = c->summary->sum_list_head->d.type; memcpy(sdrnt_ptr->name, c->summary->sum_list_head->d.name, c->summary->sum_list_head->d.nsize); wpage += JFFS2_SUMMARY_DIRENT_SIZE(c->summary->sum_list_head->d.nsize); break; } default : { BUG(); /* unknown node in summary information */ } } temp = c->summary->sum_list_head; c->summary->sum_list_head = c->summary->sum_list_head->u.next; kfree(temp); c->summary->sum_num--; } jffs2_sum_reset_collected(c->summary); wpage += padsize; sm = wpage; sm->offset = cpu_to_je32(c->sector_size - jeb->free_size); sm->magic = cpu_to_je32(JFFS2_SUM_MAGIC); isum.sum_crc = cpu_to_je32(crc32(0, c->summary->sum_buf, datasize)); isum.node_crc = cpu_to_je32(crc32(0, &isum, sizeof(isum) - 8)); vecs[0].iov_base = &isum; vecs[0].iov_len = sizeof(isum); vecs[1].iov_base = c->summary->sum_buf; vecs[1].iov_len = datasize; dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n", jeb->offset + c->sector_size - jeb->free_size); spin_unlock(&c->erase_completion_lock); ret = jffs2_flash_writev(c, vecs, 2, jeb->offset + c->sector_size - jeb->free_size, &retlen, 0); spin_lock(&c->erase_completion_lock); if (ret || (retlen != infosize)) { JFFS2_WARNING("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", infosize, jeb->offset + c->sector_size - jeb->free_size, ret, retlen); c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; WASTED_SPACE(infosize); return 1; } return 0; }