/*
* 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>
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 (;;) {
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);
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);
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;
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;
}
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;
}
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)));
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();
}
}
/* Just mark it obsolete */
jffs2_mark_node_obsolete(c, raw);
}
- up(&c->alloc_sem);
+ mutex_unlock(&c->alloc_sem);
goto eraseit_lock;
}
} else {
ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
}
- goto release_sem;
+ goto test_gcnode;
}
#endif
*/
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();
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);
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 */
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;
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 */
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. */
}
}
upnout:
- up(&f->sem);
+ mutex_unlock(&f->sem);
return ret;
}
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)
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;
}
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 {
/* 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;
/* 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)));
return jffs2_garbage_collect_dirent(c, jeb, f, fd);
}
- up(&c->erase_free_sem);
+ mutex_unlock(&c->erase_free_sem);
kfree(rd);
}
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)
{