- NOR flash with transparent ECC
            - DataFlash
 
+config JFFS2_SUMMARY
+       bool "JFFS2 summary support (EXPERIMENTAL)"
+       depends on JFFS2_FS && EXPERIMENTAL
+       default n
+       help
+         This feature makes it possible to use summary information
+         for faster filesystem mount.
+
+         The summary information can be inserted into a filesystem image
+         by the utility 'sumtool'.
+
+         If unsure, say 'N'.
+
 config JFFS2_COMPRESSION_OPTIONS
        bool "Advanced compression options for JFFS2"
        depends on JFFS2_FS
 
 #
 # Makefile for the Linux Journalling Flash File System v2 (JFFS2)
 #
-# $Id: Makefile.common,v 1.10 2005/07/17 06:56:20 dedekind Exp $
+# $Id: Makefile.common,v 1.11 2005/09/07 08:34:53 havasi Exp $
 #
 
 obj-$(CONFIG_JFFS2_FS) += jffs2.o
 jffs2-$(CONFIG_JFFS2_RUBIN)    += compr_rubin.o
 jffs2-$(CONFIG_JFFS2_RTIME)    += compr_rtime.o
 jffs2-$(CONFIG_JFFS2_ZLIB)     += compr_zlib.o
+jffs2-$(CONFIG_JFFS2_SUMMARY)   += summary.o
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: build.c,v 1.77 2005/08/31 13:51:00 havasi Exp $
+ * $Id: build.c,v 1.78 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
        INIT_LIST_HEAD(&c->bad_list);
        INIT_LIST_HEAD(&c->bad_used_list);
        c->highest_ino = 1;
+       c->summary = NULL;
+
+       if (jffs2_sum_init(c))
+               return -ENOMEM;
 
        if (jffs2_build_filesystem(c)) {
                D1(printk(KERN_DEBUG "build_fs failed\n"));
                jffs2_free_raw_node_refs(c);
 #ifndef __ECOS
                if (jffs2_blocks_use_vmalloc(c))
-                    vfree(c->blocks);
+                       vfree(c->blocks);
                else 
 #endif
-                    kfree(c->blocks);
-               
+                       kfree(c->blocks);
+
                return -EIO;
        }
 
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: debug.h,v 1.14 2005/08/17 13:48:59 dedekind Exp $
+ * $Id: debug.h,v 1.15 2005/09/07 08:34:54 havasi Exp $
  *
  */
 #ifndef _JFFS2_DEBUG_H_
 #define JFFS2_DBG_DENTLIST_MESSAGES
 #define JFFS2_DBG_NODEREF_MESSAGES
 #define JFFS2_DBG_INOCACHE_MESSAGES
+#define JFFS2_DBG_SUMMARY_MESSAGES
 #endif
 
 #if CONFIG_JFFS2_FS_DEBUG == 2
 #define JFFS2_DBG_INOCACHE(fmt, ...)
 #endif
 
+/* Summary debugging messages */
+#ifdef JFFS2_DBG_SUMMARY_MESSAGES
+#define JFFS2_DBG_SUMMARY(fmt, ...)    JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define JFFS2_DBG_SUMMARY(fmt, ...)
+#endif
+
 /* Watch the object allocations */
 #ifdef JFFS2_DBG_MEMALLOC_MESSAGES
 #define JFFS2_DBG_MEMALLOC(fmt, ...)   JFFS2_DEBUG(fmt, ##__VA_ARGS__)
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: dir.c,v 1.88 2005/08/17 13:46:22 dedekind Exp $
+ * $Id: dir.c,v 1.89 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
         * Just the node will do for now, though 
         */
        namelen = dentry->d_name.len;
-       ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 
        if (ret) {
                jffs2_free_raw_inode(ri);
        up(&f->sem);
 
        jffs2_complete_reservation(c);
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
        if (ret) {
                /* Eep. */
                jffs2_clear_inode(inode);
         * Just the node will do for now, though 
         */
        namelen = dentry->d_name.len;
-       ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL,
+                               JFFS2_SUMMARY_INODE_SIZE);
 
        if (ret) {
                jffs2_free_raw_inode(ri);
        up(&f->sem);
 
        jffs2_complete_reservation(c);
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
        if (ret) {
                /* Eep. */
                jffs2_clear_inode(inode);
         * Just the node will do for now, though 
         */
        namelen = dentry->d_name.len;
-       ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 
        if (ret) {
                jffs2_free_raw_inode(ri);
        up(&f->sem);
 
        jffs2_complete_reservation(c);
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
        if (ret) {
                /* Eep. */
                jffs2_clear_inode(inode);
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: file.c,v 1.102 2005/07/06 12:13:09 dwmw2 Exp $
+ * $Id: file.c,v 1.103 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
                D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
                          (unsigned int)inode->i_size, pageofs));
 
-               ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
+               ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len,
+                                       ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
                if (ret)
                        return ret;
 
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: fs.c,v 1.64 2005/09/01 08:42:31 havasi Exp $
+ * $Id: fs.c,v 1.65 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
                return -ENOMEM;
        }
                
-       ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
        if (ret) {
                jffs2_free_raw_inode(ri);
                if (S_ISLNK(inode->i_mode & S_IFMT))
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.153 2005/08/17 13:46:22 dedekind Exp $
+ * $Id: gc.c,v 1.154 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
        /* Ask for a small amount of space (or the totlen if smaller) because we
           don't want to force wastage of the end of a block if splitting would
           work. */
-       ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, 
-                                             rawlen), &phys_ofs, &alloclen);
+       ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) +
+                               JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen);
+                               /* this is not the exact summary size of it,
+                                       it is only an upper estimation */
+
        if (ret)
                return ret;
 
                        jffs2_dbg_acct_sanity_check(c,jeb);
                        jffs2_dbg_acct_paranoia_check(c, jeb);
 
-                       ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
+                       ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen);
+                                               /* this is not the exact summary size of it,
+                                                       it is only an upper estimation */
 
                        if (!ret) {
                                D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
 
        }
        
-       ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
+       ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen,
+                               JFFS2_SUMMARY_INODE_SIZE);
        if (ret) {
                printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
                       sizeof(ri)+ mdatalen, ret);
        rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
        rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
        
-       ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
+       ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen,
+                               JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize));
        if (ret) {
                printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
                       sizeof(rd)+rd.nsize, ret);
        ri.data_crc = cpu_to_je32(0);
        ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 
-       ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
+       ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen,
+                               JFFS2_SUMMARY_INODE_SIZE);
        if (ret) {
                printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
                       sizeof(ri), ret);
                uint32_t cdatalen;
                uint16_t comprtype = JFFS2_COMPR_NONE;
 
-               ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
+               ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs,
+                                       &alloclen, JFFS2_SUMMARY_INODE_SIZE);
 
                if (ret) {
                        printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
        jffs2_gc_release_page(c, pg_ptr, &pg);
        return ret;
 }
-
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodelist.h,v 1.139 2005/08/31 13:51:00 havasi Exp $
+ * $Id: nodelist.h,v 1.140 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
 #include <linux/jffs2.h>
 #include <linux/jffs2_fs_sb.h>
 #include <linux/jffs2_fs_i.h>
