diff -ruNp 211-refrigerator.patch-old/include/linux/sched.h 211-refrigerator.patch-new/include/linux/sched.h --- 211-refrigerator.patch-old/include/linux/sched.h 2005-04-22 23:32:23.000000000 +1000 +++ 211-refrigerator.patch-new/include/linux/sched.h 2005-05-02 16:53:47.000000000 +1000 @@ -1248,6 +1248,7 @@ extern void normalize_rt_tasks(void); extern void refrigerator(unsigned long); extern int freeze_processes(void); extern void thaw_processes(void); +extern unsigned int refrigerator_on; static inline int try_to_freeze(unsigned long refrigerator_flags) { @@ -1261,6 +1262,7 @@ static inline int try_to_freeze(unsigned static inline void refrigerator(unsigned long flag) {} static inline int freeze_processes(void) { BUG(); return 0; } static inline void thaw_processes(void) {} +#define refrigerator_on (0) static inline int try_to_freeze(unsigned long refrigerator_flags) { diff -ruNp 211-refrigerator.patch-old/kernel/exit.c 211-refrigerator.patch-new/kernel/exit.c --- 211-refrigerator.patch-old/kernel/exit.c 2005-05-02 16:53:40.000000000 +1000 +++ 211-refrigerator.patch-new/kernel/exit.c 2005-05-02 16:13:38.000000000 +1000 @@ -789,6 +789,8 @@ fastcall NORET_TYPE void do_exit(long co panic("Attempted to kill init!"); if (tsk->io_context) exit_io_context(); + if (unlikely(refrigerator_on)) + refrigerator(0); if (unlikely(current->ptrace & PT_TRACE_EXIT)) { current->ptrace_message = code; diff -ruNp 211-refrigerator.patch-old/kernel/fork.c 211-refrigerator.patch-new/kernel/fork.c --- 211-refrigerator.patch-old/kernel/fork.c 2005-04-22 23:32:24.000000000 +1000 +++ 211-refrigerator.patch-new/kernel/fork.c 2005-05-02 16:13:38.000000000 +1000 @@ -1200,6 +1200,9 @@ long do_fork(unsigned long clone_flags, int trace = 0; long pid = alloc_pidmap(); + if (unlikely(refrigerator_on)) + refrigerator(0); + if (pid < 0) return -EAGAIN; if (unlikely(current->ptrace)) { diff -ruNp 211-refrigerator.patch-old/kernel/kmod.c 211-refrigerator.patch-new/kernel/kmod.c --- 211-refrigerator.patch-old/kernel/kmod.c 2005-05-02 16:53:40.000000000 +1000 +++ 211-refrigerator.patch-new/kernel/kmod.c 2005-05-02 16:13:38.000000000 +1000 @@ -237,7 +237,7 @@ int call_usermodehelper(char *path, char }; DECLARE_WORK(work, __call_usermodehelper, &sub_info); - if (!khelper_wq) + if ((!khelper_wq) || (refrigerator_on)) return -EBUSY; if (path[0] == '\0') diff -ruNp 211-refrigerator.patch-old/kernel/power/process.c 211-refrigerator.patch-new/kernel/power/process.c --- 211-refrigerator.patch-old/kernel/power/process.c 2004-12-10 14:26:31.000000000 +1100 +++ 211-refrigerator.patch-new/kernel/power/process.c 2005-05-02 16:49:35.000000000 +1000 @@ -1,121 +1,317 @@ /* - * drivers/power/process.c - Functions for starting/stopping processes on - * suspend transitions. + * kernel/power/process.c * - * Originally from swsusp. + * Copyright (C) 1998-2001 Gabor Kuti + * Copyright (C) 1998,2001,2002 Pavel Machek + * Copyright (C) 2002-2003 Florent Chabaud + * Copyright (C) 2002-2005 Nigel Cunningham + * + * This file is released under the GPLv2. + * + * Freeze_and_free contains the routines software suspend uses to freeze other + * processes during the suspend cycle. + * + * Under high I/O load, we need to be careful about the order in which we + * freeze processes. If we freeze processes in the wrong order, we could + * deadlock others. The freeze_order array this specifies the order in which + * critical processes are frozen. All others are suspended after these have + * entered the refrigerator. + * + * Other processes on an SMP system have been taken care of already when we + * use these routines. We assume there are no races or locking issues. */ +#include +//#include +#include +#include + +unsigned int refrigerator_on = 0; -#undef DEBUG +static unsigned int sync_freeze = 0; -#include -#include -#include -#include +atomic_t __nosavedata suspend_cpu_counter = { 0 }; -/* - * Timeout for stopping processes - */ -#define TIMEOUT (6 * HZ) +/* Timeouts when freezing */ +#define FREEZER_TOTAL_TIMEOUT (5 * HZ) +#define FREEZER_CHECK_TIMEOUT (HZ / 10) +/** + * refrigerator - idle routine for frozen processes + * @flag: unsigned long, non zero if signals to be flushed. + * + * A routine for threads which should not do work during suspend + * to enter and spin in until the process is finished. + */ -static inline int freezeable(struct task_struct * p) +void refrigerator(unsigned long flag) { - if ((p == current) || + unsigned long flags; + long save; + + if (unlikely(current->flags & PF_NOFREEZE)) { + current->flags &= ~PF_FREEZE; + spin_lock_irqsave(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + return; + } + + /* You need correct to work with real-time processes. + OTOH, this way one process may see (via /proc/) some other + process in stopped state (and thereby discovered we were + suspended. We probably do not care). + */ + if ((flag) && (current->flags & PF_FREEZE)) { + + spin_lock_irqsave(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + } + + if (refrigerator_on) { + save = current->state; + current->flags |= PF_FROZEN; + while (current->flags & PF_FROZEN) { + current->state = TASK_STOPPED; + schedule(); + if (flag) { + spin_lock_irqsave( + ¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore( + ¤t->sighand->siglock, flags); + } + } + current->state = save; + } + current->flags &= ~PF_FREEZE; + spin_lock_irqsave(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); +} + +/* + * to_be_frozen + * + * Description: Determine whether a process should be frozen yet. + * Parameters: struct task_struct * The process to consider. + * int Which group of processes to consider. + * Returns: int 0 if don't freeze yet, otherwise do. + */ +static int to_be_frozen(struct task_struct * p, int type_being_frozen) { + + if ((p == current) || (p->flags & PF_NOFREEZE) || (p->exit_state == EXIT_ZOMBIE) || (p->exit_state == EXIT_DEAD) || (p->state == TASK_STOPPED) || (p->state == TASK_TRACED)) return 0; + if ((!(p->mm)) && (type_being_frozen < 3)) + return 0; + if ((p->flags & PF_SYNCTHREAD) && (type_being_frozen == 1)) + return 0; + if ((p->state == TASK_UNINTERRUPTIBLE) && (type_being_frozen == 3)) + return 0; return 1; } -/* Refrigerator is place where frozen processes are stored :-). */ -void refrigerator(unsigned long flag) -{ - /* Hmm, should we be allowed to suspend when there are realtime - processes around? */ - long save; - save = current->state; - current->state = TASK_UNINTERRUPTIBLE; - pr_debug("%s entered refrigerator\n", current->comm); - printk("="); - current->flags &= ~PF_FREEZE; +/* + * num_to_be_frozen + * + * Description: Determine how many processes of our type are still to be + * frozen. As a side effect, update the progress bar too. + * Parameters: int Which type we are trying to freeze. + */ +static int num_to_be_frozen(int type_being_frozen) { + + struct task_struct *p, *g; + int todo_this_type = 0, total_todo = 0; + int total_threads = 0; + + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (to_be_frozen(p, type_being_frozen)) { + todo_this_type++; + total_todo++; + } else if (to_be_frozen(p, 3)) + total_todo++; + total_threads++; + } while_each_thread(g, p); + read_unlock(&tasklist_lock); - spin_lock_irq(¤t->sighand->siglock); - recalc_sigpending(); /* We sent fake signal, clean it up */ - spin_unlock_irq(¤t->sighand->siglock); - - current->flags |= PF_FROZEN; - while (current->flags & PF_FROZEN) - schedule(); - pr_debug("%s left refrigerator\n", current->comm); - current->state = save; + return todo_this_type; } -/* 0 = success, else # of processes that we failed to stop */ -int freeze_processes(void) +/* + * freeze_threads + * + * Freeze a set of threads. + * + * Types: + * 1: User threads not syncing. + * 2: Remaining user threads. + * 3: Kernel threads. + */ + +static int freeze_threads(int type) { - int todo; - unsigned long start_time; - struct task_struct *g, *p; - - printk( "Stopping tasks: " ); - start_time = jiffies; + struct task_struct *p, *g; + unsigned long start_time = jiffies; + int result = 0, still_to_do; + do { - todo = 0; + int numsignalled = 0; + + preempt_disable(); + + local_irq_disable(); + read_lock(&tasklist_lock); + + /* + * Signal the processes. + * + * We signal them every time through. Otherwise pdflush - + * and maybe other processes - might never enter the + * fridge. + * + * NB: We're inside an SMP pause. Our printks are unsafe. + * They're only here for debugging. + * + */ + do_each_thread(g, p) { unsigned long flags; - if (!freezeable(p)) - continue; - if ((p->flags & PF_FROZEN) || - (p->state == TASK_TRACED) || - (p->state == TASK_STOPPED)) + if (!to_be_frozen(p, type)) continue; - - /* FIXME: smp problem here: we may not access other process' flags - without locking */ + + numsignalled++; p->flags |= PF_FREEZE; spin_lock_irqsave(&p->sighand->siglock, flags); signal_wake_up(p, 0); spin_unlock_irqrestore(&p->sighand->siglock, flags); - todo++; } while_each_thread(g, p); + read_unlock(&tasklist_lock); - yield(); /* Yield is okay here */ - if (time_after(jiffies, start_time + TIMEOUT)) { - printk( "\n" ); - printk(KERN_ERR " stopping tasks failed (%d tasks remaining)\n", todo ); - return todo; - } - } while(todo); + + preempt_enable(); + + local_irq_enable(); + + /* + * Sleep. + */ + set_task_state(current, TASK_INTERRUPTIBLE); + schedule_timeout(FREEZER_CHECK_TIMEOUT); + + still_to_do = num_to_be_frozen(type); + } while(still_to_do && !time_after(jiffies, start_time + FREEZER_TOTAL_TIMEOUT)); + + /* + * Did we time out? See if we failed to freeze processes as well. + * + */ + if ((time_after(jiffies, start_time + FREEZER_TOTAL_TIMEOUT)) && (still_to_do)) { + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (!to_be_frozen(p, type)) + continue; + + if (!result) { + printk(KERN_ERR "Stopping tasks failed.\n"); + printk(KERN_ERR "Tasks that refused to be " + "refrigerated and haven't since exited:\n"); + result = 1; + } + + if (p->flags & PF_FREEZE) { + printk(" - %s (#%d) signalled but " + "didn't enter refrigerator.\n", + p->comm, p->pid); + } else + printk(" - %s (#%d) wasn't " + "signalled.\n", + p->comm, p->pid); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); + + show_state(); + } + + return result; +} + +/* + * freeze_processes - Freeze processes prior to saving an image of memory. + * + * Return value: 0 = success, else # of processes that we failed to stop. + */ +extern asmlinkage long sys_sync(void); + +/* Freeze_processes. + * Debugging output is still printed. + */ +int freeze_processes(void) +{ + int showidlelist, result = 0; + + showidlelist = 1; + + refrigerator_on = 1; + + /* First, freeze all userspace, non syncing threads. */ + if (freeze_threads(1)) + goto aborting; - printk( "|\n" ); - BUG_ON(in_atomic()); - return 0; + /* Now freeze processes that were syncing and are still running */ + if (freeze_threads(2)) + goto aborting; + + /* Now do our own sync, just in case one wasn't running already */ + sys_sync(); + + sync_freeze = 1; + + /* Freeze kernel threads */ + if (freeze_threads(3)) + goto aborting; + +out: + return result; + +aborting: + result = -1; + thaw_processes(); + goto out; } void thaw_processes(void) { - struct task_struct *g, *p; + struct task_struct *p, *g; + + sync_freeze = 0; + refrigerator_on = 0; + + preempt_disable(); + + local_irq_disable(); - printk( "Restarting tasks..." ); read_lock(&tasklist_lock); + do_each_thread(g, p) { - if (!freezeable(p)) - continue; if (p->flags & PF_FROZEN) { p->flags &= ~PF_FROZEN; wake_up_process(p); - } else - printk(KERN_INFO " Strange, %s not stopped\n", p->comm ); + } } while_each_thread(g, p); read_unlock(&tasklist_lock); - schedule(); - printk( " done\n" ); + + preempt_enable(); + + local_irq_enable(); } EXPORT_SYMBOL(refrigerator);