*
* For licensing information, see the file 'LICENCE' in this directory.
*
- * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.102 2003/10/08 17:21:19 dwmw2 Exp $
*
*/
if (list_empty(&c->free_list)) {
+ DECLARE_WAITQUEUE(wait, current);
+
if (!c->nr_erasing_blocks &&
!list_empty(&c->erasable_list)) {
struct jffs2_eraseblock *ejeb;
list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
return -ENOSPC;
}
-
+ /* Make sure this can't deadlock. Someone has to start the erases
+ of erase_pending blocks */
+#ifdef __ECOS
+ /* In eCos, we don't have a handy kernel thread doing the erases for
+ us. We do them ourselves right now. */
+ jffs2_erase_pending_blocks(c);
+#else
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&c->erase_wait, &wait);
+ D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n",
+ c->nr_erasing_blocks, list_empty(&c->erasable_list)?"yes":"no",
+ list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
+ if (!list_empty(&c->erase_pending_list)) {
+ D1(printk(KERN_DEBUG "Triggering pending erases\n"));
+ jffs2_erase_pending_trigger(c);
+ }
spin_unlock(&c->erase_completion_lock);
- /* Don't wait for it; just erase one right now */
- jffs2_erase_pending_blocks(c, 1);
+ schedule();
+ remove_wait_queue(&c->erase_wait, &wait);
spin_lock(&c->erase_completion_lock);
-
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+#endif
/* An erase may have failed, decreasing the
amount of free space available. So we must
restart from the beginning */
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
{
struct jffs2_eraseblock *jeb;
- uint32_t len;
+ uint32_t len = new->totlen;
jeb = &c->blocks[new->flash_offset / c->sector_size];
- len = ref_totlen(c, jeb, new);
-
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
#if 1
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
spin_lock(&c->erase_completion_lock);
if (ref_flags(ref) == REF_UNCHECKED) {
- D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
+ D1(if (unlikely(jeb->unchecked_size < ref->totlen)) {
printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
- ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+ ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
- D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
- jeb->unchecked_size -= ref_totlen(c, jeb, ref);
- c->unchecked_size -= ref_totlen(c, jeb, ref);
+ D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
+ jeb->unchecked_size -= ref->totlen;
+ c->unchecked_size -= ref->totlen;
} else {
- D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
+ D1(if (unlikely(jeb->used_size < ref->totlen)) {
printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
- ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+ ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
- D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
- jeb->used_size -= ref_totlen(c, jeb, ref);
- c->used_size -= ref_totlen(c, jeb, ref);
+ D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref->totlen));
+ jeb->used_size -= ref->totlen;
+ c->used_size -= ref->totlen;
}
// Take care, that wasted size is taken into concern
- if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
+ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) {
D1(printk("Dirtying\n"));
- addedsize = ref_totlen(c, jeb, ref);
- jeb->dirty_size += ref_totlen(c, jeb, ref);
- c->dirty_size += ref_totlen(c, jeb, ref);
+ addedsize = ref->totlen;
+ jeb->dirty_size += ref->totlen;
+ c->dirty_size += ref->totlen;
/* Convert wasted space to dirty, if not a bad block */
if (jeb->wasted_size) {
} else {
D1(printk("Wasting\n"));
addedsize = 0;
- jeb->wasted_size += ref_totlen(c, jeb, ref);
- c->wasted_size += ref_totlen(c, jeb, ref);
+ jeb->wasted_size += ref->totlen;
+ c->wasted_size += ref->totlen;
}
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
if (jffs2_wbuf_dirty(c)) {
D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
+#if 0 /* This check was added to allow us to find places where we added nodes to the lists
+ after dropping the alloc_sem, and it did that just fine. But it also caused us to
+ lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise
+ have needed to. So I suspect it's outlived its usefulness. Thomas? */
+
+ /* We've changed the rules slightly. After
+ writing a node you now mustn't drop the
+ alloc_sem before you've finished all the
+ list management - this is so that when we
+ get here, we know that no other nodes have
+ been written, and the above check on wbuf
+ is valid - wbuf_len is nonzero IFF the node
+ which obsoletes this node is still in the
+ wbuf.
+
+ So we BUG() if that new rule is broken, to
+ make sure we catch it and fix it.
+ */
+ if (!down_trylock(&c->alloc_sem)) {
+ up(&c->alloc_sem);
+ printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n");
+ BUG();
+ }
+#endif
} else {
if (jiffies & 127) {
/* Most of the time, we just erase it immediately. Otherwise we
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
}
- if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
+ if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) {
+ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref->totlen);
return;
}
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
- D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
+ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
return;
}
/* XXX FIXME: This is ugly now */
}
}
#endif /* CONFIG_JFFS2_FS_DEBUG */
-
-int jffs2_thread_should_wake(struct jffs2_sb_info *c)
-{
- int ret = 0;
- uint32_t dirty;
-
- if (c->unchecked_size) {
- D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
- c->unchecked_size, c->checked_ino));
- return 1;
- }
-
- /* dirty_size contains blocks on erase_pending_list
- * those blocks are counted in c->nr_erasing_blocks.
- * If one block is actually erased, it is not longer counted as dirty_space
- * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
- * with c->nr_erasing_blocks * c->sector_size again.
- * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
- * This helps us to force gc and pick eventually a clean block to spread the load.
- */
- dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
-
- if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger &&
- (dirty > c->nospc_dirty_size))
- ret = 1;
-
- D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
-
- return ret;
-}