+#include "summary.h"
 
 #ifdef __ECOS
 #include "os-ecos.h"
 
 /* nodemgmt.c */
 int jffs2_thread_should_wake(struct jffs2_sb_info *c);
-int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+                       uint32_t *len, int prio, uint32_t sumsize);
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+                       uint32_t *len, uint32_t sumsize);
 int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
 void jffs2_complete_reservation(struct jffs2_sb_info *c);
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
 /* scan.c */
 int jffs2_scan_medium(struct jffs2_sb_info *c);
 void jffs2_rotate_lists(struct jffs2_sb_info *c);
+int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
+                               uint32_t ofs, uint32_t len);
+struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
+int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 /* build.c */
 int jffs2_do_mount_fs(struct jffs2_sb_info *c);
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: nodemgmt.c,v 1.124 2005/07/20 15:32:28 dedekind Exp $
+ * $Id: nodemgmt.c,v 1.125 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
 #include <linux/compiler.h>
 #include <linux/sched.h> /* For cond_resched() */
 #include "nodelist.h"
+#include "debug.h"
 
 /**
  *     jffs2_reserve_space - request physical space to write nodes to flash
  *     for the requested allocation.
  */
 
-static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize, uint32_t *ofs, uint32_t *len);
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize,
+                                       uint32_t *ofs, uint32_t *len, uint32_t sumsize);
 
-int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+                       uint32_t *len, int prio, uint32_t sumsize)
 {
        int ret = -EAGAIN;
        int blocksneeded = c->resv_blocks_write;
                        spin_lock(&c->erase_completion_lock);
                }
 
-               ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+               ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
                if (ret) {
                        D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
                }
        return ret;
 }
 
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
+                       uint32_t *len, uint32_t sumsize)
 {
        int ret = -EAGAIN;
        minsize = PAD(minsize);
 
        spin_lock(&c->erase_completion_lock);
        while(ret == -EAGAIN) {
-               ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+               ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
                if (ret) {
                        D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
                }
        return ret;
 }
 
-/* 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)
+
+/* Classify nextblock (clean, dirty of verydirty) and force to select an other one */
+
+static void jffs2_close_nextblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
-       struct jffs2_eraseblock *jeb = c->nextblock;
+
+       /* 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;
+
+}
+
+/* Select a new jeb for nextblock */
+
+static int jffs2_find_nextblock(struct jffs2_sb_info *c)
+{
+       struct list_head *next;
        
- 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)) {
+       /* 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);
-                       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;
+                       /* Have another go. It'll be on the erasable_list now */
+                       return -EAGAIN;
                }
-               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);
+
+               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;
                }
-               c->nextblock = jeb = NULL;
+
+               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--;
        
-       if (!jeb) {
-               struct list_head *next;
-               /* Take the next block off the 'free' list */
+       jffs2_sum_reset_collected(c->summary); /* reset collected summary */
 
-               if (list_empty(&c->free_list)) {
+       D1(printk(KERN_DEBUG "jffs2_find_nextblock(): new nextblock = 0x%08x\n", c->nextblock->offset));
 
-                       if (!c->nr_erasing_blocks && 
-                           !list_empty(&c->erasable_list)) {
-                               struct jffs2_eraseblock *ejeb;
+       return 0;
+}
 
-                               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));
+/* 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, uint32_t sumsize)
+{
+       struct jffs2_eraseblock *jeb = c->nextblock;
+       uint32_t reserved_size;                         /* for summary information at the end of the jeb */
+       int ret;
+
+ restart:
+       reserved_size = 0;
+
+       if (jffs2_sum_active() && (sumsize != JFFS2_SUMMARY_NOSUM_SIZE)) {
+                                                       /* NOSUM_SIZE means not to generate summary */
+
+               if (jeb) {
+                       reserved_size = PAD(sumsize + c->summary->sum_size + JFFS2_SUMMARY_FRAME_SIZE);
+                       JFFS2_DBG_SUMMARY("minsize=%d , jeb->free=%d ,"
+                                               "summary->size=%d , sumsize=%d\n",
+                                               minsize, jeb->free_size,
+                                               c->summary->sum_size, sumsize);
+               }
+
+               /* Is there enough space for writing out the current node, or we have to
+                  write out summary information now, close this jeb and select new nextblock? */
+               if (jeb && (PAD(minsize) + PAD(c->summary->sum_size + sumsize +
+                                       JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size)) {
+
+                       /* Has summary been disabled for this jeb? */
+                       if (jffs2_sum_is_disabled(c->summary)) {
+                               sumsize = JFFS2_SUMMARY_NOSUM_SIZE;
+                               goto restart;
                        }
 
-                       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 */                       
+                       /* Writing out the collected summary information */
+                       JFFS2_DBG_SUMMARY("generating summary for 0x%08x.\n", jeb->offset);
+                       ret = jffs2_sum_write_sumnode(c);
+
+                       if (ret)
+                               return ret;
+
+                       if (jffs2_sum_is_disabled(c->summary)) {
+                               /* jffs2_write_sumnode() couldn't write out the summary information
+                                  diabling summary for this jeb and free the collected information
+                                */
+                               sumsize = JFFS2_SUMMARY_NOSUM_SIZE;
+                               goto restart;
+                       }
+
+                       jffs2_close_nextblock(c, jeb);
+                       jeb = NULL;
+               }
+       } else {
+               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);
-                               /* 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;
+                               jeb = c->nextblock;
+                               goto restart;
                        }
 
-                       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);
+                       c->wasted_size += jeb->free_size;
+                       c->free_size -= jeb->free_size;
+                       jeb->wasted_size += jeb->free_size;
+                       jeb->free_size = 0;
 
-                       /* An erase may have failed, decreasing the
-                          amount of free space available. So we must
-                          restart from the beginning */
-                       return -EAGAIN;
+                       jffs2_close_nextblock(c, jeb);
+                       jeb = NULL;
                }
+       }
+
+       if (!jeb) {
+
+               ret = jffs2_find_nextblock(c);
+               if (ret)
+                       return ret;
 
-               next = c->free_list.next;
-               list_del(next);
-               c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
-               c->nr_free_blocks--;
+               jeb = c->nextblock;
 
                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);
        /* 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;
+       *len = jeb->free_size - reserved_size;
 
        if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
            !jeb->first_node->next_in_ino) {
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: os-linux.h,v 1.60 2005/08/06 04:51:30 nico Exp $
+ * $Id: os-linux.h,v 1.61 2005/09/07 08:34:54 havasi Exp $
  *
  */
 
 
 #ifndef CONFIG_JFFS2_FS_WRITEBUFFER
 #define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
+
+#ifdef CONFIG_JFFS2_SUMMARY
+#define jffs2_can_mark_obsolete(c) (0)
+#else
 #define jffs2_can_mark_obsolete(c) (1)
+#endif
+
 #define jffs2_is_writebuffered(c) (0)
 #define jffs2_cleanmarker_oob(c) (0)
 #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
 
-#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
+#define jffs2_flash_write(c, ofs, len, retlen, buf) jffs2_flash_direct_write(c, ofs, len, retlen, buf)
 #define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf))
 #define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; })
 #define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; })
 
 #define jffs2_is_writebuffered(c) (c->wbuf != NULL)
 #define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
