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; }
static void jffs2_garbage_collect_thread(struct jffs2_sb_info *c) { #define this_thread_should_die() 0 while(!this_thread_should_die()) { while(!jffs2_thread_should_wake(c)) { /* Sleep.... */ continue; } if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printf("No space for garbage collection. Aborting JFFS2 GC thread\n"); break; } } }
static void jffs2_garbage_collect_thread(cyg_addrword_t data) { struct jffs2_sb_info *c=(struct jffs2_sb_info *)data; struct super_block *sb=OFNI_BS_2SFFJ(c); cyg_flag_value_t flag; cyg_mtab_entry *mte; D1(printk("jffs2_garbage_collect_thread START\n")); while(1) { flag=cyg_flag_timed_wait(&sb->s_gc_thread_flags, GC_THREAD_FLAG_TRIG|GC_THREAD_FLAG_STOP, CYG_FLAG_WAITMODE_OR| CYG_FLAG_WAITMODE_CLR, cyg_current_time()+ CYGNUM_JFFS2_GS_THREAD_TICKS); if (flag & GC_THREAD_FLAG_STOP) break; D1(printk("jffs2: GC THREAD GC BEGIN\n")); mte=cyg_fs_root_lookup((cyg_dir *) sb->s_root); CYG_ASSERT(mte, "Bad mount point"); cyg_fs_lock(mte, mte->fs->syncmode); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printf("No space for garbage collection. " "Aborting JFFS2 GC thread\n"); break; } cyg_fs_unlock(mte, mte->fs->syncmode); D1(printk("jffs2: GC THREAD GC END\n")); } D1(printk("jffs2_garbage_collect_thread EXIT\n")); cyg_flag_setbits(&sb->s_gc_thread_flags,GC_THREAD_FLAG_HAS_EXIT); }
static void jffs2_garbage_collect_thread(unsigned long data) { struct jffs2_sb_info *c=(struct jffs2_sb_info *)data; struct super_block *sb=OFNI_BS_2SFFJ(c); cyg_mtab_entry *mte; rt_uint32_t flag = 0; D1(printk("jffs2_garbage_collect_thread START\n")); while(1) { rt_event_recv(&sb->s_gc_thread_flags, GC_THREAD_FLAG_TRIG | GC_THREAD_FLAG_STOP, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, cyg_current_time() + CYGNUM_JFFS2_GS_THREAD_TICKS, &flag); if (flag & GC_THREAD_FLAG_STOP) break; D1(printk("jffs2: GC THREAD GC BEGIN\n")); mte=(cyg_dir *) sb->s_root; RT_ASSERT(mte != NULL); // rt_mutex_take(&mte->fs->syncmode, RT_WAITING_FOREVER); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printf("No space for garbage collection. " "Aborting JFFS2 GC thread\n"); break; } // rt_mutex_release(&mte->fs->syncmode); D1(printk("jffs2: GC THREAD GC END\n")); } D1(printk("jffs2_garbage_collect_thread EXIT\n")); rt_event_send(&sb->s_gc_thread_flags,GC_THREAD_FLAG_HAS_EXIT); }
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; }
static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; daemonize("jffs2_gcd_mtd%d", c->mtd->index); allow_signal(SIGKILL); allow_signal(SIGSTOP); allow_signal(SIGCONT); c->gc_task = current; complete(&c->gc_thread_start); set_user_nice(current, 10); for (;;) { allow_signal(SIGHUP); if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); /* Yes, there's a race here; we checked jffs2_thread_should_wake() before setting current->state to TASK_INTERRUPTIBLE. But it doesn't matter - We don't care if we miss a wakeup, because the GC thread is only an optimisation anyway. */ schedule(); } if (try_to_freeze()) continue; cond_resched(); /* Put_super will send a SIGKILL and then wait on the sem. */ while (signal_pending(current)) { siginfo_t info; unsigned long signr; signr = dequeue_signal_lock(current, ¤t->blocked, &info); switch(signr) { case SIGSTOP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); set_current_state(TASK_STOPPED); schedule(); break; case SIGKILL: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); goto die; case SIGHUP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n")); break; default: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); } } /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ disallow_signal(SIGHUP); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n"); goto die; } } die: spin_lock(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); }
static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; allow_signal(SIGKILL); allow_signal(SIGSTOP); allow_signal(SIGCONT); c->gc_task = current; complete(&c->gc_thread_start); set_user_nice(current, 10); set_freezable(); for (;;) { allow_signal(SIGHUP); again: spin_lock(&c->erase_completion_lock); if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); spin_unlock(&c->erase_completion_lock); jffs2_dbg(1, "%s(): sleeping...\n", __func__); schedule(); } else spin_unlock(&c->erase_completion_lock); /* Problem - immediately after bootup, the GCD spends a lot * of time in places like jffs2_kill_fragtree(); so much so * that userspace processes (like gdm and X) are starved * despite plenty of cond_resched()s and renicing. Yield() * doesn't help, either (presumably because userspace and GCD * are generally competing for a higher latency resource - * disk). * This forces the GCD to slow the hell down. Pulling an * inode in with read_inode() is much preferable to having * the GC thread get there first. */ schedule_timeout_interruptible(msecs_to_jiffies(50)); if (kthread_should_stop()) { jffs2_dbg(1, "%s(): kthread_stop() called\n", __func__); goto die; } /* Put_super will send a SIGKILL and then wait on the sem. */ while (signal_pending(current) || freezing(current)) { siginfo_t info; unsigned long signr; if (try_to_freeze()) goto again; signr = dequeue_signal_lock(current, ¤t->blocked, &info); switch(signr) { case SIGSTOP: jffs2_dbg(1, "%s(): SIGSTOP received\n", __func__); set_current_state(TASK_STOPPED); schedule(); break; case SIGKILL: jffs2_dbg(1, "%s(): SIGKILL received\n", __func__); goto die; case SIGHUP: jffs2_dbg(1, "%s(): SIGHUP received\n", __func__); break; default: jffs2_dbg(1, "%s(): signal %ld received\n", __func__, signr); } } /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ disallow_signal(SIGHUP); jffs2_dbg(1, "%s(): pass\n", __func__); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { pr_notice("No space for garbage collection. Aborting GC thread\n"); goto die; } } die: spin_lock(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); }
static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; daemonize(); current->tty = NULL; c->gc_task = current; up(&c->gc_thread_start); sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index); /* FIXME in the 2.2 backport */ current->nice = 10; for (;;) { spin_lock_irq(¤t->sigmask_lock); siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); if (!thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); } if (current->need_resched) schedule(); /* Put_super will send a SIGKILL and then wait on the sem. */ while (signal_pending(current)) { siginfo_t info; unsigned long signr; spin_lock_irq(¤t->sigmask_lock); signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->sigmask_lock); switch(signr) { case SIGSTOP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n")); set_current_state(TASK_STOPPED); schedule(); break; case SIGKILL: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); spin_lock_bh(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock_bh(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); case SIGHUP: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n")); break; default: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); } } /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ spin_lock_irq(¤t->sigmask_lock); siginitsetinv (¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); jffs2_garbage_collect_pass(c); } }
static int jffs2_garbage_collect_thread(void *_c) { struct jffs2_sb_info *c = _c; allow_signal(SIGKILL); allow_signal(SIGSTOP); allow_signal(SIGCONT); c->gc_task = current; complete(&c->gc_thread_start); set_user_nice(current, 10); set_freezable(); for (;;) { allow_signal(SIGHUP); again: spin_lock(&c->erase_completion_lock); if (!jffs2_thread_should_wake(c)) { set_current_state (TASK_INTERRUPTIBLE); spin_unlock(&c->erase_completion_lock); jffs2_dbg(1, "%s(): sleeping...\n", __func__); schedule(); } else spin_unlock(&c->erase_completion_lock); schedule_timeout_interruptible(msecs_to_jiffies(50)); if (kthread_should_stop()) { jffs2_dbg(1, "%s(): kthread_stop() called\n", __func__); goto die; } while (signal_pending(current) || freezing(current)) { siginfo_t info; unsigned long signr; if (try_to_freeze()) goto again; signr = dequeue_signal_lock(current, ¤t->blocked, &info); switch(signr) { case SIGSTOP: jffs2_dbg(1, "%s(): SIGSTOP received\n", __func__); set_current_state(TASK_STOPPED); schedule(); break; case SIGKILL: jffs2_dbg(1, "%s(): SIGKILL received\n", __func__); goto die; case SIGHUP: jffs2_dbg(1, "%s(): SIGHUP received\n", __func__); break; default: jffs2_dbg(1, "%s(): signal %ld received\n", __func__, signr); } } disallow_signal(SIGHUP); jffs2_dbg(1, "%s(): pass\n", __func__); if (jffs2_garbage_collect_pass(c) == -ENOSPC) { pr_notice("No space for garbage collection. Aborting GC thread\n"); goto die; } } die: spin_lock(&c->erase_completion_lock); c->gc_task = NULL; spin_unlock(&c->erase_completion_lock); complete_and_exit(&c->gc_thread_exit, 0); }