Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
[safe/jmp/linux-2.6] / fs / jffs2 / gc.c
index 477c526..090c556 100644 (file)
@@ -1,14 +1,12 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001-2003 Red Hat, Inc.
+ * Copyright © 2001-2007 Red Hat, Inc.
  *
  * Created by David Woodhouse <dwmw2@infradead.org>
  *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: gc.c,v 1.155 2005/11/07 11:14:39 gleixner Exp $
- *
  */
 
 #include <linux/kernel.h>
@@ -124,10 +122,11 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
        struct jffs2_inode_cache *ic;
        struct jffs2_eraseblock *jeb;
        struct jffs2_raw_node_ref *raw;
+       uint32_t gcblock_dirty;
        int ret = 0, inum, nlink;
        int xattr = 0;
 
-       if (down_interruptible(&c->alloc_sem))
+       if (mutex_lock_interruptible(&c->alloc_sem))
                return -EINTR;
 
        for (;;) {
@@ -144,7 +143,8 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                               c->unchecked_size);
                        jffs2_dbg_dump_block_lists_nolock(c);
                        spin_unlock(&c->erase_completion_lock);
-                       BUG();
+                       mutex_unlock(&c->alloc_sem);
+                       return -ENOSPC;
                }
 
                spin_unlock(&c->erase_completion_lock);
@@ -161,10 +161,11 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                        continue;
                }
 
-               if (!ic->nlink) {
-                       D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
+               if (!ic->pino_nlink) {
+                       D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink/pino zero\n",
                                  ic->ino));
                        spin_unlock(&c->inocache_lock);
+                       jffs2_xattr_delete_inode(c, ic);
                        continue;
                }
                switch(ic->state) {
@@ -189,7 +190,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                         made no progress in this case, but that should be OK */
                        c->checked_ino--;
 
-                       up(&c->alloc_sem);
+                       mutex_unlock(&c->alloc_sem);
                        sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
                        return 0;
 
@@ -209,7 +210,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                        printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
 
                jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
-               up(&c->alloc_sem);
+               mutex_unlock(&c->alloc_sem);
                return ret;
        }
 
@@ -220,9 +221,15 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                jeb = jffs2_find_gc_block(c);
 
        if (!jeb) {
-               D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
+               /* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */
+               if (!list_empty(&c->erase_pending_list)) {
+                       spin_unlock(&c->erase_completion_lock);
+                       mutex_unlock(&c->alloc_sem);
+                       return -EAGAIN;
+               }
+               D1(printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
                spin_unlock(&c->erase_completion_lock);
-               up(&c->alloc_sem);
+               mutex_unlock(&c->alloc_sem);
                return -EIO;
        }
 
@@ -231,11 +238,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
           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));
 
        if (!jeb->used_size) {
-               up(&c->alloc_sem);
+               mutex_unlock(&c->alloc_sem);
                goto eraseit;
        }
 
        raw = jeb->gc_node;
+       gcblock_dirty = jeb->dirty_size;
 
        while(ref_obsolete(raw)) {
                D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
@@ -246,7 +254,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                               jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
                        jeb->gc_node = raw;
                        spin_unlock(&c->erase_completion_lock);
-                       up(&c->alloc_sem);
+                       mutex_unlock(&c->alloc_sem);
                        BUG();
                }
        }
@@ -264,7 +272,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                        /* Just mark it obsolete */
                        jffs2_mark_node_obsolete(c, raw);
                }
-               up(&c->alloc_sem);
+               mutex_unlock(&c->alloc_sem);
                goto eraseit_lock;
        }
 
@@ -275,15 +283,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
         * We can decide whether this node is inode or xattr by ic->class.     */
        if (ic->class == RAWNODE_CLASS_XATTR_DATUM
            || ic->class == RAWNODE_CLASS_XATTR_REF) {
-               BUG_ON(raw->next_in_ino != (void *)ic);
                spin_unlock(&c->erase_completion_lock);
 
                if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
-                       ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+                       ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw);
                } else {
-                       ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+                       ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
                }
-               goto release_sem;
+               goto test_gcnode;
        }
 #endif
 
@@ -333,7 +340,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                */
                printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
                       ic->ino, ic->state);
-               up(&c->alloc_sem);
+               mutex_unlock(&c->alloc_sem);
                spin_unlock(&c->inocache_lock);
                BUG();
 