+
+#ifdef CONFIG_JFFS2_SUMMARY
+#define jffs2_can_mark_obsolete(c) (0)
+#else
 #define jffs2_can_mark_obsolete(c) \
   ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \
    c->mtd->type == MTD_RAM)
+#endif
+
 #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
 
 #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
 /* writev.c */
 int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, 
                       unsigned long count, loff_t to, size_t *retlen);
-
+int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
+                       size_t *retlen, const u_char *buf);
 
 #endif /* __JFFS2_OS_LINUX_H__ */
 
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: scan.c,v 1.121 2005/07/20 15:32:28 dedekind Exp $
+ * $Id: scan.c,v 1.122 2005/09/07 08:34:54 havasi Exp $
  *
  */
 #include <linux/kernel.h>
 #include <linux/crc32.h>
 #include <linux/compiler.h>
 #include "nodelist.h"
+#include "summary.h"
+#include "debug.h"
 
 #define DEFAULT_EMPTY_SCAN_SIZE 1024
 
-#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
-               c->free_size -= _x; c->dirty_size += _x; \
-               jeb->free_size -= _x ; jeb->dirty_size += _x; \
-               }while(0)
-#define USED_SPACE(x) do { typeof(x) _x = (x); \
-               c->free_size -= _x; c->used_size += _x; \
-               jeb->free_size -= _x ; jeb->used_size += _x; \
-               }while(0)
-#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
-               c->free_size -= _x; c->unchecked_size += _x; \
-               jeb->free_size -= _x ; jeb->unchecked_size += _x; \
-               }while(0)
-
 #define noisy_printk(noise, args...) do { \
        if (*(noise)) { \
                printk(KERN_NOTICE args); \
 static uint32_t pseudo_random;
 
 static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-                                 unsigned char *buf, uint32_t buf_size);
+                                 unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s);
 
 /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. 
  * Returning an error will abort the mount - bad checksums etc. should just mark the space
  * as dirty.
  */
 static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-                                struct jffs2_raw_inode *ri, uint32_t ofs);
+                                struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s);
 static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-                                struct jffs2_raw_dirent *rd, uint32_t ofs);
-
-#define BLK_STATE_ALLFF                0
-#define BLK_STATE_CLEAN                1
-#define BLK_STATE_PARTDIRTY    2
-#define BLK_STATE_CLEANMARKER  3
-#define BLK_STATE_ALLDIRTY     4
-#define BLK_STATE_BADBLOCK     5
+                                struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s);
 
 static inline int min_free(struct jffs2_sb_info *c)
 {
        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;
 
                        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];
 
-               ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
+               /* 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;
                        break;
 
                case BLK_STATE_CLEAN:
-                        /* Full (or almost full) of clean data. Clean list */
-                        list_add(&jeb->list, &c->clean_list);
+                       /* 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) {
+                       /* 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;
                                        } else {
                                                list_add(&c->nextblock->list, &c->dirty_list);
                                        }
+                                       /* deleting summary information of the old nextblock */
+                                       jffs2_sum_reset_collected(c->summary);
                                }
-                                c->nextblock = jeb;
-                        } else {
+                               /* 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;
                                } 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 */
+                       /* 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);
+                       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);
+                       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();  
+                       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;
        return ret;
 }
 
-static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
+int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf,
                                uint32_t ofs, uint32_t len)
 {
        int ret;
        return 0;
 }
 
+int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+       if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
+               && (!jeb->first_node || !jeb->first_node->next_phys) )
+               return BLK_STATE_CLEANMARKER;
+
+       /* move blocks with max 4 byte dirty space to cleanlist */
+       else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
+               c->dirty_size -= jeb->dirty_size;
+               c->wasted_size += jeb->dirty_size;
+               jeb->wasted_size += jeb->dirty_size;
+               jeb->dirty_size = 0;
+               return BLK_STATE_CLEAN;
+       } else if (jeb->used_size || jeb->unchecked_size)
+               return BLK_STATE_PARTDIRTY;
+       else
+               return BLK_STATE_ALLDIRTY;
+}
+
 static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-                                 unsigned char *buf, uint32_t buf_size) {
+                               unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
        struct jffs2_unknown_node *node;
        struct jffs2_unknown_node crcnode;
+       struct jffs2_sum_marker *sm;
        uint32_t ofs, prevofs;
        uint32_t hdr_crc, buf_ofs, buf_len;
        int err;
        int noise = 0;
+
+
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
        int cleanmarkerfound = 0;
 #endif
                }
        }
 #endif
+
+       if (jffs2_sum_active()) {
+               sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL);
+               if (!sm) {
+                       return -ENOMEM;
+               }
+
+               err = jffs2_fill_scan_buf(c, (unsigned char *) sm, jeb->offset + c->sector_size -
+                                       sizeof(struct jffs2_sum_marker), sizeof(struct jffs2_sum_marker));
+               if (err) {
+                       kfree(sm);
+                       return err;
+               }
+
+               if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) {
+                       err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random);
+                       if (err) {
+                               kfree(sm);
+                               return err;
+                       }
+               }
+
+               kfree(sm);
+
+               ofs = jeb->offset;
+               prevofs = jeb->offset - 1;
+       }
+
        buf_ofs = jeb->offset;
 
        if (!buf_size) {
                buf_len = c->sector_size;
+
+               if (jffs2_sum_active()) {
+                       /* must reread because of summary test */
+                       err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
+                       if (err)
+                               return err;
+               }
+
        } else {
                buf_len = EMPTY_SCAN_SIZE(c->sector_size);
                err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
 
        noise = 10;
 
+       JFFS2_DBG_SUMMARY("no summary found in jeb 0x%08x. Apply original scan.\n",jeb->offset);
+
 scan_more:     
        while(ofs < jeb->offset + c->sector_size) {
 
                                buf_ofs = ofs;
                                node = (void *)buf;
                        }
-                       err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
+                       err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs, s);
                        if (err) return err;
                        ofs += PAD(je32_to_cpu(node->totlen));
                        break;
                                buf_ofs = ofs;
                                node = (void *)buf;
                        }
-                       err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
+                       err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs, s);
                        if (err) return err;
                        ofs += PAD(je32_to_cpu(node->totlen));
                        break;
                        break;
 
                case JFFS2_NODETYPE_PADDING:
+                       if (jffs2_sum_active())
+                               jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen));
                        DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
                        ofs += PAD(je32_to_cpu(node->totlen));
                        break;
                }
        }
 
+       if (jffs2_sum_active()) {
+               if (PAD(s->sum_size + JFFS2_SUMMARY_FRAME_SIZE) > jeb->free_size) {
+                       JFFS2_DBG_SUMMARY("There is not enough space for "
+                               "summary information, disabling for this jeb!\n");
+                       jffs2_sum_disable_collecting(s);
+               }
+       }
 
        D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, 
                  jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
                jeb->wasted_size = 0;
        }
 
