static void jffs2_erase_callback(struct erase_info *instr) { struct erase_priv_struct *priv = (void *)instr->priv; if(instr->state != MTD_ERASE_DONE) { printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); spin_lock(&priv->c->erase_completion_lock); priv->c->erasing_size -= priv->c->sector_size; priv->c->bad_size += priv->c->sector_size; list_del(&priv->jeb->list); list_add(&priv->jeb->list, &priv->c->bad_list); priv->c->nr_erasing_blocks--; spin_unlock(&priv->c->erase_completion_lock); wake_up(&priv->c->erase_wait); } else { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr)); spin_lock(&priv->c->erase_completion_lock); list_del(&priv->jeb->list); list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list); spin_unlock(&priv->c->erase_completion_lock); } /* Make sure someone picks up the block off the erase_complete list */ OFNI_BS_2SFFJ(priv->c)->s_dirt = 1; kfree(instr); }
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) { struct super_block *sb=OFNI_BS_2SFFJ(c); CYG_ASSERTC(sb->s_gc_thread_handle); D1(printk("jffs2_stop_garbage_collect_thread\n")); /* Stop the thread and wait for it if necessary */ cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_STOP); D1(printk("jffs2_stop_garbage_collect_thread wait\n")); cyg_flag_wait(&sb->s_gc_thread_flags, GC_THREAD_FLAG_HAS_EXIT, CYG_FLAG_WAITMODE_OR| CYG_FLAG_WAITMODE_CLR); // Kill and free the resources ... this is safe due to the flag // from the thread. cyg_thread_kill(sb->s_gc_thread_handle); cyg_thread_delete(sb->s_gc_thread_handle); cyg_mutex_destroy(&sb->s_lock); cyg_flag_destroy(&sb->s_gc_thread_flags); }
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) { struct super_block *sb=OFNI_BS_2SFFJ(c); cyg_mtab_entry *mte; rt_uint32_t e; //RT_ASSERT(sb->s_gc_thread_handle); D1(printk("jffs2_stop_garbage_collect_thread\n")); /* Stop the thread and wait for it if necessary */ rt_event_send(&sb->s_gc_thread_flags,GC_THREAD_FLAG_STOP); D1(printk("jffs2_stop_garbage_collect_thread wait\n")); rt_event_recv(&sb->s_gc_thread_flags, GC_THREAD_FLAG_HAS_EXIT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e); // Kill and free the resources ... this is safe due to the flag // from the thread. rt_thread_detach(&sb->s_gc_thread); rt_sem_detach(&sb->s_lock); rt_event_detach(&sb->s_gc_thread_flags); }
int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint16_t comprtype, unsigned char *cdata_in, unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) { struct super_block *sb = OFNI_BS_2SFFJ(c); rtems_jffs2_compressor_control *cc = sb->s_compressor_control; /* Older code had a bug where it would write non-zero 'usercompr' fields. Deal with it. */ if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) comprtype &= 0xff; switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ memcpy(data_out, cdata_in, datalen); break; case JFFS2_COMPR_ZERO: memset(data_out, 0, datalen); break; default: if (cc != NULL) { return (*cc->decompress)(cc, comprtype, cdata_in, data_out, cdatalen, datalen); } else { return -EIO; } } return 0; }
void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) { struct super_block *sb=OFNI_BS_2SFFJ(c); cyg_mtab_entry *mte; int result; RT_ASSERT(c); //RT_ASSERT(!sb->s_gc_thread_handle); mte=(cyg_dir *) sb->s_root; RT_ASSERT(mte); rt_event_init(&sb->s_gc_thread_flags, "gc_event", RT_IPC_FLAG_FIFO); rt_mutex_init(&sb->s_lock, "gc_mutex", RT_IPC_FLAG_FIFO); // rt_mutex_init(&mte->fs->syncmode, "fs_lock", RT_IPC_FLAG_FIFO); D1(printk("jffs2_start_garbage_collect_thread\n")); /* Start the thread. Doesn't matter if it fails -- it's only an * optimisation anyway */ result = rt_thread_init(&sb->s_gc_thread, "jffs2_gc_thread", jffs2_garbage_collect_thread, (void *)c, (void*)sb->s_gc_thread_stack, sizeof(sb->s_gc_thread_stack), CYGNUM_JFFS2_GC_THREAD_PRIORITY, CYGNUM_JFFS2_GC_THREAD_TICKS ); if (result != RT_EOK) { rt_thread_startup(&sb->s_gc_thread); /* how to deal with the following filed? */ /* sb->s_gc_thread_handle; */ } }
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { struct super_block *sb=OFNI_BS_2SFFJ(c); /* Wake up the thread */ D1(printk("jffs2_garbage_collect_trigger\n")); cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_TRIG); }
int jffs2_flash_erase(struct jffs2_sb_info * c, struct jffs2_eraseblock * jeb) { rt_err_t result; struct super_block *sb = OFNI_BS_2SFFJ(c); result = rt_mtd_nor_erase_block(RT_MTD_NOR_DEVICE(sb->s_dev), jeb->offset, c->sector_size); if (result != RT_EOK) return -EIO; return ENOERR; }
int jffs2_flash_write(struct jffs2_sb_info * c, uint32_t offset, const size_t size, size_t * return_size, unsigned char *buffer) { uint32_t len; struct super_block *sb = OFNI_BS_2SFFJ(c); len = rt_mtd_nor_write(RT_MTD_NOR_DEVICE(sb->s_dev), offset, buffer, size); if (len != size) return -EIO; * return_size = len; return ENOERR; }
cyg_bool jffs2_flash_read(struct jffs2_sb_info * c, cyg_uint32 read_buffer_offset, const size_t size, size_t * return_size, unsigned char *write_buffer) { Cyg_ErrNo err; cyg_uint32 len = size; struct super_block *sb = OFNI_BS_2SFFJ(c); //D2(printf("FLASH READ\n")); //D2(printf("read address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + read_buffer_offset)); //D2(printf("write address = %x\n", write_buffer)); //D2(printf("size = %x\n", size)); err = cyg_io_bread(sb->s_dev, write_buffer, &len, read_buffer_offset); *return_size = (size_t) len; return ((err == ENOERR) ? ENOERR : -EIO); }
/* jffs2_compress: * @data_in: Pointer to uncompressed data * @cpage_out: Pointer to returned pointer to buffer for compressed data * @datalen: On entry, holds the amount of data available for compression. * On exit, expected to hold the amount of data actually compressed. * @cdatalen: On entry, holds the amount of space available for compressed * data. On exit, expected to hold the actual size of the compressed * data. * * Returns: Lower byte to be stored with data indicating compression type used. * Zero is used to show that the data could not be compressed - the * compressed version was actually larger than the original. * Upper byte will be used later. (soon) * * If the cdata buffer isn't large enough to hold all the uncompressed data, * jffs2_compress should compress as much as will fit, and should set * *datalen accordingly to show the amount of data which were compressed. */ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *data_in, unsigned char **cpage_out, uint32_t *datalen, uint32_t *cdatalen) { struct super_block *sb = OFNI_BS_2SFFJ(c); rtems_jffs2_compressor_control *cc = sb->s_compressor_control; int ret; if (cc != NULL) { *cpage_out = &cc->buffer[0]; ret = (*cc->compress)(cc, data_in, *cpage_out, datalen, cdatalen); } else { ret = JFFS2_COMPR_NONE; } if (ret == JFFS2_COMPR_NONE) { *cpage_out = data_in; *datalen = *cdatalen; } return ret; }
cyg_bool jffs2_flash_erase(struct jffs2_sb_info * c, struct jffs2_eraseblock * jeb) { cyg_io_flash_getconfig_erase_t e; cyg_flashaddr_t err_addr; Cyg_ErrNo err; cyg_uint32 len = sizeof (e); struct super_block *sb = OFNI_BS_2SFFJ(c); e.offset = jeb->offset; e.len = c->sector_size; e.err_address = &err_addr; // D2(printf("FLASH ERASE ENABLED!!!\n")); // D2(printf("erase address = %x\n", CYGNUM_FS_JFFS2_BASE_ADDRESS + jeb->offset)); // D2(printf("size = %x\n", c->sector_size)); err = cyg_io_get_config(sb->s_dev, CYG_IO_GET_CONFIG_FLASH_ERASE, &e, &len); return (err != ENOERR || e.flasherr != 0); }
static void jffs2_garbage_collect_thread(cyg_addrword_t data) { struct jffs2_sb_info *c=(struct jffs2_sb_info *)data; struct super_block *sb=OFNI_BS_2SFFJ(c); cyg_flag_value_t flag; cyg_mtab_entry *mte; D1(printk("jffs2_garbage_collect_thread START\n")); while(1) { flag=cyg_flag_timed_wait(&sb->s_gc_thread_flags, GC_THREAD_FLAG_TRIG|GC_THREAD_FLAG_STOP, CYG_FLAG_WAITMODE_OR| CYG_FLAG_WAITMODE_CLR, cyg_current_time()+ CYGNUM_JFFS2_GS_THREAD_TICKS); if (flag & GC_THREAD_FLAG_STOP) break; D1(printk("jffs2: GC THREAD GC BEGIN\n")); mte=cyg_fs_root_lookup((cyg_dir *) sb->s_root); CYG_ASSERT(mte, "Bad mount point"); cyg_fs_lock(mte, mte->fs->syncmode); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printf("No space for garbage collection. " "Aborting JFFS2 GC thread\n"); break; } cyg_fs_unlock(mte, mte->fs->syncmode); D1(printk("jffs2: GC THREAD GC END\n")); } D1(printk("jffs2_garbage_collect_thread EXIT\n")); cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_HAS_EXIT); }
static void jffs2_garbage_collect_thread(unsigned long data) { struct jffs2_sb_info *c=(struct jffs2_sb_info *)data; struct super_block *sb=OFNI_BS_2SFFJ(c); cyg_mtab_entry *mte; rt_uint32_t flag = 0; D1(printk("jffs2_garbage_collect_thread START\n")); while(1) { rt_event_recv(&sb->s_gc_thread_flags, GC_THREAD_FLAG_TRIG | GC_THREAD_FLAG_STOP, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, cyg_current_time() + CYGNUM_JFFS2_GS_THREAD_TICKS, &flag); if (flag & GC_THREAD_FLAG_STOP) break; D1(printk("jffs2: GC THREAD GC BEGIN\n")); mte=(cyg_dir *) sb->s_root; RT_ASSERT(mte != NULL); // rt_mutex_take(&mte->fs->syncmode, RT_WAITING_FOREVER); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printf("No space for garbage collection. " "Aborting JFFS2 GC thread\n"); break; } // rt_mutex_release(&mte->fs->syncmode); D1(printk("jffs2: GC THREAD GC END\n")); } D1(printk("jffs2_garbage_collect_thread EXIT\n")); rt_event_send(&sb->s_gc_thread_flags,GC_THREAD_FLAG_HAS_EXIT); }
void jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) { struct super_block *sb=OFNI_BS_2SFFJ(c); CYG_ASSERTC(c); CYG_ASSERTC(!sb->s_gc_thread_handle); cyg_flag_init(&sb->s_gc_thread_flags); cyg_mutex_init(&sb->s_lock); D1(printk("jffs2_start_garbage_collect_thread\n")); /* Start the thread. Doesn't matter if it fails -- it's only an * optimisation anyway */ cyg_thread_create(CYGNUM_JFFS2_GC_THREAD_PRIORITY, jffs2_garbage_collect_thread, (cyg_addrword_t)c,"jffs2 gc thread", (void*)sb->s_gc_thread_stack, sizeof(sb->s_gc_thread_stack), &sb->s_gc_thread_handle, &sb->s_gc_thread); cyg_thread_resume(sb->s_gc_thread_handle); }
struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, int inum, int nlink) { struct inode *inode; struct jffs2_inode_cache *ic; if (!nlink) { /* The inode has zero nlink but its nodes weren't yet marked obsolete. This has to be because we're still waiting for the final (close() and) iput() to happen. There's a possibility that the final iput() could have happened while we were contemplating. In order to ensure that we don't cause a new read_inode() (which would fail) for the inode in question, we use ilookup() in this case instead of iget(). The nlink can't _become_ zero at this point because we're holding the alloc_sem, and jffs2_do_unlink() would also need that while decrementing nlink on any inode. */ inode = ilookup(OFNI_BS_2SFFJ(c), inum); if (!inode) { D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", inum)); spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, inum); if (!ic) { D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); spin_unlock(&c->inocache_lock); return NULL; } if (ic->state != INO_STATE_CHECKEDABSENT) { /* Wait for progress. Don't just loop */ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", ic->ino, ic->state)); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); } else { spin_unlock(&c->inocache_lock); } return NULL; } } else { /* Inode has links to it still; they're not going away because jffs2_do_unlink() would need the alloc_sem and we have it. Just iget() it, and if read_inode() is necessary that's OK. */ inode = iget(OFNI_BS_2SFFJ(c), inum); if (!inode) return ERR_PTR(-ENOMEM); } if (is_bad_inode(inode)) { printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", inum, nlink); /* NB. This will happen again. We need to do something appropriate here. */ iput(inode); return ERR_PTR(-EIO); } return JFFS2_INODE_INFO(inode); }
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node node; __u32 ofs, prevofs; __u32 hdr_crc, nodetype; int err; int noise = 0; ofs = jeb->offset; prevofs = jeb->offset - 1; D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); err = jffs2_scan_empty(c, jeb, &ofs, &noise); if (err) return err; if (ofs == jeb->offset + c->sector_size) { D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); return 1; /* special return code */ } noise = 10; while(ofs < jeb->offset + c->sector_size) { ssize_t retlen; unsigned char *buf = (unsigned char *) &node; ACCT_PARANOIA_CHECK(jeb); if (ofs & 3) { printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); ofs = (ofs+3)&~3; continue; } if (ofs == prevofs) { printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } prevofs = ofs; if (jeb->offset + c->sector_size < ofs + sizeof(node)) { D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node))); DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); break; } err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, buf); if ((buf[0] == 0xde) && (buf[1] == 0xad) && (buf[2] == 0xc0) && (buf[3] == 0xde)) { /* end of filesystem. erase everything after this point */ c->flags |= (1 << 7); printk("jffs2_scan_eraseblock(): End of filesystem marker found at 0x%x\n", jeb->offset); return 1; } if (err) { D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err)); return err; } if (retlen < sizeof(node)) { D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen)); DIRTY_SPACE(retlen); ofs += retlen; continue; } if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) { D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs)); err = jffs2_scan_empty(c, jeb, &ofs, &noise); if (err) return err; continue; } if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) { printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); DIRTY_SPACE(4); ofs += 4; continue; } if (node.magic == JFFS2_DIRTY_BITMASK) { D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs)); DIRTY_SPACE(4); ofs += 4; continue; } if (node.magic == JFFS2_OLD_MAGIC_BITMASK) { printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); DIRTY_SPACE(4); ofs += 4; continue; } if (node.magic != JFFS2_MAGIC_BITMASK) { /* OK. We're out of possibilities. Whinge and move on */ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic); DIRTY_SPACE(4); ofs += 4; continue; } /* We seem to have a node of sorts. Check the CRC */ nodetype = node.nodetype; node.nodetype |= JFFS2_NODE_ACCURATE; hdr_crc = crc32(0, &node, sizeof(node)-4); node.nodetype = nodetype; if (hdr_crc != node.hdr_crc) { noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc); DIRTY_SPACE(4); ofs += 4; continue; } if (ofs + node.totlen > jeb->offset + c->sector_size) { /* Eep. Node goes over the end of the erase block. */ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", ofs, node.totlen); printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); DIRTY_SPACE(4); ofs += 4; continue; } switch(node.nodetype | JFFS2_NODE_ACCURATE) { case JFFS2_NODETYPE_INODE: err = jffs2_scan_inode_node(c, jeb, &ofs); if (err) return err; break; case JFFS2_NODETYPE_DIRENT: err = jffs2_scan_dirent_node(c, jeb, &ofs); if (err) return err; break; case JFFS2_NODETYPE_CLEANMARKER: if (node.totlen != sizeof(struct jffs2_unknown_node)) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", ofs, node.totlen, sizeof(struct jffs2_unknown_node)); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ofs += PAD(sizeof(struct jffs2_unknown_node)); continue; } else { struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); if (!marker_ref) { printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n"); return -ENOMEM; } marker_ref->next_in_ino = NULL; marker_ref->next_phys = NULL; marker_ref->flash_offset = ofs; marker_ref->totlen = sizeof(struct jffs2_unknown_node); jeb->first_node = jeb->last_node = marker_ref; USED_SPACE(PAD(sizeof(struct jffs2_unknown_node))); } ofs += PAD(sizeof(struct jffs2_unknown_node)); break; default: switch (node.nodetype & JFFS2_COMPAT_MASK) { case JFFS2_FEATURE_ROCOMPAT: printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); c->flags |= JFFS2_SB_FLAG_RO; if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)) return -EROFS; DIRTY_SPACE(PAD(node.totlen)); ofs += PAD(node.totlen); continue; case JFFS2_FEATURE_INCOMPAT: printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); return -EINVAL; case JFFS2_FEATURE_RWCOMPAT_DELETE: printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); DIRTY_SPACE(PAD(node.totlen)); ofs += PAD(node.totlen); break; case JFFS2_FEATURE_RWCOMPAT_COPY: printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); USED_SPACE(PAD(node.totlen)); ofs += PAD(node.totlen); break; } } } D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); return 0; }