* jffs2_reserve_space - request physical space to write nodes to flash
* @c: superblock info
* @minsize: Minimum acceptable size of allocation
+ * @ofs: Returned value of node offset
* @len: Returned value of allocation length
* @prio: Allocation type - ALLOC_{NORMAL,DELETION}
*
* Requests a block of physical space on the flash. Returns zero for success
- * and puts 'len' into the appropriate place, or returns -ENOSPC or other
- * error if appropriate. Doesn't return len since that's
+ * and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC
+ * or other error if appropriate.
*
* If it returns zero, jffs2_reserve_space() also downs the per-filesystem
* allocation semaphore, to prevent more than one allocation from being
*/
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
- uint32_t *len, uint32_t sumsize);
+ uint32_t *ofs, uint32_t *len, uint32_t sumsize);
-int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
+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;
spin_lock(&c->erase_completion_lock);
}
- ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
}
}
spin_unlock(&c->erase_completion_lock);
- if (!ret)
- ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
if (ret)
up(&c->alloc_sem);
return ret;
}
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
- uint32_t *len, 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 ret = -EAGAIN;
minsize = PAD(minsize);
spin_lock(&c->erase_completion_lock);
while(ret == -EAGAIN) {
- ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
+ 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));
}
}
spin_unlock(&c->erase_completion_lock);
- if (!ret)
- ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
-
return ret;
}
struct jffs2_eraseblock *ejeb;
ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
- list_move_tail(&ejeb->list, &c->erase_pending_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",
}
/* Called with alloc sem _and_ erase_completion_lock */
-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
- uint32_t *len, uint32_t sumsize)
+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 */
+ uint32_t reserved_size; /* for summary information at the end of the jeb */
int ret;
restart:
}
} else {
if (jeb && minsize > jeb->free_size) {
- uint32_t waste;
-
/* Skip the end of this block and file it as having some dirty space */
/* If there's a pending write to it, flush now */
goto restart;
}
- spin_unlock(&c->erase_completion_lock);
-
- ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
- if (ret)
- return ret;
- /* Just lock it again and continue. Nothing much can change because
- we hold c->alloc_sem anyway. In fact, it's not entirely clear why
- we hold c->erase_completion_lock in the majority of this function...
- but that's a question for another (more caffeine-rich) day. */
- spin_lock(&c->erase_completion_lock);
-
- waste = jeb->free_size;
- jffs2_link_node_ref(c, jeb,
- (jeb->offset + c->sector_size - waste) | REF_OBSOLETE,
- waste, NULL);
- /* FIXME: that made it count as dirty. Convert to wasted */
- jeb->dirty_size -= waste;
- c->dirty_size -= waste;
- jeb->wasted_size += waste;
- c->wasted_size += waste;
+ c->wasted_size += jeb->free_size;
+ c->free_size -= jeb->free_size;
+ jeb->wasted_size += jeb->free_size;
+ jeb->free_size = 0;
jffs2_close_nextblock(c, jeb);
jeb = NULL;
}
/* 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 - reserved_size;
if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
spin_lock(&c->erase_completion_lock);
}
- D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n",
- *len, jeb->offset + (c->sector_size - jeb->free_size)));
+ D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
return 0;
}
* @c: superblock info
* @new: new node reference to add
* @len: length of this physical node
+ * @dirty: dirty flag for new node
*
* Should only be used to report nodes for which space has been allocated
* by jffs2_reserve_space.
* Must be called with the alloc_sem held.
*/
-struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
- uint32_t ofs, uint32_t len,
- struct jffs2_inode_cache *ic)
+int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
{
struct jffs2_eraseblock *jeb;
- struct jffs2_raw_node_ref *new;
+ uint32_t len;
- jeb = &c->blocks[ofs / c->sector_size];
+ 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",
- ofs & ~3, ofs & 3, len));
+ 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
- /* Allow non-obsolete nodes only to be added at the end of c->nextblock,
- if c->nextblock is set. Note that wbuf.c will file obsolete nodes
- even after refiling c->nextblock */
- if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
- && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
+ /* we could get some obsolete nodes after nextblock was refiled
+ in wbuf.c */
+ if ((c->nextblock || !ref_obsolete(new))
+ &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) {
printk(KERN_WARNING "argh. node added in wrong place\n");
- return ERR_PTR(-EINVAL);
+ jffs2_free_raw_node_ref(new);
+ return -EINVAL;
}
#endif
spin_lock(&c->erase_completion_lock);
- new = jffs2_link_node_ref(c, jeb, ofs, len, ic);
+ if (!jeb->first_node)
+ jeb->first_node = new;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = new;
+ jeb->last_node = new;
+
+ jeb->free_size -= len;
+ c->free_size -= len;
+ if (ref_obsolete(new)) {
+ jeb->dirty_size += len;
+ c->dirty_size += len;
+ } else {
+ jeb->used_size += len;
+ c->used_size += len;
+ }
if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
spin_unlock(&c->erase_completion_lock);
- return new;
+ return 0;
}
struct jffs2_unknown_node n;
int ret, addedsize;
size_t retlen;
- uint32_t freed_len;
- if(unlikely(!ref)) {
+ if(!ref) {
printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
return;
}
spin_lock(&c->erase_completion_lock);
- freed_len = ref_totlen(c, jeb, ref);
-
if (ref_flags(ref) == REF_UNCHECKED) {
- D1(if (unlikely(jeb->unchecked_size < freed_len)) {
+ D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
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",
- freed_len, blocknr, ref->flash_offset, jeb->used_size);
+ ref_totlen(c, jeb, ref), 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), freed_len));
- jeb->unchecked_size -= freed_len;
- c->unchecked_size -= freed_len;
+ 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);
} else {
- D1(if (unlikely(jeb->used_size < freed_len)) {
+ D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
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",
- freed_len, blocknr, ref->flash_offset, jeb->used_size);
+ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
BUG();
})
- D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), freed_len));
- jeb->used_size -= freed_len;
- c->used_size -= freed_len;
+ 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);
}
// Take care, that wasted size is taken into concern
- if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) {
- D1(printk("Dirtying\n"));
- addedsize = freed_len;
- jeb->dirty_size += freed_len;
- c->dirty_size += freed_len;
+ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
+ D1(printk(KERN_DEBUG "Dirtying\n"));
+ addedsize = ref_totlen(c, jeb, ref);
+ jeb->dirty_size += ref_totlen(c, jeb, ref);
+ c->dirty_size += ref_totlen(c, jeb, ref);
/* Convert wasted space to dirty, if not a bad block */
if (jeb->wasted_size) {
}
}
} else {
- D1(printk("Wasting\n"));
+ D1(printk(KERN_DEBUG "Wasting\n"));
addedsize = 0;
- jeb->wasted_size += freed_len;
- c->wasted_size += freed_len;
+ jeb->wasted_size += ref_totlen(c, jeb, ref);
+ c->wasted_size += ref_totlen(c, jeb, ref);
}
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
/* The erase_free_sem is locked, and has been since before we marked the node obsolete
and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
- by jffs2_free_jeb_node_refs() in erase.c. Which is nice. */
+ by jffs2_free_all_node_refs() in erase.c. Which is nice. */
D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
goto out_erase_sem;
}
- if (PAD(je32_to_cpu(n.totlen)) != PAD(freed_len)) {
- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), freed_len);
+ 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));
goto out_erase_sem;
}
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
*p = ref->next_in_ino;
ref->next_in_ino = NULL;
- switch (ic->class) {
-#ifdef CONFIG_JFFS2_FS_XATTR
- case RAWNODE_CLASS_XATTR_DATUM:
- jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
- break;
- case RAWNODE_CLASS_XATTR_REF:
- jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
- break;
-#endif
- default:
- if (ic->nodes == (void *)ic && ic->nlink == 0)
- jffs2_del_ino_cache(c, ic);
- break;
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
+ jffs2_del_ino_cache(c, ic);
+
+ spin_unlock(&c->erase_completion_lock);
+ }
+
+
+ /* Merge with the next node in the physical list, if there is one
+ and if it's also obsolete and if it doesn't belong to any inode */
+ if (ref->next_phys && ref_obsolete(ref->next_phys) &&
+ !ref->next_phys->next_in_ino) {
+ struct jffs2_raw_node_ref *n = ref->next_phys;
+
+ spin_lock(&c->erase_completion_lock);
+
+ ref->__totlen += n->__totlen;
+ ref->next_phys = n->next_phys;
+ if (jeb->last_node == n) jeb->last_node = ref;
+ if (jeb->gc_node == n) {
+ /* gc will be happy continuing gc on this node */
+ jeb->gc_node=ref;
}
spin_unlock(&c->erase_completion_lock);
+
+ jffs2_free_raw_node_ref(n);
}
+ /* Also merge with the previous node in the list, if there is one
+ and that one is obsolete */
+ if (ref != jeb->first_node ) {
+ struct jffs2_raw_node_ref *p = jeb->first_node;
+
+ spin_lock(&c->erase_completion_lock);
+
+ while (p->next_phys != ref)
+ p = p->next_phys;
+
+ if (ref_obsolete(p) && !ref->next_in_ino) {
+ p->__totlen += ref->__totlen;
+ if (jeb->last_node == ref) {
+ jeb->last_node = p;
+ }
+ if (jeb->gc_node == ref) {
+ /* gc will be happy continuing gc on this node */
+ jeb->gc_node=p;
+ }
+ p->next_phys = ref->next_phys;
+ jffs2_free_raw_node_ref(ref);
+ }
+ spin_unlock(&c->erase_completion_lock);
+ }
out_erase_sem:
up(&c->erase_free_sem);
}