-       if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
-               && (!jeb->first_node || !jeb->first_node->next_phys) )
-               return BLK_STATE_CLEANMARKER;
-               
-       /* move blocks with max 4 byte dirty space to cleanlist */      
-       else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
-               c->dirty_size -= jeb->dirty_size;
-               c->wasted_size += jeb->dirty_size; 
-               jeb->wasted_size += jeb->dirty_size;
-               jeb->dirty_size = 0;
-               return BLK_STATE_CLEAN;
-       } else if (jeb->used_size || jeb->unchecked_size)
-               return BLK_STATE_PARTDIRTY;
-       else
-               return BLK_STATE_ALLDIRTY;
+       return jffs2_scan_classify_jeb(c, jeb);
 }
 
-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
 {
        struct jffs2_inode_cache *ic;
 
 }
 
 static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-                                struct jffs2_raw_inode *ri, uint32_t ofs)
+                                struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s)
 {
        struct jffs2_raw_node_ref *raw;
        struct jffs2_inode_cache *ic;
        pseudo_random += je32_to_cpu(ri->version);
 
        UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
+
+       if (jffs2_sum_active()) {
+               jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset);
+       }
+
        return 0;
 }
 
 static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-                                 struct jffs2_raw_dirent *rd, uint32_t ofs)
+                                 struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s)
 {
        struct jffs2_raw_node_ref *raw;
        struct jffs2_full_dirent *fd;
        USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
        jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
+       if (jffs2_sum_active()) {
+               jffs2_sum_add_dirent_mem(s, rd, ofs - jeb->offset);
+       }
+
        return 0;
 }
 
 