@@ -344,7 +351,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
                   the alloc_sem() (for marking nodes invalid) so we must
                   drop the alloc_sem before sleeping. */
 
-               up(&c->alloc_sem);
+               mutex_unlock(&c->alloc_sem);
                D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
                          ic->ino, ic->state));
                sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
@@ -377,7 +384,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
                if (ret != -EBADFD) {
                        spin_unlock(&c->inocache_lock);
-                       goto release_sem;
+                       goto test_gcnode;
                }
 
                /* Fall through if it wanted us to, with inocache_lock held */
@@ -391,10 +398,10 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
           it's vaguely possible. */
 
        inum = ic->ino;
-       nlink = ic->nlink;
+       nlink = ic->pino_nlink;
        spin_unlock(&c->inocache_lock);
 
-       f = jffs2_gc_fetch_inode(c, inum, nlink);
+       f = jffs2_gc_fetch_inode(c, inum, !nlink);
        if (IS_ERR(f)) {
                ret = PTR_ERR(f);
                goto release_sem;
@@ -408,8 +415,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 
        jffs2_gc_release_inode(c, f);
 
+ test_gcnode:
+       if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) {
+               /* Eep. This really should never happen. GC is broken */
+               printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node));
+               ret = -ENOSPC;
+       }
  release_sem:
-       up(&c->alloc_sem);
+       mutex_unlock(&c->alloc_sem);
 
  eraseit_lock:
        /* If we've finished this block, start it erasing */
@@ -438,7 +451,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_era
        uint32_t start = 0, end = 0, nrfrags = 0;
        int ret = 0;
 
-       down(&f->sem);
+       mutex_lock(&f->sem);
 
        /* Now we have the lock for this inode. Check that it's still the one at the head
           of the list. */
@@ -518,7 +531,7 @@ static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_era
                }
        }
  upnout:
-       up(&f->sem);
+       mutex_unlock(&f->sem);
 
        return ret;
 }
@@ -557,7 +570,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 
        node = kmalloc(rawlen, GFP_KERNEL);
        if (!node)
-               return -ENOMEM;
+               return -ENOMEM;
 
        ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
        if (!ret && retlen != rawlen)
@@ -599,10 +612,15 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
                        goto bail;
                }
 
+               if (strnlen(node->d.name, node->d.nsize) != node->d.nsize) {
+                       printk(KERN_WARNING "Name in dirent node at 0x%08x contains zeroes\n", ref_offset(raw));
+                       goto bail;
+               }
+
                if (node->d.nsize) {
                        crc = crc32(0, node->d.name, node->d.nsize);
                        if (je32_to_cpu(node->d.name_crc) != crc) {
-                               printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+                               printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
                                       ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
                                goto bail;
                        }
@@ -625,7 +643,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
 
        if (ret || (retlen != rawlen)) {
                printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
-                       rawlen, phys_ofs, ret, retlen);
+                      rawlen, phys_ofs, ret, retlen);
                if (retlen) {
                        jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
                } else {
@@ -834,10 +852,12 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
                /* Prevent the erase code from nicking the obsolete node refs while
                   we're looking at them. I really don't like this extra lock but
                   can't see any alternative. Suggestions on a postcard to... */
-               down(&c->erase_free_sem);
+               mutex_lock(&c->erase_free_sem);
 
                for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
 
+                       cond_resched();
+
                        /* We only care about obsolete ones */
                        if (!(ref_obsolete(raw)))
                                continue;
@@ -885,7 +905,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
                        /* OK. The name really does match. There really is still an older node on
                           the flash which our deletion dirent obsoletes. So we have to write out
                           a new deletion dirent to replace it */
-                       up(&c->erase_free_sem);
+                       mutex_unlock(&c->erase_free_sem);
 
                        D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
                                  ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
@@ -894,7 +914,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
                        return jffs2_garbage_collect_dirent(c, jeb, f, fd);
                }
 
-               up(&c->erase_free_sem);
+               mutex_unlock(&c->erase_free_sem);
                kfree(rd);
        }
 
@@ -1067,7 +1087,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
        return 0;
 }
 
-static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *orig_jeb,
                                       struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
                                       uint32_t start, uint32_t end)
 {