diff -Naur linux-2.6.20/fs/jffs2/debug.c linux-2.6.20-impr/fs/jffs2/debug.c --- linux-2.6.20/fs/jffs2/debug.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/debug.c 2007-05-31 21:01:55.000000000 +0400 @@ -72,6 +72,9 @@ __jffs2_dbg_fragtree_paranoia_check_nolock(struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; + struct inode *inode = OFNI_EDONI_2SFFJ(f); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + int bitched = 0; for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { @@ -92,15 +95,15 @@ rather than mucking around with actually reading the node and checking the compression type, which is the real way to tell a hole node. */ - if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) - && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { + if (frag->ofs & (jffs2_granularity(c)-1) && frag_prev(frag) + && frag_prev(frag)->size < jffs2_granularity(c) && frag_prev(frag)->node) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2.\n", ref_offset(fn->raw)); bitched = 1; } - if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) - && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { + if ((frag->ofs+frag->size) & (jffs2_granularity(c)-1) && frag_next(frag) + && frag_next(frag)->size < jffs2_granularity(c) && frag_next(frag)->node) { JFFS2_ERROR("REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2.\n", ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); bitched = 1; diff -Naur linux-2.6.20/fs/jffs2/erase.c linux-2.6.20-impr/fs/jffs2/erase.c --- linux-2.6.20/fs/jffs2/erase.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/erase.c 2007-08-29 18:08:16.000000000 +0400 @@ -19,6 +19,7 @@ #include #include #include "nodelist.h" +#include "nopgcache.h" struct erase_priv_struct { struct jffs2_eraseblock *jeb; @@ -133,6 +134,8 @@ c->used_size -= jeb->used_size; c->dirty_size -= jeb->dirty_size; jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; + if ( jffs2_file_direct_ops(c) ) + jffs2_clean_rawinode_hash(c, jeb); jffs2_free_jeb_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); diff -Naur linux-2.6.20/fs/jffs2/file.c linux-2.6.20-impr/fs/jffs2/file.c --- linux-2.6.20/fs/jffs2/file.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/file.c 2007-10-23 20:50:44.000000000 +0400 @@ -20,6 +20,14 @@ #include #include #include "nodelist.h" +#include "nopgcache.h" + + +static int jffs2_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos); + +static int jffs2_file_write(struct file *filp, char *buf, size_t count, loff_t *ppos); + +static ssize_t jffs2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs); static int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end); @@ -42,14 +50,14 @@ { .llseek = generic_file_llseek, .open = generic_file_open, - .read = do_sync_read, .aio_read = generic_file_aio_read, - .write = do_sync_write, .aio_write = generic_file_aio_write, .ioctl = jffs2_ioctl, .mmap = generic_file_readonly_mmap, .fsync = jffs2_fsync, - .sendfile = generic_file_sendfile + .sendfile = generic_file_sendfile, + .read = jffs2_file_read, + .write = jffs2_file_write }; /* jffs2_file_inode_operations */ @@ -64,13 +72,64 @@ .removexattr = jffs2_removexattr }; + const struct address_space_operations jffs2_file_address_operations = { .readpage = jffs2_readpage, - .prepare_write =jffs2_prepare_write, - .commit_write = jffs2_commit_write + .prepare_write = jffs2_prepare_write, + .commit_write = jffs2_commit_write, + .direct_IO = jffs2_direct_IO, + }; + +static int jffs2_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + + if ( jffs2_file_direct_ops(c) == JFFS2_NO_PGCACHE ) + filp->f_flags |= O_DIRECT; + + return do_sync_read(filp, buf, count, ppos); +} + + +static int jffs2_file_write(struct file *filp, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + + if ((jffs2_file_direct_ops(c) == JFFS2_NO_PGCACHE) || (jffs2_file_direct_ops(c) == JFFS2_READ_PGCACHE) ) + filp->f_flags |= O_DIRECT; + + return do_sync_write(filp, buf, count, ppos); +} + +static ssize_t +jffs2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct file *filp = iocb->ki_filp; + unsigned long seg = 0; + loff_t pos = iocb->ki_pos; + ssize_t nbytes = 0; + ssize_t count; + char* addr; + + do { + addr = iov[seg].iov_base; + count = iov[seg].iov_len; + if ( rw == WRITE ) + nbytes += jffs2_direct_file_write(filp, addr, count, &pos); + else + nbytes += jffs2_direct_file_read(filp, addr, count, &pos); + + } while ( ++seg < nr_segs); + + return nbytes; +} + static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); @@ -131,6 +190,7 @@ D1(printk(KERN_DEBUG "jffs2_prepare_write()\n")); + if (pageofs > inode->i_size) { /* Make new hole frag from old EOF to new page */ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); diff -Naur linux-2.6.20/fs/jffs2/fs.c linux-2.6.20-impr/fs/jffs2/fs.c --- linux-2.6.20/fs/jffs2/fs.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/fs.c 2007-05-31 20:45:13.000000000 +0400 @@ -23,6 +23,7 @@ #include #include #include "nodelist.h" +#include "nopgcache.h" static int jffs2_flash_setup(struct jffs2_sb_info *c); @@ -197,8 +198,8 @@ unsigned long avail; buf->f_type = JFFS2_SUPER_MAGIC; - buf->f_bsize = 1 << PAGE_SHIFT; - buf->f_blocks = c->flash_size >> PAGE_SHIFT; + buf->f_bsize = 1 << jffs2_granularity_shift(c); + buf->f_blocks = c->flash_size >> jffs2_granularity_shift(c); buf->f_files = 0; buf->f_ffree = 0; buf->f_namelen = JFFS2_MAX_NAME_LEN; @@ -211,7 +212,7 @@ avail = 0; spin_unlock(&c->erase_completion_lock); - buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; + buf->f_bavail = buf->f_bfree = avail >> jffs2_granularity_shift(c); return 0; } @@ -510,6 +511,13 @@ jffs2_init_xattr_subsystem(c); + jffs2_get_pgcache_options( data, c); + + if (jffs2_file_direct_ops(c)) + ret = jffs2_nopgcache_init(c); + if (ret) + goto out_nopgcache; + if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; @@ -528,8 +536,8 @@ goto out_root_i; sb->s_maxbytes = 0xFFFFFFFF; - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_blocksize = jffs2_granularity(c); + sb->s_blocksize_bits = jffs2_granularity_shift(c); sb->s_magic = JFFS2_SUPER_MAGIC; if (!(sb->s_flags & MS_RDONLY)) jffs2_start_garbage_collect_thread(c); @@ -543,6 +551,9 @@ vfree(c->blocks); else kfree(c->blocks); + out_nopgcache: + if (jffs2_file_direct_ops(c)) + jffs2_nopgcache_exit(c); out_inohash: jffs2_clear_xattr_subsystem(c); kfree(c->inocache_list); diff -Naur linux-2.6.20/fs/jffs2/gc.c linux-2.6.20-impr/fs/jffs2/gc.c --- linux-2.6.20/fs/jffs2/gc.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/gc.c 2007-10-31 16:54:29.000000000 +0300 @@ -19,6 +19,7 @@ #include #include #include "nodelist.h" +#include "nopgcache.h" #include "compr.h" static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, @@ -38,7 +39,6 @@ uint32_t start, uint32_t end); static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); - /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) { @@ -227,7 +227,7 @@ return -EIO; } - D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); + D1(printk(KERN_DEBUG "GC from block %08x, used %08x, dirty %08x, free %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); D1(if (c->nextblock) printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); @@ -487,7 +487,7 @@ goto upnout; } /* We found a datanode. Do the GC */ - if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) { + if((start >> jffs2_granularity_shift(c)) < ((end-1) >> jffs2_granularity_shift(c))) { /* It crosses a page boundary. Therefore, it must be a hole. */ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end); } else { @@ -1100,9 +1100,9 @@ struct jffs2_node_frag *frag; uint32_t min, max; - min = start & ~(PAGE_CACHE_SIZE-1); - max = min + PAGE_CACHE_SIZE; - + min = start & ~(jffs2_granularity(c)-1); + max = min + jffs2_granularity(c); + frag = jffs2_lookup_node_frag(&f->fragtree, start); /* BUG_ON(!frag) but that'll happen anyway... */ @@ -1219,7 +1219,17 @@ * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ - pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + + /* Now we operate pages assigned on jffs2_granularity(c) jffs2_gc_fetch_page loads + * the only page into buffer - we need little bit more + * akrlv */ + + if (jffs2_file_direct_ops(c)) { + pg_ptr = jffs2_gc_fetch_buffer(c, f, start); + } + else { + pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + } if (IS_ERR(pg_ptr)) { printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); @@ -1243,8 +1253,12 @@ cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset); datalen = end - offset; - writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); - + /* Aghh: Since offset is not aligned to page we must not update position in the buffer due to alignment */ + if (jffs2_file_direct_ops(c)) + writebuf = pg_ptr + offset - start; + else + writebuf = pg_ptr + (offset & (jffs2_granularity(c) - 1)); + comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen); ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -1287,6 +1301,10 @@ } } - jffs2_gc_release_page(c, pg_ptr, &pg); + if (jffs2_file_direct_ops(c)) + jffs2_gc_release_buffer(c, pg_ptr); + else + jffs2_gc_release_page(c, pg_ptr, &pg); + return ret; } diff -Naur linux-2.6.20/fs/jffs2/ioctl.c linux-2.6.20-impr/fs/jffs2/ioctl.c --- linux-2.6.20/fs/jffs2/ioctl.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/ioctl.c 2007-10-31 16:46:23.000000000 +0300 @@ -16,8 +16,6 @@ int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { - /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which - will include compression support etc. */ return -ENOTTY; } diff -Naur linux-2.6.20/fs/jffs2/jffs2_fs_sb.h linux-2.6.20-impr/fs/jffs2/jffs2_fs_sb.h --- linux-2.6.20/fs/jffs2/jffs2_fs_sb.h 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/jffs2_fs_sb.h 2007-08-29 19:00:52.000000000 +0400 @@ -129,6 +129,16 @@ uint32_t xdatum_mem_usage; uint32_t xdatum_mem_threshold; #endif +#ifdef CONFIG_JFFS2_NO_PG_CACHE + uint32_t pgcache_mode; /* Struct defines per mount file IO mode */ + struct semaphore file_readbuf_sem; + struct semaphore file_writebuf_sem; + unsigned char *file_read_buf; + unsigned char *file_write_buf; + struct list_head ri_hash_list; + struct semaphore ri_hash_sem; +#endif + /* OS-private pointer for getting back to master superblock info */ void *os_priv; }; diff -Naur linux-2.6.20/fs/jffs2/Makefile linux-2.6.20-impr/fs/jffs2/Makefile --- linux-2.6.20/fs/jffs2/Makefile 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/Makefile 2007-05-17 21:16:01.000000000 +0400 @@ -19,3 +19,4 @@ jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o +jffs2-$(CONFIG_JFFS2_NO_PG_CACHE) += nopgcache.o diff -Naur linux-2.6.20/fs/jffs2/nodelist.c linux-2.6.20-impr/fs/jffs2/nodelist.c --- linux-2.6.20/fs/jffs2/nodelist.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/nodelist.c 2007-05-31 20:49:39.000000000 +0400 @@ -20,6 +20,7 @@ #include #include #include "nodelist.h" +#include "nopgcache.h" static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this); @@ -83,7 +84,7 @@ * REF_PRISTINE irrespective of its size. */ frag = frag_last(list); - if (frag->node && (frag->ofs & (PAGE_CACHE_SIZE - 1)) == 0) { + if (frag->node && (frag->ofs & (jffs2_granularity(c) - 1)) == 0) { dbg_fragtree2("marking the last fragment 0x%08x-0x%08x REF_PRISTINE.\n", frag->ofs, frag->ofs + frag->size); frag->node->raw->flash_offset = ref_offset(frag->node->raw) | REF_PRISTINE; @@ -229,7 +230,7 @@ If so, both 'this' and the new node get marked REF_NORMAL so the GC can take a look. */ - if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { + if (lastend && (lastend-1) >> jffs2_granularity_shift(c) == newfrag->ofs >> jffs2_granularity_shift(c)) { if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); @@ -374,7 +375,7 @@ /* If we now share a page with other nodes, mark either previous or next node REF_NORMAL, as appropriate. */ - if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { + if (newfrag->ofs & (jffs2_granularity(c)-1)) { struct jffs2_node_frag *prev = frag_prev(newfrag); mark_ref_normal(fn->raw); @@ -383,7 +384,7 @@ mark_ref_normal(prev->node->raw); } - if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { + if ((newfrag->ofs+newfrag->size) & (jffs2_granularity(c)-1)) { struct jffs2_node_frag *next = frag_next(newfrag); if (next) { @@ -441,7 +442,7 @@ * adding and jffs2_flash_read_end() interface. */ if (c->mtd->point) { err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); - if (!err && retlen < tn->csize) { + if (!err && retlen < len) { JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); c->mtd->unpoint(c->mtd, buffer, ofs, len); } else if (err) @@ -661,7 +662,7 @@ lastend = 0; /* Detect the preliminary type of node */ - if (fn->size >= PAGE_CACHE_SIZE) + if (fn->size >= jffs2_granularity(c)) ref_flag = REF_PRISTINE; else ref_flag = REF_NORMAL; @@ -838,7 +839,7 @@ } out_ok: - BUG_ON(fn->size < PAGE_CACHE_SIZE && ref_flag == REF_PRISTINE); + BUG_ON(fn->size < jffs2_granularity(c) && ref_flag == REF_PRISTINE); if (ref_flag == REF_OBSOLETE) { dbg_fragtree2("the node is obsolete now\n"); diff -Naur linux-2.6.20/fs/jffs2/nodemgmt.c linux-2.6.20-impr/fs/jffs2/nodemgmt.c --- linux-2.6.20/fs/jffs2/nodemgmt.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/nodemgmt.c 2007-10-31 16:43:45.000000000 +0300 @@ -720,7 +720,6 @@ c->unchecked_size, c->checked_ino)); return 1; } - /* dirty_size contains blocks on erase_pending_list * those blocks are counted in c->nr_erasing_blocks. * If one block is actually erased, it is not longer counted as dirty_space diff -Naur linux-2.6.20/fs/jffs2/nopgcache.c linux-2.6.20-impr/fs/jffs2/nopgcache.c --- linux-2.6.20/fs/jffs2/nopgcache.c 1970-01-01 03:00:00.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/nopgcache.c 2007-10-31 16:45:29.000000000 +0300 @@ -0,0 +1,521 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Intel corp. + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: nopgcache.c,v 1.07 2007/07/15 akrlv intel corp. Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nopgcache.h" +#include "nodelist.h" + + + +/****************************************************************************** + * + * "jffs2_direct_file_read" + * + * DESCRIPTION: + * read the file + * + * PARAMETERS: + * filp [IN] file to read + * + * buf [OUT] buffer to put the read data + * + * count [IN] bytes to read + * + * ppos [OUT] file offset after reading will be put here + * + * RETURNS: + * error code + * + *****************************************************************************/ +ssize_t jffs2_direct_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + unsigned int nread = 0, size, left, readlen; + unsigned int page_num, page_ofs; + int ret = 0; + + size = inode->i_size; + + if (*ppos > size) { + left = 0; + } else { + left = size - *ppos; + } + if (left > count) { + left = count; + } + if (left <= 0) { + return 0; + } + down(&f->sem); + down(&c->file_readbuf_sem); + + /* if read is not page aligned */ + page_num = *ppos >> jffs2_granularity_shift(c); + page_ofs = *ppos - (page_num << jffs2_granularity_shift(c)); + + while(left > 0) + { + /* TODO improve it by reading not aligned size */ + ret = jffs2_read_inode_range(c, f, c->file_read_buf, page_num << jffs2_granularity_shift(c) , jffs2_granularity(c)); + if (ret) { + D1(printk(KERN_DEBUG"jffs2_file_read: failure ino #%lu, offset 0x%lx\n", inode->i_ino, nread)); + break; + } + /* How much requested data have we read */ + readlen = min( (uint32_t) jffs2_granularity(c) - page_ofs, (uint32_t) left ); + + if (copy_to_user(buf + nread, c->file_read_buf + page_ofs, readlen)) { + ret = -EFAULT; + break; + } + + left -= readlen; + *ppos += readlen; + nread += readlen; + page_num ++; + page_ofs = 0; + } + up(&c->file_readbuf_sem); + up(&f->sem); + return ret ? ret : nread; +} + +unsigned char* jffs2_gc_fetch_buffer(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned long offset) +{ + unsigned char* buf; + unsigned int toread; + int ret = 0; + + + toread = jffs2_granularity(c); + + buf = kmalloc( jffs2_granularity(c), GFP_KERNEL); + if (!buf) { + printk(KERN_WARNING"jffs2_gc_fetch_buffer can't allocate memory !\n"); + return NULL; + } + + ret = jffs2_read_inode_range(c, f, buf, offset, toread); + if (ret) { + kfree(buf); + printk(KERN_WARNING"jffs2_gc_fetch_buffer can't read data!\n"); + return NULL; + } + return buf; +} + +void jffs2_gc_release_buffer(struct jffs2_sb_info *c, unsigned char* buf) +{ + kfree(buf); + return; +} + + +/****************************************************************************** + * + * "jffs2_direct_file_write" + * + * DESCRIPTION: + * write the file + * + * PARAMETERS: + * filp [IN] file to write + * + * buf [IN] data to write + * + * count [IN] bytes to write + * + * ppos [OUT] file offset after writing will be put here + * + * RETURNS: + * error code + * + *****************************************************************************/ +ssize_t jffs2_direct_file_write(struct file *filp,const char *buf,size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_raw_inode* pri; + int towrite, ret=0, pos = 0, nwritten = 0, writtenlen = 0; + + D1(printk(KERN_DEBUG "jffs2_file_write() : ino #%lu, at %d of %d bytes \n", + inode->i_ino, *ppos, count)); + + if (*ppos > inode->i_size) { + /* Make new hole frag from old EOF to new page */ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_raw_inode ri; + struct jffs2_full_dnode *fn; + + /* Make new hole frag from old EOF to new page */ + uint32_t alloc_len; + + D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new position\n", + (unsigned int)inode->i_size, *ppos)); + + ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + if (ret) + return ret; + + down(&f->sem); + memset(&ri, 0, sizeof(ri)); + + ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri.totlen = cpu_to_je32(sizeof(ri)); + ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); + + ri.ino = cpu_to_je32(f->inocache->ino); + ri.version = cpu_to_je32(++f->highest_version); + ri.mode = cpu_to_jemode(inode->i_mode); + ri.uid = cpu_to_je16(inode->i_uid); + ri.gid = cpu_to_je16(inode->i_gid); + ri.isize = cpu_to_je32(max(inode->i_size, *ppos)); + ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); + ri.offset = cpu_to_je32(inode->i_size); + ri.dsize = cpu_to_je32(*ppos - inode->i_size); + ri.csize = cpu_to_je32(0); + ri.compr = JFFS2_COMPR_ZERO; + ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + ri.data_crc = cpu_to_je32(0); + + fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL); + + if (IS_ERR(fn)) { + ret = PTR_ERR(fn); + jffs2_complete_reservation(c); + up(&f->sem); + return ret; + } + ret = jffs2_add_full_dnode_to_inode(c, f, fn); + if (f->metadata) { + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + f->metadata = NULL; + } + if (ret) { + D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); + jffs2_complete_reservation(c); + up(&f->sem); + return ret; + } + jffs2_complete_reservation(c); + inode->i_size = *ppos; + up(&f->sem); + } + + pri = jffs2_alloc_raw_inode(); + + if (!pri) { + D1(printk(KERN_DEBUG "jffs2_file_write(): Allocation of raw inode failed\n")); + return -ENOMEM; + } + + /* Set the fields that the generic jffs2_write_inode_range() code can't find */ + pri->ino = cpu_to_je32(inode->i_ino); + pri->mode = cpu_to_jemode(inode->i_mode); + pri->uid = cpu_to_je16(inode->i_uid); + pri->gid = cpu_to_je16(inode->i_gid); + pri->isize = cpu_to_je32((uint32_t)inode->i_size); + pri->atime = pri->ctime = pri->mtime = cpu_to_je32(get_seconds()); + + down(&c->file_writebuf_sem); + while(count > 0 && !ret) + { + towrite = (count > jffs2_granularity(c) ? jffs2_granularity(c) : count); + if (copy_from_user(c->file_write_buf, buf + pos, towrite)) { + ret = -EFAULT; + break; + }; + ret = jffs2_write_inode_range(c, f, pri, c->file_write_buf, + *ppos, towrite, &writtenlen); + nwritten += writtenlen; + count -= writtenlen; + pos += writtenlen; + *ppos += writtenlen; + + if (ret) { + D1(printk(KERN_DEBUG "jffs2_file_write() failure: %d\n", ret)); + break; + } + + }; + up(&c->file_writebuf_sem); + + if (*ppos > inode->i_size) + { + inode->i_size = *ppos; + inode->i_blocks = (inode->i_size + 511) >> 9; + inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(pri->ctime)); + }; + + jffs2_free_raw_inode(pri); + + if (ret) { + /* generic_file_write has written more to the page cache than we've + actually written to the medium. Mark the page !Uptodate so that + it gets reread */ + D1(printk(KERN_DEBUG "jffs2_file_write() failure: ret %d bytes written %d \n", count, nwritten)); + return ret; + } + + D1(printk(KERN_DEBUG "jffs2_file_write() returning %d\n",ret)); + return nwritten; +} + +/****************************************************************************** + * + * "jffs2_get_pgcache_options" + * + * DESCRIPTION: + * Parsec mount options realted to "page cache"/"direct file io" support. + * + * PARAMETERS: + * options [IN] String to mount options + * c [IN] Super block info structure + * + * RETURNS: + * error code + * + *****************************************************************************/ + +void jffs2_get_pgcache_options( char* options, struct jffs2_sb_info *c) +{ +char *this_char; +int pgcache_mode = -1; + + c->pgcache_mode = JFFS2_NO_PGCACHE; /* defaul option is set*/ + + while ((this_char = strsep(&options, ",")) != NULL) { + if (!*this_char) + continue; + + if (!strcmp(this_char, "nopgcache")) { + if (pgcache_mode != -1) + goto warn_out; + pgcache_mode = JFFS2_NO_PGCACHE; + } + else if (!strcmp(this_char, "pgcache")) { + if (pgcache_mode != -1) + goto warn_out; + pgcache_mode = JFFS2_PGCACHE; + } + else if (!strcmp(this_char, "cachedread")) { + if (pgcache_mode != -1) + goto warn_out; + pgcache_mode = JFFS2_READ_PGCACHE; + } + } + if (pgcache_mode != -1 ) + c->pgcache_mode = pgcache_mode; + return; + +warn_out: + printk(KERN_WARNING"jffs2: mount option \"nopgcache, pgcache and cachedread \" are exclusive default option \"nopgcache\" is set \n"); + +} + +/****************************************************************************** + * + * "jffs2_clean_rawinode_hash" + * Clear sll raw inode hash records within the given erase_block + * PARAMETERS: + * c [IN] Super block info structure + * jeb [IN] Erase block structure + * + * RETURNS: + * + * + *****************************************************************************/ +void jffs2_clean_rawinode_hash(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb ) +{ + struct jffs2_ri_hash* ri_hash; + struct list_head *item, *tmp; + + down(&c->ri_hash_sem); + /* Clear raw inode hash first */ + list_for_each_safe (item, tmp, &c->ri_hash_list) { + ri_hash = list_entry (item, struct jffs2_ri_hash, list); + + if ( (ri_hash->flash_offset >= jeb->offset) && + (ri_hash->flash_offset < (jeb->offset + c->sector_size)) ) { + /* We found - invalidate record and place it to bottom */ + ri_hash->flash_offset = -1UL; + list_del( &ri_hash->list ); + list_add_tail( &ri_hash->list, &c->ri_hash_list ); + } + } + up(&c->ri_hash_sem); + + return; +} + +/****************************************************************************** + * + * "jffs2_find_rawinode_hash_frag" + * Finds a record in rawinode hash for the given fragment + * PARAMETERS: + * c [IN] Super block info structure + * fd [IN] Full dnnode structure + * + * RETURNS: + * Pointer to ri_hash structure or NULL if not found + * + * + *****************************************************************************/ +struct jffs2_ri_hash* jffs2_find_rawinode_hash_frag(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd) +{ + struct jffs2_ri_hash* ri_hash; + list_for_each_entry(ri_hash, &c->ri_hash_list, list) { + if (ri_hash->flash_offset == ref_offset(fd->raw) ) { + return ri_hash; + } + } + + return NULL; +} + +/****************************************************************************** + * + * "jffs2_nopgcahe_init" + * + * DESCRIPTION: + * Initializes variables and buffers for for direct file operations + * + * PARAMETERS: + * c [IN] Super block info structure + * + * RETURNS: + * error code + * + *****************************************************************************/ +int jffs2_nopgcache_init(struct jffs2_sb_info *c) +{ + int ret; + struct jffs2_ri_hash* ri_hash; + struct list_head *item, *tmp; + int i; + + c->file_read_buf = NULL; + c->file_write_buf = NULL; + + init_MUTEX(&c->file_readbuf_sem); + init_MUTEX(&c->file_writebuf_sem); + init_MUTEX(&c->ri_hash_sem); + INIT_LIST_HEAD(&c->ri_hash_list); + + + c->file_read_buf = kmalloc( jffs2_granularity(c), GFP_KERNEL); + if (!c->file_read_buf) { + JFFS2_WARNING("Can't allocate memory for file read buffer !\n"); + ret = -ENOMEM; + goto out_err; + } + + c->file_write_buf = kmalloc( jffs2_granularity(c), GFP_KERNEL); + if (!c->file_write_buf) { + JFFS2_WARNING("Can't allocate memory for file write buffer !\n"); + ret = -ENOMEM; + goto out_err; + } + + down(&c->ri_hash_sem); + for (i = 0; i < JFFS2_RAW_INODE_HASHSIZE; i++) + { + ri_hash = kmalloc( sizeof(struct jffs2_ri_hash), GFP_KERNEL ); + if (!ri_hash) { + ret = -ENOMEM; + goto out_hash_err; + } + memset( ri_hash, 0 , sizeof(struct jffs2_ri_hash) ); + ri_hash->flash_offset = -1UL; + ri_hash->ri = jffs2_alloc_raw_inode(); + if (!ri_hash->ri) { + kfree (ri_hash); + ret = -ENOMEM; + goto out_hash_err; + } + INIT_LIST_HEAD(&ri_hash->list); + list_add( &ri_hash->list, &c->ri_hash_list); + } + up(&c->ri_hash_sem); + + return 0; + +out_hash_err: + list_for_each_safe (item, tmp, &c->ri_hash_list) { + ri_hash = list_entry (item, struct jffs2_ri_hash, list); + list_del( &ri_hash->list); + kfree(ri_hash->ri); + kfree(ri_hash); + } + up(&c->ri_hash_sem); + +out_err: + if (c->file_read_buf) + kfree(c->file_read_buf); + if (c->file_write_buf) + kfree(c->file_write_buf); + return ret; + +} + + +/****************************************************************************** + * + * "jffs2_nopgcache_exit" + * + * PARAMETERS: + * c [IN] Super block info structure + * + * RETURNS: + * + * + *****************************************************************************/ +void jffs2_nopgcache_exit(struct jffs2_sb_info *c) +{ + + struct jffs2_ri_hash* ri_hash; + struct list_head *item, *tmp; + + down(&c->ri_hash_sem); + /* Clear raw inode hash first */ + list_for_each_safe (item, tmp, &c->ri_hash_list) { + ri_hash = list_entry (item, struct jffs2_ri_hash, list); + list_del( &ri_hash->list); + jffs2_free_raw_inode(ri_hash->ri); + kfree(ri_hash); + } + up(&c->ri_hash_sem); + + if (c->file_read_buf) + kfree(c->file_read_buf); + if (c->file_write_buf) + kfree(c->file_write_buf); + return; +} + + diff -Naur linux-2.6.20/fs/jffs2/nopgcache.h linux-2.6.20-impr/fs/jffs2/nopgcache.h --- linux-2.6.20/fs/jffs2/nopgcache.h 1970-01-01 03:00:00.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/nopgcache.h 2007-08-29 20:14:22.000000000 +0400 @@ -0,0 +1,64 @@ +#include +#include "jffs2_fs_sb.h" +#include "jffs2_fs_i.h" + +#define JFFS2_PGCACHE 0 /* Read and write operations are passing through vfs pg cache*/ +#define JFFS2_NO_PGCACHE 1 /* Read and write operations are passing through direct file operations*/ +#define JFFS2_READ_PGCACHE 2 /* Read operations are passing through vfs cache while write operations passing through direct file operations (experimental)*/ + +#define JFFS2_RAW_INODE_HASHSIZE 16 /* The size of RAW inode hash, each hash unit eats ~80 bytes */ + +#ifdef CONFIG_JFFS2_NO_PG_CACHE /* DIRECT FILE OPERATION ARE ENABLED */ + +#define jffs2_file_direct_ops(c) (c->pgcache_mode) +ssize_t jffs2_direct_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos); +ssize_t jffs2_direct_file_write(struct file *file,const char *buf,size_t count, loff_t *ppos); + +int jffs2_nopgcache_init(struct jffs2_sb_info *c); +void jffs2_nopgcache_exit(struct jffs2_sb_info *c); +unsigned char* jffs2_gc_fetch_buffer(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned long offset); +void jffs2_gc_release_buffer(struct jffs2_sb_info *c, unsigned char* buf); +void jffs2_get_pgcache_options( char* options, struct jffs2_sb_info *c); +void jffs2_clean_rawinode_hash(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb ); +struct jffs2_ri_hash* jffs2_find_rawinode_hash_frag(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd); + +#else + +#define jffs2_file_direct_ops(c) (0) +#define jffs2_direct_file_read(a,b,c,d) (0) +#define jffs2_direct_file_write(a,b,c,d) (0) +#define jffs2_do_direct_file_read(a,b,c,d) (0) +#define jffs2_do_direct_file_write(a,b,c,d) (0) +#define jffs2_nopgcache_init(a) (0) +#define jffs2_nopgcache_exit(a) +#define jffs2_gc_fetch_buffer(a,b,c) (0) +#define jffs2_gc_release_buffer(a,b) +#define jffs2_get_pgcache_options( a, b ) +#define jffs2_clean_rawinode_hash( a, b ) +#define jffs2_find_rawinode_hash_frag( a, b) (0) + + +#endif /* CONFIG_JFFS2_NO_PG_CACHE */ + +/* Size of the direct IO granularity the set of experiments showed that the optimal value for granularity + is 1/4 of erase block */ +#define jffs2_granularity(c) ( jffs2_file_direct_ops(c) ? c->sector_size / 4 : PAGE_CACHE_SIZE ) + +static inline uint32_t jffs2_granularity_shift( struct jffs2_sb_info *c) +{ + uint32_t shift = 0; + while( (jffs2_granularity(c) >> (shift + 1) ) != 0 ) + shift++; + + return shift; +} + +/* This structure is need for keeping RAW inode cache for read dnode operations + for the case of NAND and large granularity it must increase read performance */ +struct jffs2_ri_hash +{ + uint32_t flash_offset; + struct jffs2_raw_inode *ri; + struct list_head list; +}; + diff -Naur linux-2.6.20/fs/jffs2/read.c linux-2.6.20-impr/fs/jffs2/read.c --- linux-2.6.20/fs/jffs2/read.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/read.c 2007-10-31 16:53:09.000000000 +0300 @@ -18,8 +18,21 @@ #include #include #include "nodelist.h" +#include "nopgcache.h" #include "compr.h" +/* The optimized function for reading dnode + optimization 1: since CRC of all nodes was checked at the mount time and compression is + off just read a part of dnode it gives >400% in case of default granularity + and 4k requests. + TODO: If NAND page is corrupted after tons of write and we have ECC error. + Clarify what happens first: BAD block or 2 bits error. + If 2 bits errors: + TODO clarify need we to transfer a EBADMSG status and handle this case + optimization 2: Place RAW inodes into the hash - if we have large dnodes to read part of dnode assumes + reading data from two different nand pages +*/ + int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) @@ -29,42 +42,58 @@ uint32_t crc; unsigned char *decomprbuf = NULL; unsigned char *readbuf = NULL; + struct jffs2_ri_hash* ri_hash = NULL; int ret = 0; - ri = jffs2_alloc_raw_inode(); - if (!ri) - return -ENOMEM; - - ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); - if (ret) { - jffs2_free_raw_inode(ri); - printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); - return ret; - } - if (readlen != sizeof(*ri)) { - jffs2_free_raw_inode(ri); - printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", - ref_offset(fd->raw), sizeof(*ri), readlen); - return -EIO; - } - crc = crc32(0, ri, sizeof(*ri)-8); - - D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", - ref_offset(fd->raw), je32_to_cpu(ri->node_crc), - crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), - je32_to_cpu(ri->offset), buf)); - if (crc != je32_to_cpu(ri->node_crc)) { - printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", - je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); - ret = -EIO; - goto out_ri; - } - /* There was a bug where we wrote hole nodes out with csize/dsize - swapped. Deal with it */ - if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && - je32_to_cpu(ri->csize)) { - ri->dsize = ri->csize; - ri->csize = cpu_to_je32(0); + down(&c->ri_hash_sem); + ri_hash = jffs2_find_rawinode_hash_frag( c, fd ); + if ( ri_hash ) { + ri = ri_hash->ri; + /* Place it at the top of list */ + list_del( &ri_hash->list); + list_add( &ri_hash->list, &c->ri_hash_list); + + } + else { + ri_hash = list_entry(c->ri_hash_list.prev, struct jffs2_ri_hash, list); + ri = ri_hash->ri; + ri_hash->flash_offset = -1UL; + + ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); + if (ret) { + printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); + goto out_ri; + } + if (readlen != sizeof(*ri)) { + printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", + ref_offset(fd->raw), sizeof(*ri), readlen); + ret = -EIO; + goto out_ri; + } + crc = crc32(0, ri, sizeof(*ri)-8); + + D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", + ref_offset(fd->raw), je32_to_cpu(ri->node_crc), + crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), + je32_to_cpu(ri->offset), buf)); + if (crc != je32_to_cpu(ri->node_crc)) { + printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", + je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); + ret = -EIO; + goto out_ri; + } + + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ + if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && + je32_to_cpu(ri->csize)) { + ri->dsize = ri->csize; + ri->csize = cpu_to_je32(0); + } + /* RAW inode is OK let put it at the top of the list */ + ri_hash->flash_offset = ref_offset(fd->raw); + list_del( &ri_hash->list); + list_add( &ri_hash->list, &c->ri_hash_list); } D1(if(ofs + len > je32_to_cpu(ri->dsize)) { @@ -74,7 +103,7 @@ goto out_ri; }); - + if (ri->compr == JFFS2_COMPR_ZERO) { memset(buf, 0, len); goto out_ri; @@ -82,8 +111,8 @@ /* Cases: Reading whole node and it's uncompressed - read directly to buffer provided, check CRC. - Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided - Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy + Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided + Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy */ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) { @@ -104,39 +133,55 @@ } } else { decomprbuf = buf; - } + } } else { decomprbuf = readbuf; } D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize), readbuf)); - ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), - je32_to_cpu(ri->csize), &readlen, readbuf); + + if ( ri->compr == JFFS2_COMPR_NONE) { - if (!ret && readlen != je32_to_cpu(ri->csize)) - ret = -EIO; - if (ret) - goto out_decomprbuf; - - crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); - if (crc != je32_to_cpu(ri->data_crc)) { - printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", - je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); - ret = -EIO; - goto out_decomprbuf; - } - D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); - if (ri->compr != JFFS2_COMPR_NONE) { - D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", - je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); - ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); - if (ret) { - printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); + BUG_ON(ref_flags(fd->raw) == REF_UNCHECKED); + + ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri) + ofs, + len, &readlen,readbuf + ofs); + if (!ret && readlen != len) + ret = -EIO; + if (ret) + goto out_decomprbuf; + + } + else + { + ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), + je32_to_cpu(ri->csize), &readlen, readbuf); + + if (!ret && readlen != je32_to_cpu(ri->csize)) + ret = -EIO; + if (ret) + goto out_decomprbuf; + + crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); + if (crc != je32_to_cpu(ri->data_crc)) { + printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", + je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); + ret = -EIO; + goto out_decomprbuf; + } + D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); + if (ri->compr != JFFS2_COMPR_NONE) { + D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", + je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); + ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); + if (ret) { + printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); goto out_decomprbuf; } + } } - + if (len < je32_to_cpu(ri->dsize)) { memcpy(buf, decomprbuf+ofs, len); } @@ -147,11 +192,13 @@ if(readbuf != buf) kfree(readbuf); out_ri: - jffs2_free_raw_inode(ri); - + up(&c->ri_hash_sem); + return ret; } + + int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, unsigned char *buf, uint32_t offset, uint32_t len) { diff -Naur linux-2.6.20/fs/jffs2/scan.c linux-2.6.20-impr/fs/jffs2/scan.c --- linux-2.6.20/fs/jffs2/scan.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/scan.c 2007-05-31 20:46:35.000000000 +0400 @@ -20,6 +20,7 @@ #include "nodelist.h" #include "summary.h" #include "debug.h" +#include "nopgcache.h" #define DEFAULT_EMPTY_SCAN_SIZE 1024 @@ -115,7 +116,7 @@ if (jffs2_cleanmarker_oob(c)) buf_size = c->sector_size; else - buf_size = PAGE_SIZE; + buf_size = jffs2_granularity(c); /* Respect kmalloc limitations */ if (buf_size > 128*1024) diff -Naur linux-2.6.20/fs/jffs2/super.c linux-2.6.20-impr/fs/jffs2/super.c --- linux-2.6.20/fs/jffs2/super.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/super.c 2007-05-31 19:31:55.000000000 +0400 @@ -25,6 +25,7 @@ #include #include #include "compr.h" +#include "nopgcache.h" #include "nodelist.h" static void jffs2_put_super(struct super_block *); @@ -300,6 +301,8 @@ kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); + if (jffs2_file_direct_ops(c)) + jffs2_nopgcache_exit(c); jffs2_clear_xattr_subsystem(c); if (c->mtd->sync) c->mtd->sync(c->mtd); diff -Naur linux-2.6.20/fs/jffs2/wbuf.c linux-2.6.20-impr/fs/jffs2/wbuf.c --- linux-2.6.20/fs/jffs2/wbuf.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/wbuf.c 2007-10-23 20:48:47.000000000 +0400 @@ -633,7 +633,10 @@ memset(c->wbuf,0xff,c->wbuf_pagesize); /* adjust write buffer offset, else we get a non contiguous write bug */ - c->wbuf_ofs += c->wbuf_pagesize; + if (SECTOR_ADDR(c->wbuf_ofs) == SECTOR_ADDR(c->wbuf_ofs+c->wbuf_pagesize)) + c->wbuf_ofs += c->wbuf_pagesize; + else + c->wbuf_ofs = 0xffffffff; c->wbuf_len = 0; return 0; } diff -Naur linux-2.6.20/fs/jffs2/write.c linux-2.6.20-impr/fs/jffs2/write.c --- linux-2.6.20/fs/jffs2/write.c 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/jffs2/write.c 2007-05-31 20:38:30.000000000 +0400 @@ -19,6 +19,7 @@ #include #include "nodelist.h" #include "compr.h" +#include "nopgcache.h" int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) @@ -167,8 +168,8 @@ beginning of a page and runs to the end of the file, or if it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. */ - if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || - ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && + if ((je32_to_cpu(ri->dsize) >= jffs2_granularity(c)) || + ( ((je32_to_cpu(ri->offset)&(jffs2_granularity(c)-1))==0) && (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { flash_ofs |= REF_PRISTINE; } else { @@ -332,7 +333,7 @@ break; } down(&f->sem); - datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); + datalen = min_t(uint32_t, writelen, jffs2_granularity(c) - (offset & (jffs2_granularity(c)-1))); cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen); diff -Naur linux-2.6.20/fs/Kconfig linux-2.6.20-impr/fs/Kconfig --- linux-2.6.20/fs/Kconfig 2007-03-23 22:52:51.000000000 +0300 +++ linux-2.6.20-impr/fs/Kconfig 2007-10-23 21:56:08.000000000 +0400 @@ -1278,6 +1278,12 @@ If unsure, say 'N'. +config JFFS2_NO_PG_CACHE + bool "JFFS2 direct file operations support" + help + JFFS2 direct file operations support. It allows RAM usage reduction + on large volumes. + config JFFS2_FS_XATTR bool "JFFS2 XATTR support (EXPERIMENTAL)" depends on JFFS2_FS && EXPERIMENTAL @@ -1381,7 +1387,7 @@ help Tries all compressors and chooses the one which has the smallest result. - + endchoice config CRAMFS