--- /dev/null
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                     Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *                     Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *                     University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: summary.c,v 1.1 2005/09/07 08:34:54 havasi Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.h>
+#include <linux/crc32.h>
+#include <linux/compiler.h>
+#include <linux/vmalloc.h>
+#include "nodelist.h"
+#include "debug.h"
+
+int jffs2_sum_init(struct jffs2_sb_info *c)
+{
+       c->summary = kmalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
+
+       if (!c->summary) {
+               JFFS2_WARNING("Can't allocate memory for summary information!\n");
+               return -ENOMEM;
+       }
+
+       memset(c->summary, 0, sizeof(struct jffs2_summary));
+
+       c->summary->sum_buf = vmalloc(c->sector_size);
+
+       if (!c->summary->sum_buf) {
+               JFFS2_WARNING("Can't allocate buffer for writing out summary information!\n");
+               return -ENOMEM;
+       }
+
+       JFFS2_DBG_SUMMARY("returned succesfully\n");
+
+       return 0;
+}
+
+void jffs2_sum_exit(struct jffs2_sb_info *c)
+{
+       JFFS2_DBG_SUMMARY("called\n");
+
+       jffs2_sum_disable_collecting(c->summary);
+
+       vfree(c->summary->sum_buf);
+       c->summary->sum_buf = NULL;
+
+       kfree(c->summary);
+       c->summary = NULL;
+}
+
+static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item)
+{
+       if (!s->sum_list_head)
+               s->sum_list_head = (union jffs2_sum_mem *) item;
+       if (s->sum_list_tail)
+               s->sum_list_tail->u.next = (union jffs2_sum_mem *) item;
+       s->sum_list_tail = (union jffs2_sum_mem *) item;
+
+       switch (je16_to_cpu(item->u.nodetype)) {
+               case JFFS2_NODETYPE_INODE:
+                       s->sum_size += JFFS2_SUMMARY_INODE_SIZE;
+                       s->sum_num++;
+                       JFFS2_DBG_SUMMARY("inode (%u) added to summary\n",
+                                               je32_to_cpu(item->i.inode));
+                       break;
+               case JFFS2_NODETYPE_DIRENT:
+                       s->sum_size += JFFS2_SUMMARY_DIRENT_SIZE(item->d.nsize);
+                       s->sum_num++;
+                       JFFS2_DBG_SUMMARY("dirent (%u) added to summary\n",
+                                               je32_to_cpu(item->d.ino));
+                       break;
+               default:
+                       JFFS2_WARNING("UNKNOWN node type %u\n", 
+                                           je16_to_cpu(item->u.nodetype));
+                       return 1;
+       }
+       return 0;
+}
+
+
+/* The following 3 functions are called from scan.c to collect summary info for not closed jeb */
+
+int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size)
+{
+       JFFS2_DBG_SUMMARY("called with %u\n", size);
+       s->sum_padded += size;
+       return 0;
+}
+
+int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri,
+                               uint32_t ofs)
+{
+       struct jffs2_sum_inode_mem *temp = kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL);
+
+       if (!temp)
+               return -ENOMEM;
+
+       temp->nodetype = ri->nodetype;
+       temp->inode = ri->ino;
+       temp->version = ri->version;
+       temp->offset = cpu_to_je32(ofs); /* relative offset from the begining of the jeb */
+       temp->totlen = ri->totlen;
+       temp->next = NULL;
+
+       return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd,
+                               uint32_t ofs)
+{
+       struct jffs2_sum_dirent_mem *temp =
+               kmalloc(sizeof(struct jffs2_sum_dirent_mem) + rd->nsize, GFP_KERNEL);
+
+       if (!temp)
+               return -ENOMEM;
+
+       temp->nodetype = rd->nodetype;
+       temp->totlen = rd->totlen;
+       temp->offset = cpu_to_je32(ofs);        /* relative from the begining of the jeb */
+       temp->pino = rd->pino;
+       temp->version = rd->version;
+       temp->ino = rd->ino;
+       temp->nsize = rd->nsize;
+       temp->type = rd->type;
+       temp->next = NULL;
+
+       memcpy(temp->name, rd->name, rd->nsize);
+
+       return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+/* Cleanup every collected summary information */
+
+static void jffs2_sum_clean_collected(struct jffs2_summary *s)
+{
+       union jffs2_sum_mem *temp;
+
+       if (!s->sum_list_head) {
+               JFFS2_DBG_SUMMARY("already empty\n");
+       }
+       while (s->sum_list_head) {
+               temp = s->sum_list_head;
+               s->sum_list_head = s->sum_list_head->u.next;
+               kfree(temp);
+       }
+       s->sum_list_tail = NULL;
+       s->sum_padded = 0;
+       s->sum_num = 0;
+}
+
+void jffs2_sum_reset_collected(struct jffs2_summary *s)
+{
+       JFFS2_DBG_SUMMARY("called\n");
+       jffs2_sum_clean_collected(s);
+       s->sum_size = 0;
+}
+
+void jffs2_sum_disable_collecting(struct jffs2_summary *s)
+{
+       JFFS2_DBG_SUMMARY("called\n");
+       jffs2_sum_clean_collected(s);
+       s->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
+}
+
+int jffs2_sum_is_disabled(struct jffs2_summary *s) 
+{
+       return (s->sum_size == JFFS2_SUMMARY_NOSUM_SIZE);
+}
+
+/* Move the collected summary information into sb (called from scan.c) */
+
+void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s)
+{
+       JFFS2_DBG_SUMMARY("oldsize=0x%x oldnum=%u => newsize=0x%x newnum=%u\n",
+                               c->summary->sum_size, c->summary->sum_num,
+                               s->sum_size, s->sum_num);
+
+       c->summary->sum_size = s->sum_size;
+       c->summary->sum_num = s->sum_num;
+       c->summary->sum_padded = s->sum_padded;
+       c->summary->sum_list_head = s->sum_list_head;
+       c->summary->sum_list_tail = s->sum_list_tail;
+
+       s->sum_list_head = s->sum_list_tail = NULL;
+}
+
+/* Called from wbuf.c to collect writed node info */
+
+int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
+                               unsigned long count, uint32_t ofs)
+{
+       union jffs2_node_union *node;
+       struct jffs2_eraseblock *jeb;
+
+       node = invecs[0].iov_base;
+       jeb = &c->blocks[ofs / c->sector_size];
+       ofs -= jeb->offset;
+
+       switch (je16_to_cpu(node->u.nodetype)) {
+               case JFFS2_NODETYPE_INODE: {
+                       struct jffs2_sum_inode_mem *temp =
+                               kmalloc(sizeof(struct jffs2_sum_inode_mem), GFP_KERNEL);
+
+                       if (!temp)
+                               goto no_mem;
+
+                       temp->nodetype = node->i.nodetype;
+                       temp->inode = node->i.ino;
+                       temp->version = node->i.version;
+                       temp->offset = cpu_to_je32(ofs);
+                       temp->totlen = node->i.totlen;
+                       temp->next = NULL;
+
+                       return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+               }
+
+               case JFFS2_NODETYPE_DIRENT: {
+                       struct jffs2_sum_dirent_mem *temp =
+                               kmalloc(sizeof(struct jffs2_sum_dirent_mem) + node->d.nsize, GFP_KERNEL);
+
+                       if (!temp)
+                               goto no_mem;
+
+                       temp->nodetype = node->d.nodetype;
+                       temp->totlen = node->d.totlen;
+                       temp->offset = cpu_to_je32(ofs);
+                       temp->pino = node->d.pino;
+                       temp->version = node->d.version;
+                       temp->ino = node->d.ino;
+                       temp->nsize = node->d.nsize;
+                       temp->type = node->d.type;
+                       temp->next = NULL;
+
+                       switch (count) {
+                               case 1:
+                                       memcpy(temp->name,node->d.name,node->d.nsize);
+                                       break;
+
+                               case 2:
+                                       memcpy(temp->name,invecs[1].iov_base,node->d.nsize);
+                                       break;
+
+                               default:
+                                       BUG();  /* impossible count value */
+                                       break;
+                       }
+
+                       return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+               }
+
+               case JFFS2_NODETYPE_PADDING:
+                       JFFS2_DBG_SUMMARY("node PADDING\n");
+                       c->summary->sum_padded += je32_to_cpu(node->u.totlen);
+                       break;
+
+               case JFFS2_NODETYPE_CLEANMARKER:
+                       JFFS2_DBG_SUMMARY("node CLEANMARKER\n");
+                       break;
+
+               case JFFS2_NODETYPE_SUMMARY:
+                       JFFS2_DBG_SUMMARY("node SUMMARY\n");
+                       break;
+
+               default:
+                       /* If you implement a new node type you should also implement
+                          summary support for it or disable summary.
+                       */
+                       BUG();
+                       break;
+       }
+
+       return 0;
+
+no_mem:
+       JFFS2_WARNING("MEMORY ALLOCATION ERROR!");
+       return -ENOMEM;
+}
+
+
+/* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */
+
+static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+                               struct jffs2_summary_node *summary, uint32_t *pseudo_random)
+{
+       struct jffs2_raw_node_ref *raw;
+       struct jffs2_inode_cache *ic;
+       struct jffs2_full_dirent *fd;
+       void *sp;
+       int i, ino;
+
+       sp = summary->sum;
+
+       for (i=0; i<je32_to_cpu(summary->sum_num); i++) {
+               JFFS2_DBG_SUMMARY("processing summary index %d\n", i);
+
+               switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
+                       case JFFS2_NODETYPE_INODE: {
+                               struct jffs2_sum_inode_flash *spi;
+                               spi = sp;
+
+                               ino = je32_to_cpu(spi->inode);
+
+                               JFFS2_DBG_SUMMARY("Inode at 0x%08x\n",
+                                                       jeb->offset + je32_to_cpu(spi->offset));
+
+                               raw = jffs2_alloc_raw_node_ref();
+                               if (!raw) {
+                                       JFFS2_NOTICE("allocation of node reference failed\n");
+                                       kfree(summary);
+                                       return -ENOMEM;
+                               }
+
+                               ic = jffs2_scan_make_ino_cache(c, ino);
+                               if (!ic) {
+                                       JFFS2_NOTICE("scan_make_ino_cache failed\n");
+                                       jffs2_free_raw_node_ref(raw);
+                                       kfree(summary);
+                                       return -ENOMEM;
+                               }
+
+                               raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED;
+                               raw->__totlen = PAD(je32_to_cpu(spi->totlen));
+                               raw->next_phys = NULL;
+                               raw->next_in_ino = ic->nodes;
+
+                               ic->nodes = raw;
+                               if (!jeb->first_node)
+                                       jeb->first_node = raw;
+                               if (jeb->last_node)
+                                       jeb->last_node->next_phys = raw;
+                               jeb->last_node = raw;
+                               *pseudo_random += je32_to_cpu(spi->version);
+
+                               UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen)));
+
+                               sp += JFFS2_SUMMARY_INODE_SIZE;
+
+                               break;
+                       }
+
+                       case JFFS2_NODETYPE_DIRENT: {
+                               struct jffs2_sum_dirent_flash *spd;
+                               spd = sp;
+
+                               JFFS2_DBG_SUMMARY("Dirent at 0x%08x\n",
+                                                       jeb->offset + je32_to_cpu(spd->offset));
+
+                               fd = jffs2_alloc_full_dirent(spd->nsize+1);
+                               if (!fd) {
+                                       kfree(summary);
+                                       return -ENOMEM;
+                               }
+
+                               memcpy(&fd->name, spd->name, spd->nsize);
+                               fd->name[spd->nsize] = 0;
+
+                               raw = jffs2_alloc_raw_node_ref();
+                               if (!raw) {
+                                       jffs2_free_full_dirent(fd);
+                                       JFFS2_NOTICE("allocation of node reference failed\n");
+                                       kfree(summary);
+                                       return -ENOMEM;
+                               }
+
+                               ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
+                               if (!ic) {
+                                       jffs2_free_full_dirent(fd);
+                                       jffs2_free_raw_node_ref(raw);
+                                       kfree(summary);
+                                       return -ENOMEM;
+                               }
+
+                               raw->__totlen = PAD(je32_to_cpu(spd->totlen));
+                               raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE;
+                               raw->next_phys = NULL;
+                               raw->next_in_ino = ic->nodes;
+                               ic->nodes = raw;
+                               if (!jeb->first_node)
+                                       jeb->first_node = raw;
+                               if (jeb->last_node)
+                                       jeb->last_node->next_phys = raw;
+                               jeb->last_node = raw;
+
+                               fd->raw = raw;
+                               fd->next = NULL;
+                               fd->version = je32_to_cpu(spd->version);
+                               fd->ino = je32_to_cpu(spd->ino);
+                               fd->nhash = full_name_hash(fd->name, spd->nsize);
+                               fd->type = spd->type;
+                               USED_SPACE(PAD(je32_to_cpu(spd->totlen)));
+                               jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
+
+                               *pseudo_random += je32_to_cpu(spd->version);
+
+                               sp += JFFS2_SUMMARY_DIRENT_SIZE(spd->nsize);
+
+                               break;
+                       }
+
+                       default : {
+                               JFFS2_WARNING("Unsupported node type found in summary! Exiting...");
+                               kfree(summary);
+                               return -EIO;
+                       }
+               }
+       }
+
+       kfree(summary);
+       return 0;
+}
+
+/* Process the summary node - called from jffs2_scan_eraseblock() */
+
+int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+                               uint32_t ofs, uint32_t *pseudo_random)
+{
+       struct jffs2_unknown_node crcnode;
+       struct jffs2_raw_node_ref *cache_ref;
+       struct jffs2_summary_node *summary;
+       int ret, sumsize;
+       uint32_t crc;
+
+       sumsize = c->sector_size - ofs;
+       ofs += jeb->offset;
+
+       JFFS2_DBG_SUMMARY("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
+                               jeb->offset, ofs, sumsize);
+
+       summary = kmalloc(sumsize, GFP_KERNEL);
+
+       if (!summary) {
+               return -ENOMEM;
+       }
+
+       ret = jffs2_fill_scan_buf(c, (unsigned char *)summary, ofs, sumsize);
+
+       if (ret) {
+               kfree(summary);
+               return ret;
+       }
+
+       /* OK, now check for node validity and CRC */
+       crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+       crcnode.nodetype = cpu_to_je16(JFFS2_NODETYPE_SUMMARY);
+       crcnode.totlen = summary->totlen;
+       crc = crc32(0, &crcnode, sizeof(crcnode)-4);
+
+       if (je32_to_cpu(summary->hdr_crc) != crc) {
+               JFFS2_DBG_SUMMARY("Summary node header is corrupt (bad CRC or "
+                               "no summary at all)\n");
+               goto crc_err;
+       }
+
+       if (je32_to_cpu(summary->totlen) != sumsize) {
+               JFFS2_DBG_SUMMARY("Summary node is corrupt (wrong erasesize?)\n");
+               goto crc_err;
+       }
+
+       crc = crc32(0, summary, sizeof(struct jffs2_summary_node)-8);
+
+       if (je32_to_cpu(summary->node_crc) != crc) {
+               JFFS2_DBG_SUMMARY("Summary node is corrupt (bad CRC)\n");
+               goto crc_err;
+       }
+
+       crc = crc32(0, summary->sum, sumsize - sizeof(struct jffs2_summary_node));
+
+       if (je32_to_cpu(summary->sum_crc) != crc) {
+               JFFS2_DBG_SUMMARY("Summary node data is corrupt (bad CRC)\n");
+               goto crc_err;
+       }
+
+       if ( je32_to_cpu(summary->cln_mkr) ) {
+
+               JFFS2_DBG_SUMMARY("Summary : CLEANMARKER node \n");
+
+               if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) {
+                       JFFS2_DBG_SUMMARY("CLEANMARKER node has totlen 0x%x != normal 0x%x\n",
+                               je32_to_cpu(summary->cln_mkr), c->cleanmarker_size);
+                       UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr)));
+               } else if (jeb->first_node) {
+                       JFFS2_DBG_SUMMARY("CLEANMARKER node not first node in block "
+                                       "(0x%08x)\n", jeb->offset);
+                       UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr)));
+               } else {
+                       struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
+
+                       if (!marker_ref) {
+                               JFFS2_NOTICE("Failed to allocate node ref for clean marker\n");
+                               kfree(summary);
+                               return -ENOMEM;
+                       }
+
+                       marker_ref->next_in_ino = NULL;
+                       marker_ref->next_phys = NULL;
+                       marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+                       marker_ref->__totlen = je32_to_cpu(summary->cln_mkr);
+                       jeb->first_node = jeb->last_node = marker_ref;
+
+                       USED_SPACE( PAD(je32_to_cpu(summary->cln_mkr)) );
+               }
+       }
+
+       if (je32_to_cpu(summary->padded)) {
+               DIRTY_SPACE(je32_to_cpu(summary->padded));
+       }
+
+       ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random);
+       if (ret)
+               return ret;
+
+       /* for PARANOIA_CHECK */
+       cache_ref = jffs2_alloc_raw_node_ref();
+
+       if (!cache_ref) {
+               JFFS2_NOTICE("Failed to allocate node ref for cache\n");
+               return -ENOMEM;
+       }
+
+       cache_ref->next_in_ino = NULL;
+       cache_ref->next_phys = NULL;
+       cache_ref->flash_offset = ofs | REF_NORMAL;
+       cache_ref->__totlen = sumsize;
+
+       if (!jeb->first_node)
+               jeb->first_node = cache_ref;
+       if (jeb->last_node)
+               jeb->last_node->next_phys = cache_ref;
+       jeb->last_node = cache_ref;
+
+       USED_SPACE(sumsize);
+
+       jeb->wasted_size += jeb->free_size;
+       c->wasted_size += jeb->free_size;
+       c->free_size -= jeb->free_size;
+       jeb->free_size = 0;
+
+       return jffs2_scan_classify_jeb(c, jeb);
+
+crc_err:
+       JFFS2_WARNING("Summary node crc error, skipping summary information.\n");
+
+       return 0;
+}
+
+/* Write summary data to flash - helper function for jffs2_sum_write_sumnode() */
+
+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_summary_node 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;
+
+       JFFS2_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;
+}
+
+/* Write out summary information - called from jffs2_do_reserve_space */
+
+int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
+{
+       struct jffs2_raw_node_ref *summary_ref;
+       int datasize, infosize, padsize, ret;
+       struct jffs2_eraseblock *jeb;
+
+       JFFS2_DBG_SUMMARY("called\n");
+
+       jeb = c->nextblock;
+
+       if (!c->summary->sum_num || !c->summary->sum_list_head) {
+               JFFS2_WARNING("Empty summary info!!!\n");
+               BUG();
+       }
+
+       datasize = c->summary->sum_size + sizeof(struct jffs2_sum_marker);
+       infosize = sizeof(struct jffs2_summary_node) + datasize;
+       padsize = jeb->free_size - infosize;
+       infosize += padsize; 
+       datasize += padsize;
+
+       /* 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);
+               return 0;
+       }
+
+       ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize);
+       if (ret)
+               return 0; /* can't write out summary, block is marked as NOSUM_SIZE */
+
+       /* for ACCT_PARANOIA_CHECK */
+       spin_unlock(&c->erase_completion_lock);
+       summary_ref = jffs2_alloc_raw_node_ref();
+       spin_lock(&c->erase_completion_lock);
+
+       if (!summary_ref) {
+               JFFS2_NOTICE("Failed to allocate node ref for summary\n");
+               return -ENOMEM;
+       }
+
+       summary_ref->next_in_ino = NULL;
+       summary_ref->next_phys = NULL;
+       summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL;
+       summary_ref->__totlen = infosize;
+
+       if (!jeb->first_node)
+               jeb->first_node = summary_ref;
+       if (jeb->last_node)
+               jeb->last_node->next_phys = summary_ref;
+       jeb->last_node = summary_ref;
+
+       USED_SPACE(infosize);
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2004  Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                     Zoltan Sogor <weth@inf.u-szeged.hu>,
+ *                     Patrik Kluba <pajko@halom.u-szeged.hu>,
+ *                     University of Szeged, Hungary
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: summary.h,v 1.1 2005/09/07 08:34:54 havasi Exp $
+ *
+ */
+
+#ifndef JFFS2_SUMMARY_H
+#define JFFS2_SUMMARY_H
+
+#include <linux/uio.h>
+#include <linux/jffs2.h>
+
+#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+               c->free_size -= _x; c->dirty_size += _x; \
+               jeb->free_size -= _x ; jeb->dirty_size += _x; \
+               }while(0)
+#define USED_SPACE(x) do { typeof(x) _x = (x); \
+               c->free_size -= _x; c->used_size += _x; \
+               jeb->free_size -= _x ; jeb->used_size += _x; \
+               }while(0)
+#define WASTED_SPACE(x) do { typeof(x) _x = (x); \
+               c->free_size -= _x; c->wasted_size += _x; \
+               jeb->free_size -= _x ; jeb->wasted_size += _x; \
+               }while(0)
+#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
+               c->free_size -= _x; c->unchecked_size += _x; \
+               jeb->free_size -= _x ; jeb->unchecked_size += _x; \
+               }while(0)
+
+#define BLK_STATE_ALLFF                0
+#define BLK_STATE_CLEAN                1
+#define BLK_STATE_PARTDIRTY    2
+#define BLK_STATE_CLEANMARKER  3
+#define BLK_STATE_ALLDIRTY     4
+#define BLK_STATE_BADBLOCK     5
+
+#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
+#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
+#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+
+/* Summary structures used on flash */
+
+struct jffs2_sum_unknown_flash
+{
+       jint16_t nodetype;      /* node type */
+};
+
+struct jffs2_sum_inode_flash
+{
+       jint16_t nodetype;      /* node type */
+       jint32_t inode;         /* inode number */
+       jint32_t version;       /* inode version */
+       jint32_t offset;        /* offset on jeb */
+       jint32_t totlen;        /* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_flash
+{
+       jint16_t nodetype;      /* == JFFS_NODETYPE_DIRENT */
+       jint32_t totlen;        /* record length */
+       jint32_t offset;        /* offset on jeb */
+       jint32_t pino;          /* parent inode */
+       jint32_t version;       /* dirent version */
+       jint32_t ino;           /* == zero for unlink */
+       uint8_t nsize;          /* dirent name size */
+       uint8_t type;           /* dirent type */
+       uint8_t name[0];        /* dirent name */
+} __attribute__((packed));
+
+union jffs2_sum_flash
+{
+       struct jffs2_sum_unknown_flash u;
+       struct jffs2_sum_inode_flash i;
+       struct jffs2_sum_dirent_flash d;
+};
+
+/* Summary structures used in the memory */
+
+struct jffs2_sum_unknown_mem
+{
+       union jffs2_sum_mem *next;
+       jint16_t nodetype;      /* node type */
+};
+
+struct jffs2_sum_inode_mem
+{
+       union jffs2_sum_mem *next;
+       jint16_t nodetype;      /* node type */
+       jint32_t inode;         /* inode number */
+       jint32_t version;       /* inode version */
+       jint32_t offset;        /* offset on jeb */
+       jint32_t totlen;        /* record length */
+} __attribute__((packed));
+
+struct jffs2_sum_dirent_mem
+{
+       union jffs2_sum_mem *next;
+       jint16_t nodetype;      /* == JFFS_NODETYPE_DIRENT */
+       jint32_t totlen;        /* record length */
+       jint32_t offset;        /* ofset on jeb */
+       jint32_t pino;          /* parent inode */
+       jint32_t version;       /* dirent version */
+       jint32_t ino;           /* == zero for unlink */
+       uint8_t nsize;          /* dirent name size */
+       uint8_t type;           /* dirent type */
+       uint8_t name[0];        /* dirent name */
+} __attribute__((packed));
+
+union jffs2_sum_mem
+{
+       struct jffs2_sum_unknown_mem u;
+       struct jffs2_sum_inode_mem i;
+       struct jffs2_sum_dirent_mem d;
+};
+
+/* Summary related information stored in superblock */
+
+struct jffs2_summary
+{
+       uint32_t sum_size;      /* collected summary information for nextblock */
+       uint32_t sum_num;
+       uint32_t sum_padded;
+       union jffs2_sum_mem *sum_list_head;
+       union jffs2_sum_mem *sum_list_tail;
+
+       jint32_t *sum_buf;      /* buffer for writing out summary */
+};
+
+/* Summary marker is stored at the end of every sumarized erase block */
+
+struct jffs2_sum_marker
+{
+       jint32_t offset;        /* offset of the summary node in the jeb */
+       jint32_t magic;         /* == JFFS2_SUM_MAGIC */
+};
+
+#define JFFS2_SUMMARY_FRAME_SIZE (sizeof(struct jffs2_summary_node) + sizeof(struct jffs2_sum_marker))
+
+#ifdef CONFIG_JFFS2_SUMMARY    /* SUMMARY SUPPORT ENABLED */
+
+#define jffs2_sum_active() (1)
+int jffs2_sum_init(struct jffs2_sb_info *c);
+void jffs2_sum_exit(struct jffs2_sb_info *c);
+void jffs2_sum_disable_collecting(struct jffs2_summary *s);
+int jffs2_sum_is_disabled(struct jffs2_summary *s);
+void jffs2_sum_reset_collected(struct jffs2_summary *s);
+void jffs2_sum_move_collected(struct jffs2_sb_info *c, struct jffs2_summary *s);
+int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
+                       unsigned long count,  uint32_t to);
+int jffs2_sum_write_sumnode(struct jffs2_sb_info *c);
+int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
+int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
+int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
+int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+                       uint32_t ofs, uint32_t *pseudo_random);
+
+#else                          /* SUMMARY DISABLED */
+
+#define jffs2_sum_active() (0)
+#define jffs2_sum_init(a) (0)
+#define jffs2_sum_exit(a)
+#define jffs2_sum_disable_collecting(a)
+#define jffs2_sum_is_disabled(a) (0)
+#define jffs2_sum_reset_collected(a)
+#define jffs2_sum_add_kvec(a,b,c,d) (0)
+#define jffs2_sum_move_collected(a,b)
+#define jffs2_sum_write_sumnode(a) (0)
+#define jffs2_sum_add_padding_mem(a,b)
+#define jffs2_sum_add_inode_mem(a,b,c)
+#define jffs2_sum_add_dirent_mem(a,b,c)
+#define jffs2_sum_scan_sumnode(a,b,c,d) (0)
+
+#endif /* CONFIG_JFFS2_SUMMARY */
+
+#endif /* JFFS2_SUMMARY_H */
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: super.c,v 1.108 2005/08/31 13:51:00 havasi Exp $
+ * $Id: super.c,v 1.109 2005/09/07 08:34:55 havasi Exp $
  *
  */
 
        down(&c->alloc_sem);
        jffs2_flush_wbuf_pad(c);
        up(&c->alloc_sem);
