============================================= deferred-resume-04.12.10.patch: This patch provides a power management enhancement that breaks the system resume operation into two phases. In the first phase, the suspend initiator performs a minimal set of operations to resume, and in the second phase a dedicated kernel thread performs the deferred operations. Signed-off-by: Geoff Levand for CELF --- Kconfig | 6 + main.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- process.c | 59 ++++++++++++++++ 3 files changed, 284 insertions(+), 2 deletions(-) diff -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/kernel/power/Kconfig branch_DEFERRED_RESUME/kernel/power/Kconfig --- branch_PM_ON_EBONY/kernel/power/Kconfig 2004-10-28 20:48:26.000000000 -0700 +++ branch_DEFERRED_RESUME/kernel/power/Kconfig 2004-10-27 23:08:25.000000000 -0700 @@ -71,3 +71,9 @@ config PM_STD_PARTITION suspended image to. It will simply pick the first available swap device. +config DEFERRED_RESUME + bool "Deferred resume" + depends on PM + ---help--- + Use another thread to resume system. + diff -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/kernel/power/main.c branch_DEFERRED_RESUME/kernel/power/main.c --- branch_PM_ON_EBONY/kernel/power/main.c 2004-10-28 20:48:26.000000000 -0700 +++ branch_DEFERRED_RESUME/kernel/power/main.c 2004-10-27 23:08:25.000000000 -0700 @@ -15,11 +15,15 @@ #include #include #include +#ifdef CONFIG_DEFERRED_RESUME +#include +#endif #include "power.h" DECLARE_MUTEX(pm_sem); +atomic_t on_suspend = ATOMIC_INIT(0); struct pm_ops * pm_ops = NULL; u32 pm_disk_mode = PM_DISK_SHUTDOWN; @@ -121,6 +125,64 @@ char * pm_states[] = { NULL, }; +#ifdef CONFIG_DEFERRED_RESUME + +#include + +static DECLARE_COMPLETION(resume_thread_wakeup); + +static struct task_struct *resume_thread_tsk = 0; +static int enable_deferred_resume = 0; +static u32 resume_state; + + +/* + * We will replace pm_trace() with better instrumentation + * method, like LTT, later. + */ + +static int __tm_printk(const char *fmt, ...) +{ + char buf[512]; + va_list args; + int i; + unsigned long long tt; + unsigned long us, ms; + + tt = sched_clock(); + ms = tt >> 10; /* convert to usec */ + us = ms % 1000; + ms = ms / 1000; + + i = sprintf(buf, "%6.6lu.%3.3lums:", ms,us); + va_start(args, fmt); + i = vsnprintf(buf+i, sizeof(buf)-i, fmt, args); + va_end(args); + printk(buf); + + return i; +} + + +void pm_trace(char *msg) +{ + static int last_cnt = 1; + + int c = pm_sem.count.counter; + if (c == 0 || last_cnt != c) + __tm_printk("[%3.3d]:pm_sem:%1.1d:<%s>:%s\n", + current->pid, + c, + current->comm, + msg); + last_cnt = c; +} +#else +static inline void pm_trace(char *msg) +{ + return; +} +#endif /* CONFIG_DEFERRED_RESUME */ /** * enter_state - Do common work of entering low-power state. @@ -151,15 +213,48 @@ static int enter_state(u32 state) goto Unlock; } + atomic_inc(&on_suspend); + smp_wmb(); pr_debug("PM: Preparing system for suspend\n"); - if ((error = suspend_prepare(state))) + if ((error = suspend_prepare(state))) { + atomic_dec(&on_suspend); + smp_wmb(); goto Unlock; + } pr_debug("PM: Entering state.\n"); error = suspend_enter(state); + atomic_dec(&on_suspend); + smp_wmb(); + +#ifdef CONFIG_DEFERRED_RESUME + pm_trace("Back"); + + if (enable_deferred_resume && resume_thread_tsk) { + if (!error) { + extern void thaw_process_group(int pgid); + + /* wake up related threads */ + thaw_process_group(process_group(current)); + pm_trace("woke up minimal threads"); + + /* wake up resume thread */ + resume_state=state; + smp_wmb(); + complete(&resume_thread_wakeup); + pr_debug("PM: Return without finishing up.\n"); + pm_trace("Return without finishing up"); + + return 0; + } + } +#endif /* CONFIG_DEFERRED_RESUME */ pr_debug("PM: Finishing up.\n"); suspend_finish(state); + + pm_trace("Return with finishing up"); + Unlock: up(&pm_sem); return error; @@ -175,6 +270,78 @@ int software_suspend(void) } +#ifdef CONFIG_DEFERRED_RESUME + + +static int resume_thread(void *state_p) +{ + struct k_sigaction sa; + sigset_t blocked; + u32 st; + + /* resume-thread is not freezable */ + current->flags |= PF_NOFREEZE; + + /* set scheduling params. */ + set_user_nice(current, 10); + + /* Block and flush all signals */ + sigfillset(&blocked); + sigprocmask(SIG_BLOCK, &blocked, NULL); + flush_signals(current); + + /* SIG_IGN makes children autoreap: see do_notify_parent(). */ + sa.sa.sa_handler = SIG_IGN; + sa.sa.sa_flags = 0; + siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD)); + do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0); + + pm_trace("init"); + + set_current_state(TASK_INTERRUPTIBLE); + while (!kthread_should_stop()) { + + __set_current_state(TASK_RUNNING); + + pm_trace("waiting completion"); + + wait_for_completion (&resume_thread_wakeup); /* wait until resume */ + st = *(u32 *) state_p; + + pm_trace("begin deferred finishing up"); + + pr_debug("PM(deferred): Finishing up.\n"); + suspend_finish(st); + + pm_trace("end deferred finishing up"); + + up(&pm_sem); + + set_current_state(TASK_INTERRUPTIBLE); + } + __set_current_state(TASK_RUNNING); + + pm_trace("end"); + + resume_thread_tsk = 0; + enable_deferred_resume = 0; + mb(); + return 0; +} + +int __init resume_thread_init(void) +{ + struct task_struct *p = 0; + p = kthread_create(resume_thread, &resume_state, "kresume"); + resume_thread_tsk = (IS_ERR(p)) ? NULL: p; + wake_up_process(resume_thread_tsk); + return 0; +} + +__initcall(resume_thread_init); + +#endif /* CONFIG_DEFERRED_RESUME */ + /** * pm_suspend - Externally visible function for suspending system. * @state: Enumarted value of state to enter. @@ -243,8 +410,60 @@ static ssize_t state_store(struct subsys power_attr(state); +#ifdef CONFIG_DEFERRED_RESUME + +char * deferred_resume_states[] = { + "disable", + "enable", + NULL +}; + + +static ssize_t deferred_resume_show(struct subsystem * subsys, char * buf) +{ + char * s = buf; + + s += sprintf(s, deferred_resume_states[enable_deferred_resume]); + s += sprintf(s, "\n"); + return (s - buf); +} + +static ssize_t deferred_resume_store(struct subsystem * subsys, const char * buf, size_t n) +{ + char ** s; + char *p; + int len; + int i; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + + i = 0; + s = &deferred_resume_states[0]; + while (*s) { + if (!strncmp(buf, *s, len)) + break; + s++; i++; + } + + if (!*s) { + return -EINVAL; + } + + if (resume_thread_tsk) + enable_deferred_resume = i; + return n; +} + +power_attr(deferred_resume); + +#endif /* CONFIG_DEFERRED_RESUME */ + static struct attribute * g[] = { &state_attr.attr, +#ifdef CONFIG_DEFERRED_RESUME + &deferred_resume_attr.attr, +#endif /* CONFIG_DEFERRED_RESUME */ NULL, }; diff -pruN -X ../cvs-admin/dontdiff branch_PM_ON_EBONY/kernel/power/process.c branch_DEFERRED_RESUME/kernel/power/process.c --- branch_PM_ON_EBONY/kernel/power/process.c 2004-11-11 21:53:04.000000000 -0800 +++ branch_DEFERRED_RESUME/kernel/power/process.c 2004-11-11 22:32:13.000000000 -0800 @@ -6,13 +6,17 @@ */ -#undef DEBUG +#define DEBUG +//#undef DEBUG #include #include #include #include +extern atomic_t on_suspend; +extern struct semaphore pm_sem; + /* * Timeout for stopping processes */ @@ -39,7 +43,25 @@ void refrigerator(unsigned long flag) long save; save = current->state; current->state = TASK_UNINTERRUPTIBLE; + + /* Check refrigerator() on resume or after resume */ + if (!atomic_read(&on_suspend)) { +#ifdef DEBUG + pr_debug("Strange: [%d] %s try to enter refrigerator again\n", + current->pid, current->comm); + dump_stack(); +#endif + current->flags &= ~PF_FREEZE; + mb(); + current->state = save; + return; + } + +#ifdef CONFIG_DEFERRED_RESUME + pr_debug("[%d] %s entered refrigerator\n", current->pid, current->comm); +#else pr_debug("%s entered refrigerator\n", current->comm); +#endif printk("="); current->flags &= ~PF_FREEZE; @@ -50,7 +72,11 @@ void refrigerator(unsigned long flag) current->flags |= PF_FROZEN; while (current->flags & PF_FROZEN) schedule(); +#ifdef CONFIG_DEFERRED_RESUME + pr_debug("[%d] %s left refrigerator\n", current->pid, current->comm); +#else pr_debug("%s left refrigerator\n", current->comm); +#endif current->state = save; } @@ -131,8 +157,13 @@ void thaw_processes(void) if (p->flags & PF_FROZEN) { p->flags &= ~PF_FROZEN; wake_up_process(p); + pr_debug("thaw: [%d] %s\n", p->pid, p->comm); } else +#ifdef CONFIG_DEFERRED_RESUME + printk(KERN_INFO " [%d] %s not stopped\n", p->pid, p->comm ); +#else printk(KERN_INFO " Strange, %s not stopped\n", p->comm ); +#endif } while_each_thread(g, p); read_unlock(&tasklist_lock); @@ -143,3 +174,29 @@ void thaw_processes(void) } EXPORT_SYMBOL(refrigerator); + +#ifdef CONFIG_DEFERRED_RESUME + +/* Thaw processes blong to PGID process group. */ + +void thaw_process_group(int pgid) +{ + struct task_struct *p; + + read_lock(&tasklist_lock); + do_each_task_pid(pgid, PIDTYPE_PGID, p) { + if (!freezeable(p)) + continue; + if (p->flags & PF_FROZEN) { + p->flags &= ~PF_FROZEN; + wake_up_process(p); + pr_debug("thaw pgid(%d): [%d] %s\n", pgid, p->pid, p->comm); + } else { + printk(KERN_INFO " Thaw pgid(%d): Strange, [%d] %s not stopped\n", + pgid, p->pid, p->comm); + } + } while_each_task_pid(pgid, PIDTYPE_PGID, p); + read_unlock(&tasklist_lock); +} + +#endif /* CONFIG_DEFERRED_RESUME */