int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio) { int ret = -EAGAIN; int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE; /* align it */ minsize = PAD(minsize); if (prio == ALLOC_DELETION) blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION; D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); down(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); spin_lock_bh(&c->erase_completion_lock); /* this needs a little more thought */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { int ret; up(&c->alloc_sem); if (c->dirty_size < c->sector_size) { D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size)); spin_unlock_bh(&c->erase_completion_lock); return -ENOSPC; } D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); spin_unlock_bh(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) return ret; if (current->need_resched) schedule(); if (signal_pending(current)) return -EINTR; down(&c->alloc_sem); spin_lock_bh(&c->erase_completion_lock); } ret = jffs2_do_reserve_space(c, minsize, ofs, len); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } spin_unlock_bh(&c->erase_completion_lock); if (ret) up(&c->alloc_sem); return ret; }
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) { int ret = -EAGAIN; minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { ret = jffs2_do_reserve_space(c, minsize, ofs, len); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); return ret; }
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) { int ret = -EAGAIN; int blocksneeded = c->resv_blocks_write; /* align it */ minsize = PAD(minsize); D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); down(&c->alloc_sem); D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); spin_lock(&c->erase_completion_lock); /* this needs a little more thought (true <tglx> :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { int ret; uint32_t dirty, avail; /* calculate real dirty size * 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. * We add unchecked_size here, as we hopefully will find some space to use. * This will affect the sum only once, as gc first finishes checking * of nodes. */ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; if (dirty < c->nospc_dirty_size) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", dirty, c->unchecked_size, c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } /* Calc possibly available space. Possibly available means that we * don't know, if unchecked size contains obsoleted nodes, which could give us some * more usable space. This will affect the sum only once, as gc first finishes checking * of nodes. + Return -ENOSPC, if the maximum possibly available space is less or equal than * blocksneeded * sector_size. * This blocks endless gc looping on a filesystem, which is nearly full, even if * the check above passes. */ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; if ( (avail / c->sector_size) <= blocksneeded) { if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n")); break; } D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", avail, blocksneeded * c->sector_size)); spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); return -ENOSPC; } up(&c->alloc_sem); D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) return ret; cond_resched(); if (signal_pending(current)) return -EINTR; down(&c->alloc_sem); spin_lock(&c->erase_completion_lock); } ret = jffs2_do_reserve_space(c, minsize, ofs, len); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); if (ret) up(&c->alloc_sem); return ret; }