+
+       jffs2_sum_exit(c);
+
        jffs2_free_ino_caches(c);
        jffs2_free_raw_node_refs(c);
        if (jffs2_blocks_use_vmalloc(c))
        printk(KERN_INFO "JFFS2 version 2.2."
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
               " (NAND)"
+#endif
+#ifdef CONFIG_JFFS2_SUMMARY
+              " (SUMMARY) "
 #endif
               " (C) 2001-2003 Red Hat, Inc.\n");
 
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: wbuf.c,v 1.97 2005/08/06 04:51:30 nico Exp $
+ * $Id: wbuf.c,v 1.98 2005/09/07 08:34:55 havasi Exp $
  *
  */
 
 
 
        /* ... and get an allocation of space from a shiny new block instead */
-       ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
+       ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len, JFFS2_SUMMARY_NOSUM_SIZE);
        if (ret) {
                printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
                kfree(buf);
 alldone:
        *retlen = donelen;
 
+       if (jffs2_sum_active()) {
+               int res = jffs2_sum_add_kvec(c, invecs, count, (uint32_t) to);
+               if (res)
+                       return res;
+       }
+
        if (c->wbuf_len && ino)
                jffs2_wbuf_dirties_inode(c, ino);
 
        struct kvec vecs[1];
 
        if (!jffs2_is_writebuffered(c))
-               return c->mtd->write(c->mtd, ofs, len, retlen, buf);
+               return jffs2_flash_direct_write(c, ofs, len, retlen, buf);
 
        vecs[0].iov_base = (unsigned char *) buf;
        vecs[0].iov_len = len;
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: write.c,v 1.95 2005/08/17 13:46:23 dedekind Exp $
+ * $Id: write.c,v 1.96 2005/09/07 08:34:55 havasi Exp $
  *
  */
 
                        jffs2_dbg_acct_paranoia_check(c, jeb);
 
                        if (alloc_mode == ALLOC_GC) {
-                               ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
+                               ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs,
+                                                       &dummy, JFFS2_SUMMARY_INODE_SIZE);
                        } else {
                                /* Locking pain */
                                up(&f->sem);
                                jffs2_complete_reservation(c);
                        
-                               ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
+                               ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs,
+                                                       &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE);
                                down(&f->sem);
                        }
 
                        jffs2_dbg_acct_paranoia_check(c, jeb);
 
                        if (alloc_mode == ALLOC_GC) {
-                               ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
+                               ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs,
+                                                       &dummy, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
                        } else {
                                /* Locking pain */
                                up(&f->sem);
                                jffs2_complete_reservation(c);
                        
-                               ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
+                               ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs,
+                                                       &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
                                down(&f->sem);
                        }
 
        retry:
                D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
 
