2005-04-17 02:20:36 +04:00
/*
* JFFS2 - - Journalling Flash File System , Version 2.
*
2007-04-25 17:16:47 +04:00
* Copyright © 2001 - 2007 Red Hat , Inc .
2010-08-08 17:15:22 +04:00
* Copyright © 2004 - 2010 David Woodhouse < dwmw2 @ infradead . org >
2005-04-17 02:20:36 +04:00
*
* Created by David Woodhouse < dwmw2 @ infradead . org >
*
* For licensing information , see the file ' LICENCE ' in this directory .
*
*/
# include <linux/kernel.h>
# include <linux/jffs2.h>
# include <linux/mtd/mtd.h>
# include <linux/completion.h>
2005-10-31 02:03:48 +03:00
# include <linux/sched.h>
2006-12-07 07:34:23 +03:00
# include <linux/freezer.h>
2009-06-18 00:08:55 +04:00
# include <linux/kthread.h>
2005-04-17 02:20:36 +04:00
# include "nodelist.h"
static int jffs2_garbage_collect_thread ( void * ) ;
void jffs2_garbage_collect_trigger ( struct jffs2_sb_info * c )
{
2010-05-19 20:00:10 +04:00
assert_spin_locked ( & c - > erase_completion_lock ) ;
2007-07-10 13:01:22 +04:00
if ( c - > gc_task & & jffs2_thread_should_wake ( c ) )
send_sig ( SIGHUP , c - > gc_task , 1 ) ;
2005-04-17 02:20:36 +04:00
}
/* This must only ever be called when no GC thread is currently running */
int jffs2_start_garbage_collect_thread ( struct jffs2_sb_info * c )
{
2009-06-18 00:08:55 +04:00
struct task_struct * tsk ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
2006-04-01 03:15:35 +04:00
BUG_ON ( c - > gc_task ) ;
2005-04-17 02:20:36 +04:00
2005-05-19 20:18:11 +04:00
init_completion ( & c - > gc_thread_start ) ;
2005-04-17 02:20:36 +04:00
init_completion ( & c - > gc_thread_exit ) ;
2009-06-18 00:08:55 +04:00
tsk = kthread_run ( jffs2_garbage_collect_thread , c , " jffs2_gcd_mtd%d " , c - > mtd - > index ) ;
if ( IS_ERR ( tsk ) ) {
printk ( KERN_WARNING " fork failed for JFFS2 garbage collect thread: %ld \n " , - PTR_ERR ( tsk ) ) ;
2005-04-17 02:20:36 +04:00
complete ( & c - > gc_thread_exit ) ;
2009-06-18 00:08:55 +04:00
ret = PTR_ERR ( tsk ) ;
2005-04-17 02:20:36 +04:00
} else {
/* Wait for it... */
2009-06-18 00:08:55 +04:00
D1 ( printk ( KERN_DEBUG " JFFS2: Garbage collect thread is pid %d \n " , tsk - > pid ) ) ;
2005-05-19 20:18:11 +04:00
wait_for_completion ( & c - > gc_thread_start ) ;
2009-06-18 00:08:55 +04:00
ret = tsk - > pid ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:16:07 +03:00
2005-04-17 02:20:36 +04:00
return ret ;
}
void jffs2_stop_garbage_collect_thread ( struct jffs2_sb_info * c )
{
2005-05-21 01:37:15 +04:00
int wait = 0 ;
2005-04-17 02:20:36 +04:00
spin_lock ( & c - > erase_completion_lock ) ;
if ( c - > gc_task ) {
D1 ( printk ( KERN_DEBUG " jffs2: Killing GC task %d \n " , c - > gc_task - > pid ) ) ;
send_sig ( SIGKILL , c - > gc_task , 1 ) ;
2005-05-21 01:37:15 +04:00
wait = 1 ;
2005-04-17 02:20:36 +04:00
}
spin_unlock ( & c - > erase_completion_lock ) ;
2005-05-21 01:37:15 +04:00
if ( wait )
wait_for_completion ( & c - > gc_thread_exit ) ;
2005-04-17 02:20:36 +04:00
}
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 ;
2005-05-19 20:18:11 +04:00
complete ( & c - > gc_thread_start ) ;
2005-04-17 02:20:36 +04:00
set_user_nice ( current , 10 ) ;
2007-07-17 15:03:35 +04:00
set_freezable ( ) ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
allow_signal ( SIGHUP ) ;
2007-06-28 22:49:36 +04:00
again :
2008-10-31 17:52:24 +03:00
spin_lock ( & c - > erase_completion_lock ) ;
2005-04-17 02:20:36 +04:00
if ( ! jffs2_thread_should_wake ( c ) ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
2008-10-31 17:52:24 +03:00
spin_unlock ( & c - > erase_completion_lock ) ;
2005-04-17 02:20:36 +04:00
D1 ( printk ( KERN_DEBUG " jffs2_garbage_collect_thread sleeping... \n " ) ) ;
schedule ( ) ;
2008-10-31 17:52:24 +03:00
} else
spin_unlock ( & c - > erase_completion_lock ) ;
2005-04-17 02:20:36 +04:00
2009-02-12 00:27:02 +03:00
/* 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 ) ) ;
2005-04-17 02:20:36 +04:00
2009-06-18 00:08:55 +04:00
if ( kthread_should_stop ( ) ) {
D1 ( printk ( KERN_DEBUG " jffs2_garbage_collect_thread(): kthread_stop() called. \n " ) ) ;
goto die ;
}
2005-11-07 14:16:07 +03:00
/* Put_super will send a SIGKILL and then wait on the sem.
2005-04-17 02:20:36 +04:00
*/
2007-12-04 03:11:09 +03:00
while ( signal_pending ( current ) | | freezing ( current ) ) {
2005-04-17 02:20:36 +04:00
siginfo_t info ;
unsigned long signr ;
2007-06-28 22:49:36 +04:00
if ( try_to_freeze ( ) )
goto again ;
2005-04-17 02:20:36 +04:00
signr = dequeue_signal_lock ( current , & current - > 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 ) ;
}