-               ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
+               ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs,
+                                       &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
                if (ret) {
                        D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
                        break;
        /* Try to reserve enough space for both node and dirent. 
         * Just the node will do for now, though 
         */
-       ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL,
+                               JFFS2_SUMMARY_INODE_SIZE);
        D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
        if (ret) {
                up(&f->sem);
 
        up(&f->sem);
        jffs2_complete_reservation(c);
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
                
        if (ret) {
                /* Eep. */
                if (!rd)
                        return -ENOMEM;
 
-               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
+               ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+                                       ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
                if (ret) {
                        jffs2_free_raw_dirent(rd);
                        return ret;
        if (!rd)
                return -ENOMEM;
 
-       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
+                               ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
        if (ret) {
                jffs2_free_raw_dirent(rd);
                return ret;
 
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: writev.c,v 1.6 2004/11/16 20:36:12 dwmw2 Exp $
+ * $Id: writev.c,v 1.7 2005/09/07 08:34:55 havasi Exp $
  *
  */
 
 {
        if (c->mtd->writev)
                return c->mtd->writev(c->mtd, vecs, count, to, retlen);
-       else
+       else {
+               if (jffs2_sum_active()) {
+                       int res;
+
+                       res = jffs2_sum_add_kvec(c, vecs, count, (uint32_t) to);
+                       if (res) {
+                               return res;
+                       }
+               }
+
                return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
+       }
 }
 
+int jffs2_flash_direct_write(struct jffs2_sb_info *c, loff_t ofs, size_t len,
+                       size_t *retlen, const u_char *buf)
+{
+       int ret;
+       ret = c->mtd->write(c->mtd, ofs, len, retlen, buf);
+
+       if (jffs2_sum_active()) {
+               struct kvec vecs[1];
+               int res;
+
+               vecs[0].iov_base = (unsigned char *) buf;
+               vecs[0].iov_len = len;
+
+               res = jffs2_sum_add_kvec(c, vecs, 1, (uint32_t) ofs);
+               if (res) {
+                       return res;
+               }
+       }
+       return ret;
+}
 
  * For licensing information, see the file 'LICENCE' in the 
  * jffs2 directory.
  *
- * $Id: jffs2.h,v 1.36 2005/07/26 13:19:36 havasi Exp $
+ * $Id: jffs2.h,v 1.37 2005/09/07 08:34:55 havasi Exp $
  *
  */
 
 #define JFFS2_EMPTY_BITMASK 0xffff
 #define JFFS2_DIRTY_BITMASK 0x0000
 
+/* Summary node MAGIC marker */
+#define JFFS2_SUM_MAGIC        0x02851885
+
 /* We only allow a single char for length, and 0xFF is empty flash so
    we don't want it confused with a real length. Hence max 254.
 */
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
+#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
+
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
        uint8_t data[0];
 } __attribute__((packed));
 
+struct jffs2_summary_node{
+       jint16_t magic;
+       jint16_t nodetype;      /* = JFFS2_NODETYPE_INODE_SUM */
+       jint32_t totlen;
+       jint32_t hdr_crc;
+       jint32_t sum_num;       /* number of sum entries*/
+       jint32_t cln_mkr;       /* clean marker size, 0 = no cleanmarker */
+       jint32_t padded;        /* sum of the size of padding nodes */
+       jint32_t sum_crc;       /* summary information crc */
+       jint32_t node_crc;      /* node crc */
+       jint32_t sum[0];        /* inode summary info */
+} __attribute__((packed));
+
 union jffs2_node_union {
        struct jffs2_raw_inode i;
        struct jffs2_raw_dirent d;
        struct jffs2_unknown_node u;
+       struct jffs2_summary_node s;
 };
 
 #endif /* __LINUX_JFFS2_H__ */
 
-/* $Id: jffs2_fs_sb.h,v 1.52 2005/05/19 16:12:17 gleixner Exp $ */
+/* $Id: jffs2_fs_sb.h,v 1.53 2005/09/07 08:34:56 havasi Exp $ */
 
 #ifndef _JFFS2_FS_SB
 #define _JFFS2_FS_SB
        uint32_t fsdata_len;
 #endif
 
+       struct jffs2_summary *summary;          /* Summary information */
+
        /* OS-private pointer for getting back to master superblock info */
        void *os_priv;
 };