Messages in this thread Patch in this message | ![/](/images/icornerl.gif) | | Subject | [PATCH] [43/48] Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch | Date | Wed, 6 Jul 2005 12:20:44 +1000 | From | Nigel Cunningham <> |
| |
diff -ruNp 620-userui-header.patch-old/include/linux/netlink.h 620-userui-header.patch-new/include/linux/netlink.h --- 620-userui-header.patch-old/include/linux/netlink.h 2005-06-20 11:47:29.000000000 +1000 +++ 620-userui-header.patch-new/include/linux/netlink.h 2005-07-04 23:14:19.000000000 +1000 @@ -14,6 +14,7 @@ #define NETLINK_SELINUX 7 /* SELinux event notifications */ #define NETLINK_ARPD 8 #define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_SUSPEND2_USERUI 10 /* For suspend2's userui */ #define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ #define NETLINK_IP6_FW 13 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ diff -ruNp 620-userui-header.patch-old/kernel/power/suspend2_core/ui.c 620-userui-header.patch-new/kernel/power/suspend2_core/ui.c --- 620-userui-header.patch-old/kernel/power/suspend2_core/ui.c 1970-01-01 10:00:00.000000000 +1000 +++ 620-userui-header.patch-new/kernel/power/suspend2_core/ui.c 2005-07-05 23:48:59.000000000 +1000 @@ -0,0 +1,1186 @@ +/* + * kernel/power/ui.c + * + * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu> + * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz> + * Copyright (C) 2002-2003 Florent Chabaud <fchabaud@free.fr> + * Copyright (C) 2002-2005 Nigel Cunningham <nigel@suspend2.net> + * + * This file is released under the GPLv2. + * + * Routines for Software Suspend's user interface. + * + * The user interface code talks to a userspace program via a + * netlink socket. + * + * The kernel side: + * - starts the userui program; + * - sends text messages and progress bar status; + * + * The user space side: + * - passes messages regarding user requests (abort, toggle reboot etc) + * + */ +#define SUSPEND_CONSOLE_C + +#define __KERNEL_SYSCALLS__ + +#include <linux/suspend.h> +#include <linux/console.h> +#include <linux/ctype.h> +#include <linux/tty.h> +#include <linux/vt_kern.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/kmod.h> +#include <linux/netlink.h> +#include <linux/security.h> +#include <linux/syscalls.h> +#include <net/sock.h> + +#include "proc.h" +#include "plugins.h" +#include "suspend.h" +#include "suspend2_common.h" +#include "ui.h" +#include "version.h" +#include "userui.h" + +#include "../power.h" + +static struct suspend_plugin_ops userui_ops; +static int orig_loglevel; +static int orig_default_message_loglevel; +static int orig_kmsg; + +static char local_printf_buf[1024]; /* Same as printk - should be safe */ +static char lastheader[512]; +static int lastheader_message_len = 0; + +#define NUM_USERUI_SKBS 16 /* Number of preallocated skbs for emergencies */ + +static char userui_program[256] = ""; + +/* Number of distinct progress amounts that userspace can display */ +static int progress_granularity = 50; + +static struct sock *userui_nl; +static u32 userui_sock_seq; +static struct sk_buff *userui_skbs[NUM_USERUI_SKBS]; +static int next_free_userui_skb; +static pid_t userui_pid = -1; + +DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_process); +DECLARE_WAIT_QUEUE_HEAD(userui_wait_for_key); + +void s2_userui_message(unsigned long section, unsigned long level, + int normally_logged, + const char *fmt, va_list args); +unsigned long userui_update_progress(unsigned long value, unsigned long maximum, + const char *fmt, va_list args); +void userui_prepare_console(void); +void userui_cleanup_console(void); + +/* + * Refill our pool of SKBs for use in emergencies (eg, when eating memory and none + * can be allocated). + */ +static void userui_replenish_skbs(void) +{ + while (next_free_userui_skb+1 < NUM_USERUI_SKBS) { + userui_skbs[next_free_userui_skb+1] = + alloc_skb(NLMSG_SPACE(sizeof(struct userui_msg_params)), GFP_ATOMIC); + if (userui_skbs[next_free_userui_skb+1]) + next_free_userui_skb++; + else + break; + } +} + +/* + * Return a single skbuff either from our pool, or try to allocate one if + * the pool is exhausted. + */ +static struct sk_buff * userui_get_skb(void) +{ + struct sk_buff *skb; + if (next_free_userui_skb == -1) + skb = alloc_skb(NLMSG_SPACE(sizeof(struct userui_msg_params)), GFP_ATOMIC); + else { + skb = userui_skbs[next_free_userui_skb]; + userui_skbs[next_free_userui_skb] = NULL; + next_free_userui_skb--; + } + + userui_replenish_skbs(); + + return skb; +} + +static void userui_notify_userspace(void* data) +{ + struct task_struct *t; + read_lock(&tasklist_lock); + if ((t = find_task_by_pid(userui_pid))) + wake_up_process(t); + read_unlock(&tasklist_lock); +} + +DECLARE_WORK(userui_notify_userspace_work, userui_notify_userspace, NULL); + +static void userui_send_message(int type, void* params, size_t len) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + void *dest; + + skb = userui_get_skb(); + if (!skb) { + printk("suspend_userui: Can't allocate skb!\n"); + return; + } + + /* NLMSG_PUT contains a hidden goto nlmsg_failure */ + nlh = NLMSG_PUT(skb, 0, userui_sock_seq, type, len); + userui_sock_seq++; + + dest = NLMSG_DATA(nlh); + if (params && len > 0) + memcpy(dest, params, len); + + netlink_unicast(userui_nl, skb, userui_pid, 0); + + /* We may be in an interrupt context so defer waking up userspace */ + schedule_work(&userui_notify_userspace_work); + + return; + +nlmsg_failure: + if (skb) + kfree_skb(skb); +} + +/* + * Set the PF_NOFREEZE flag on the given process to ensure it can run whilst we + * are suspending. + */ +static int userui_nl_set_nofreeze(int pid) +{ + struct task_struct *t; + userui_pid = pid; + + read_lock(&tasklist_lock); + if ((t = find_task_by_pid(userui_pid)) == NULL) { + read_unlock(&tasklist_lock); + return -EINVAL; + } + + t->flags |= PF_NOFREEZE; + + read_unlock(&tasklist_lock); + + userui_send_message(USERUI_MSG_NOFREEZE_ACK, NULL, 0); + + return 0; +} + +#if CONFIG_PM_DEBUG +static int is_debugging = 1; +#else +static int is_debugging = 0; +#endif + +static void send_whether_debugging(void) +{ + userui_send_message(USERUI_MSG_IS_DEBUGGING, &is_debugging, sizeof(int)); +} + +/* + * Called when the userspace process has informed us that it's ready to roll. + */ +static int userui_nl_ready(int version) +{ + int ret = 0; + if (version != SUSPEND_USERUI_INTERFACE_VERSION) { + printk("suspend_userui: Userspace process using invalid interface version\n"); + SET_RESULT_STATE(SUSPEND_ABORTED); + ret = -EINVAL; + } + + wake_up_interruptible(&userui_wait_for_process); + + return ret; +} + +static int userui_nl_set_state(int n) +{ + /* Only let them change certain settings */ + static const int suspend_action_mask = + (1 << SUSPEND_REBOOT) | (1 << SUSPEND_PAUSE) | (1 << SUSPEND_SLOW) | + (1 << SUSPEND_LOGALL) | (1 << SUSPEND_SINGLESTEP) | + (1 << SUSPEND_PAUSE_NEAR_PAGESET_END); + + suspend_action = (suspend_action & (~suspend_action_mask)) | + (n & suspend_action_mask); + + return 0; +} + +static int userui_nl_set_progress_granularity(int n) +{ + if (n < 1) n = 1; + progress_granularity = n; + return 0; +} + +static int userui_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, int *errp) +{ + int type; + int *data; + + *errp = 0; + + if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) + return 0; + + type = nlh->nlmsg_type; + + /* A control message: ignore them */ + if (type < USERUI_MSG_BASE) + return 0; + + /* Unknown message: reply with EINVAL */ + if (type >= USERUI_MSG_MAX) { + *errp = -EINVAL; + return -1; + } + + /* All operations require privileges, even GET */ + if (security_netlink_recv(skb)) { + *errp = -EPERM; + return -1; + } + + /* Only allow one task to receive NOFREEZE privileges */ + if (type == USERUI_MSG_NOFREEZE_ME && userui_pid != -1) { + *errp = -EBUSY; + return -1; + } + + data = (int*)NLMSG_DATA(nlh); + + switch (type) { + case USERUI_MSG_NOFREEZE_ME: + if ((*errp = userui_nl_set_nofreeze(nlh->nlmsg_pid))) + return -1; + break; + case USERUI_MSG_READY: + if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) { + *errp = -EINVAL; + return -1; + } + if ((*errp = userui_nl_ready(*data))) + return -1; + break; + case USERUI_MSG_ABORT: + request_abort_suspend(); + break; + case USERUI_MSG_GET_STATE: + userui_send_message(USERUI_MSG_GET_STATE, &suspend_action, + sizeof(suspend_action)); + break; + case USERUI_MSG_SET_STATE: + if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) { + *errp = -EINVAL; + return -1; + } + if ((*errp = userui_nl_set_state(*data))) + return -1; + break; + case USERUI_MSG_SET_PROGRESS_GRANULARITY: + if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(int))) { + *errp = -EINVAL; + return -1; + } + if ((*errp = userui_nl_set_progress_granularity(*data))) + return -1; + break; + case USERUI_MSG_SPACE: + wake_up_interruptible(&userui_wait_for_key); + break; + case USERUI_MSG_GET_DEBUGGING: + send_whether_debugging(); + break; + } + + return 0; +} + +static int userui_user_rcv_skb(struct sk_buff *skb) +{ + int err; + struct nlmsghdr *nlh; + + while (skb->len >= NLMSG_SPACE(0)) { + u32 rlen; + + nlh = (struct nlmsghdr *) skb->data; + if (nlh->nlmsg_len < sizeof(*nlh) || + skb->len < nlh->nlmsg_len) + return 0; + rlen = NLMSG_ALIGN(nlh->nlmsg_len); + if (rlen > skb->len) + rlen = skb->len; + if (userui_user_rcv_msg(skb, nlh, &err) < 0) { + if (err == 0) + return -1; + netlink_ack(skb, nlh, err); + } else if (nlh->nlmsg_flags & NLM_F_ACK) + netlink_ack(skb, nlh, 0); + skb_pull(skb, rlen); + } + + return 0; +} + +static void userui_netlink_input(struct sock *sk, int len) +{ + do { + struct sk_buff *skb; + while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { + if (userui_user_rcv_skb(skb)) { + if (skb->len) + skb_queue_head(&sk->sk_receive_queue, skb); + else + kfree_skb(skb); + break; + } + kfree_skb(skb); + } + } while (userui_nl && userui_nl->sk_receive_queue.qlen); +} + +static int open_userui_netlink(void) +{ + int i; + + userui_sock_seq = 0x42c0ffee; + userui_nl = netlink_kernel_create(NETLINK_SUSPEND2_USERUI, userui_netlink_input); + if (!userui_nl) { + printk("suspend_userui: Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + for (i = 0; i < NUM_USERUI_SKBS; i++) + userui_skbs[i] = NULL; + + next_free_userui_skb = -1; + + userui_replenish_skbs(); + + return 0; +} + +static void close_userui_netlink(void) +{ + int i; + + if (userui_nl) { + sock_release(userui_nl->sk_socket); + userui_nl = NULL; + } + + for (i = 0; i < NUM_USERUI_SKBS; i++) + if (userui_skbs[i]) + kfree_skb(userui_skbs[i]); +} + +static int launch_userui_program(void) +{ + int retval; + static char *envp[] = { + "HOME=/", + "TERM=linux", + "PATH=/sbin:/usr/sbin:/bin:/usr/bin", + NULL }; + static char *argv[] = { userui_program, NULL }; + + retval = call_usermodehelper(userui_program, argv, envp, 0); + + if (retval) + printk("suspend_userui: Failed to launch userui program: Error %d\n", retval); + + return retval; +} + +static int wait_for_userui_contact(void) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&userui_wait_for_process, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* Wait 2 seconds for the userspace UI process to make contact */ + if (userui_pid == -1 && !TEST_RESULT_STATE(SUSPEND_ABORTED)) + interruptible_sleep_on_timeout(&userui_wait_for_process, 2*HZ); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&userui_wait_for_process, &wait); + + return 0; +} + +void userui_redraw(void) +{ + if (userui_pid == -1) + return; + + userui_send_message(USERUI_MSG_REDRAW, NULL, 0); +} + +void userui_prepare_console(void) +{ + userui_pid = -1; + + if (!*userui_program) { + printk("suspend_userui: userui_program not configured. suspend_userui disabled.\n"); + return; + } + + if (open_userui_netlink() < 0) + return; + + if (launch_userui_program() < 0) + goto out_close; + + if (wait_for_userui_contact() < 0) + goto out_close; + + if (userui_pid == -1) { + printk("suspend_userui: Failed to contact userui process. suspend_userui disabled.\n"); + goto out_close; + } + + return; + +out_close: + close_userui_netlink(); +} + +void userui_cleanup_console(void) +{ + struct task_struct *t; + + if (userui_pid == -1) + return; + + userui_send_message(USERUI_MSG_CLEANUP, NULL, 0); + + read_lock(&tasklist_lock); + if ((t = find_task_by_pid(userui_pid))) + t->flags &= ~PF_NOFREEZE; + read_unlock(&tasklist_lock); + + close_userui_netlink(); + + userui_pid = -1; +} + +static unsigned long userui_storage_needed(void) +{ + return sizeof(userui_program); +} + +static int userui_save_config_info(char *buf) +{ + *((int *) buf) = progress_granularity; + memcpy(buf + sizeof(int), userui_program, sizeof(userui_program)); + return sizeof(userui_program) + sizeof(int); +} + +static void userui_load_config_info(char *buf, int size) +{ + /* Don't load the saved path if one has already been set */ + if (userui_program[0]) + return; + + progress_granularity = *((int *) buf); + size -= sizeof(int); + + if (size > sizeof(userui_program)) + size = sizeof(userui_program); + + memcpy(userui_program, buf + sizeof(int), size); + userui_program[sizeof(userui_program)-1] = '\0'; +} + +static unsigned long userui_memory_needed(void) +{ + /* ball park figure of 128 pages */ + return (128 * PAGE_SIZE); +} + +unsigned long userui_update_progress(unsigned long value, unsigned long maximum, + const char *fmt, va_list args) +{ + static int last_step = -1; + struct userui_msg_params msg; + int bitshift = generic_fls(maximum) - 16; + int this_step; + unsigned long next_update; + + if (userui_pid == -1) + return 0; + + if ((!maximum) || (!progress_granularity)) + return maximum; + + if (value < 0) + value = 0; + + if (value > maximum) + value = maximum; + + /* Try to avoid math problems - we can't do 64 bit math here + * (and shouldn't need it - anyone got screen resolution + * of 65536 pixels or more?) */ + if (bitshift > 0) { + unsigned long temp_maximum = maximum >> bitshift; + unsigned long temp_value = value >> bitshift; + this_step = (int) (temp_value * progress_granularity / temp_maximum); + next_update = (((this_step + 1) * temp_maximum / progress_granularity) + 1) << bitshift; + } else { + this_step = (int) (value * progress_granularity / maximum); + next_update = ((this_step + 1) * maximum / progress_granularity) + 1; + } + + if (this_step == last_step) + return next_update; + + memset(&msg, 0, sizeof(msg)); + + msg.a = this_step; + msg.b = progress_granularity; + + if (fmt) { + vsnprintf(msg.text, sizeof(msg.text), fmt, args); + msg.text[sizeof(msg.text)-1] = '\0'; + } + + userui_send_message(USERUI_MSG_PROGRESS, &msg, sizeof(msg)); + last_step = this_step; + + return next_update; +} + +/* __suspend_message. + * + * Description: This function is intended to do the same job as printk, but + * without normally logging what is printed. The point is to be + * able to get debugging info on screen without filling the logs + * with "1/534. ^M 2/534^M. 3/534^M" + * + * It may be called from an interrupt context - can't sleep! + * + * Arguments: int mask: The debugging section(s) this message belongs to. + * int level: The level of verbosity of this message. + * int restartline: Whether to output a \r or \n with this line + * (\n if we're logging all output). + * const char *fmt, ...: Message to be displayed a la printk. + */ +void __suspend_message(unsigned long section, unsigned long level, + int normally_logged, + const char *fmt, ...) +{ + struct userui_msg_params msg; + + va_list args; + + if ((level) && (level > console_loglevel)) + return; + + if (userui_pid == -1) + return; + + memset(&msg, 0, sizeof(msg)); + + msg.a = section; + msg.b = level; + msg.c = normally_logged; + + if (fmt) { + va_start(args, fmt); + vsnprintf(msg.text, sizeof(msg.text), fmt, args); + va_end(args); + msg.text[sizeof(msg.text)-1] = '\0'; + } + + userui_send_message(USERUI_MSG_MESSAGE, &msg, sizeof(msg)); +} + +static void wait_for_key_via_userui(void) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&userui_wait_for_key, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + interruptible_sleep_on(&userui_wait_for_key); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&userui_wait_for_key, &wait); +} + +char suspend_wait_for_keypress(int timeout) +{ + int fd; + char key = '\0'; + struct termios t, t_backup; + + if (userui_pid != -1) { + wait_for_key_via_userui(); + key = 32; + goto out; + } + + /* We should be guaranteed /dev/console exists after populate_rootfs() in + * init/main.c + */ + if ((fd = sys_open("/dev/console", O_RDONLY, 0)) < 0) + goto out; + + if (sys_ioctl(fd, TCGETS, (long)&t) < 0) + goto out_close; + + memcpy(&t_backup, &t, sizeof(t)); + + t.c_lflag &= ~(ISIG|ICANON|ECHO); + t.c_cc[VMIN] = 0; + if (timeout) + t.c_cc[VTIME] = timeout*10; + + if (sys_ioctl(fd, TCSETS, (long)&t) < 0) + goto out_restore; + + while (1) { + if (sys_read(fd, &key, 1) <= 0) { + key = '\0'; + break; + } + key = tolower(key); + if (test_suspend_state(SUSPEND_SANITY_CHECK_PROMPT)) { + if (key == 'c') { + set_suspend_state(SUSPEND_CONTINUE_REQ); + break; + } else if (key == ' ') + break; + } else + break; + } + +out_restore: + sys_ioctl(fd, TCSETS, (long)&t_backup); + +out_close: + sys_close(fd); +out: + return key; +} + +/* abort_suspend + * + * Description: Begin to abort a cycle. If this wasn't at the user's request + * (and we're displaying output), tell the user why and wait for + * them to acknowledge the message. + * Arguments: A parameterised string (imagine this is printk) to display, + * telling the user why we're aborting. + */ + +void abort_suspend(const char *fmt, ...) +{ + va_list args; + int printed_len = 0; + + if (!TEST_RESULT_STATE(SUSPEND_ABORTED)) { + if (!TEST_RESULT_STATE(SUSPEND_ABORT_REQUESTED)) { + va_start(args, fmt); + printed_len = vsnprintf(local_printf_buf, + sizeof(local_printf_buf), fmt, args); + va_end(args); + if (userui_pid != -1) + printed_len = sprintf(local_printf_buf + printed_len, + " (Press SPACE to continue)"); + suspend2_prepare_status(1, 1, local_printf_buf); + + /* + * Make sure message seen - wait for shift to be + * released if being pressed + */ + if (userui_pid != -1) + suspend_wait_for_keypress(0); + } + /* Turn on aborting flag */ + SET_RESULT_STATE(SUSPEND_ABORTED); + } +} + +/* suspend2_prepare_status + * Description: Prepare the 'nice display', drawing the header and version, + * along with the current action and perhaps also resetting the + * progress bar. + * Arguments: int printalways: Whether to print the action when debugging + * is on. + * int clearbar: Whether to reset the progress bar. + * const char *fmt, ...: The action to be displayed. + */ +void suspend2_prepare_status(int printalways, int clearbar, const char *fmt, ...) +{ + va_list args; + + if (fmt) { + va_start(args, fmt); + lastheader_message_len = vsnprintf(lastheader, 512, fmt, args); + va_end(args); + } + + if (clearbar) + userui_update_progress(0, 1, NULL, NULL); + + __suspend_message(0, SUSPEND_STATUS, 1, lastheader, NULL); + + if (userui_pid == -1) + printk("%s\n", lastheader); +} + +/* update_status + * + * Description: Update the progress bar and (if on) in-bar message. + * Arguments: UL value, maximum: Current progress percentage (value/max). + * const char *fmt, ...: Message to be displayed in the middle + * of the progress bar. + * Note that a NULL message does not mean that any previous + * message is erased! For that, you need suspend2_prepare_status with + * clearbar on. + * Returns: Unsigned long: The next value where status needs to be updated. + * This is to reduce unnecessary calls to update_status. + */ +unsigned long suspend2_update_status(unsigned long value, unsigned long maximum, + const char *fmt, ...) +{ + unsigned long next_update = maximum; + va_list args; + + if (!maximum) + return maximum; + + if (value < 0) + value = 0; + + if (value > maximum) + value = maximum; + + va_start(args, fmt); + + next_update = userui_update_progress(value, maximum, fmt, args); + + va_end(args); + + return next_update; +} + +static struct waiting_message +{ + int message; + struct waiting_message * next; +} * waiting_messages = NULL; + +/* display_suspend_message + * + * Description: Display a message as a result of the user pressing a key + * and the key being processed in an interrupt handler. + */ + +int display_suspend_messages(void) +{ + int did_work = (waiting_messages != NULL); + + while (waiting_messages) { + struct waiting_message * this_message = waiting_messages; + + switch(waiting_messages->message) { + case 5: + suspend2_prepare_status(1, 1, + "--- ESCAPE PRESSED AGAIN :" + " TRYING HARDER TO ABORT ---"); + break; + case 6: + suspend2_prepare_status(1, 1, "--- ESCAPE PRESSED :" + " ABORTING PROCESS ---"); + break; + } + + waiting_messages = this_message->next; + kfree(this_message); + } + return did_work; +} + +/* suspend2_schedule_message + * + * Description: + * + */ + +void suspend2_schedule_message(int message_number) +{ + struct waiting_message * new_message = + kmalloc(sizeof(struct waiting_message), GFP_ATOMIC); + + if (!new_message) { + printk("Argh. Unable to allocate memory for " + "scheduling the display of a message.\n"); + return; + } + + new_message->message = message_number; + new_message->next = waiting_messages; + + waiting_messages = new_message; + return; +} + +/* request_abort_suspend + * + * Description: Handle the user requesting the cancellation of a suspend by + * pressing escape. Note that on a second press, we try a little + * harder, attempting to forcefully thaw processes. This shouldn't + * been needed, and may result in an oops (if we've overwritten + * memory), but has been useful on ocassion. + * Callers: Invoked from a netlink packet from userspace when the user presses + * escape. + */ +void request_abort_suspend(void) +{ + if (test_suspend_state(SUSPEND_NOW_RESUMING) || (TEST_RESULT_STATE(SUSPEND_ABORT_REQUESTED))) + return; + + if (TEST_RESULT_STATE(SUSPEND_ABORTED)) { + suspend2_schedule_message(5); + show_state(); + thaw_processes(FREEZER_ALL_THREADS); + } else { + suspend2_schedule_message(6); + SET_RESULT_STATE(SUSPEND_ABORTED); + SET_RESULT_STATE(SUSPEND_ABORT_REQUESTED); + } +} + +/* check_shift_keys + * + * Description: Potentially pause and wait for the user to tell us to continue. + * We normally only pause when @pause is set. + * Arguments: int pause: Whether we normally pause. + * char * message: The message to display. Not parameterised + * because it's normally a constant. + */ + +void check_shift_keys(int pause, char * message) +{ +#ifdef CONFIG_PM_DEBUG + int displayed_message = 0, last_key = 0; + + display_suspend_messages(); + while (last_key != 32 && + userui_pid != -1 && + ((TEST_ACTION_STATE(SUSPEND_PAUSE) && pause) || + (TEST_ACTION_STATE(SUSPEND_SINGLESTEP)))) { + if (!displayed_message) { + suspend2_prepare_status(1, 0, + "%s Press SPACE to continue.%s", + message ? message : "", + (TEST_ACTION_STATE(SUSPEND_SINGLESTEP)) ? + " Single step on." : ""); + displayed_message = 1; + } + last_key = suspend_wait_for_keypress(0); + display_suspend_messages(); + } +#else + display_suspend_messages(); +#endif + schedule(); +} + +extern asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg); + +/* suspend2_prepare_console + * + * Description: Prepare a console for use, save current settings. + * Returns: Boolean: Whether an error occured. Errors aren't + * treated as fatal, but a warning is printed. + */ +int suspend2_prepare_console(void) +{ + orig_loglevel = console_loglevel; + orig_default_message_loglevel = default_message_loglevel; + orig_kmsg = kmsg_redirect; + kmsg_redirect = fg_console + 1; + default_message_loglevel = 1; + console_loglevel = suspend_default_console_level; + userui_prepare_console(); + + return 0; +} + +/* suspend2_restore_console + * + * Description: Restore the settings we saved above. + */ + +void suspend2_cleanup_console(void) +{ + userui_cleanup_console(); + suspend_default_console_level = console_loglevel; + console_loglevel = orig_loglevel; + kmsg_redirect = orig_kmsg; + default_message_loglevel = orig_default_message_loglevel; +} + +/* suspend_early_boot_message() + * Description: Handle errors early in the process of booting. + * The user may press C to continue booting, perhaps + * invalidating the image, or space to reboot. + * This works from either the serial console or normally + * attached keyboard. + * + * Note that we come in here from init, while the kernel is + * locked. If we want to get events from the serial console, + * we need to temporarily unlock the kernel. + * + * suspend_early_boot_message may also be called post-boot. + * In this case, it simply printks the message and returns. + * + * Arguments: int Whether we are able to erase the image. + * int default_answer. What to do when we timeout. This + * will normally be continue, but the user might + * provide command line options (__setup) to override + * particular cases. + * Char *. Pointer to a string explaining why we're moaning. + */ + +#define say(message, a...) printk(KERN_EMERG message, ##a) +#define message_timeout 25 /* message_timeout * 10 must fit in 8 bits */ + +int suspend_early_boot_message(int can_erase_image, int default_answer, char *warning_reason, ...) +{ + unsigned long orig_state = get_suspend_state(), continue_req = 0; + va_list args; + int printed_len; + + if (warning_reason) { + va_start(args, warning_reason); + printed_len = vsnprintf(local_printf_buf, + sizeof(local_printf_buf), + warning_reason, + args); + va_end(args); + } + + if (!test_suspend_state(SUSPEND_BOOT_TIME)) { + printk(name_suspend "%s", local_printf_buf); + return default_answer; + } + + /* We might be called directly from do_mounts_initrd if the + * user fails to set up their initrd properly. We need to + * enable the keyboard handler by setting the running flag */ + set_suspend_state(SUSPEND_RUNNING); + +#if defined(CONFIG_VT) || defined(CONFIG_SERIAL_CONSOLE) + console_loglevel = 7; + + say("=== Software Suspend ===\n\n"); + if (warning_reason) { + say("BIG FAT WARNING!! %s\n\n", local_printf_buf); + if (can_erase_image) { + say("If you want to use the current suspend image, reboot and try\n"); + say("again with the same kernel that you suspended from. If you want\n"); + say("to forget that image, continue and the image will be erased.\n"); + } else { + say("If you continue booting, note that any image WILL NOT BE REMOVED.\n"); + say("Suspend is unable to do so because the appropriate modules aren't\n"); + say("loaded. You should manually remove the image to avoid any\n"); + say("possibility of corrupting your filesystem(s) later.\n"); + } + say("Press SPACE to reboot or C to continue booting with this kernel\n\n"); + say("Default action if you don't select one in %d seconds is: %s.\n", + message_timeout, + default_answer == SUSPEND_CONTINUE_REQ ? + "continue booting" : "reboot"); + } else { + say("BIG FAT WARNING!!\n\n"); + say("You have tried to resume from this image before.\n"); + say("If it failed once, may well fail again.\n"); + say("Would you like to remove the image and boot normally?\n"); + say("This will be equivalent to entering noresume2 on the\n"); + say("kernel command line.\n\n"); + say("Press SPACE to remove the image or C to continue resuming.\n\n"); + say("Default action if you don't select one in %d seconds is: %s.\n", + message_timeout, + !!default_answer ? + "continue resuming" : "remove the image"); + } + + set_suspend_state(SUSPEND_SANITY_CHECK_PROMPT); + clear_suspend_state(SUSPEND_CONTINUE_REQ); + + if (suspend_wait_for_keypress(message_timeout) == 0) /* We timed out */ + continue_req = !!default_answer; + else + continue_req = test_suspend_state(SUSPEND_CONTINUE_REQ); + + if ((warning_reason) && (!continue_req)) + machine_restart(NULL); + + restore_suspend_state(orig_state); + if (continue_req) + set_suspend_state(SUSPEND_CONTINUE_REQ); + +#endif // CONFIG_VT or CONFIG_SERIAL_CONSOLE + return -EPERM; +} +#undef say + +/* + * User interface specific /proc/suspend entries. + */ + +static struct suspend_proc_data proc_params[] = { +#ifdef CONFIG_PROC_FS + { .filename = "default_console_level", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &suspend_default_console_level, + .minimum = 0, +#ifdef CONFIG_PM_DEBUG + .maximum = 7, +#else + .maximum = 1, +#endif + + } + } + }, + + { .filename = "enable_escape", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_BIT, + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit = SUSPEND_CAN_CANCEL, + } + } + }, + +#ifdef CONFIG_PM_DEBUG + { .filename = "debug_sections", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_UL, + .data = { + .ul = { + .variable = &suspend_debug_state, + .minimum = 0, + .maximum = 2 << 30, + } + } + }, + + { .filename = "log_everything", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_BIT, + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit = SUSPEND_LOGALL, + } + } + }, + + { .filename = "pause_between_steps", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_BIT, + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit = SUSPEND_PAUSE, + } + } + }, +#endif + { .filename = "disable_userui_support", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &userui_ops.disabled, + .minimum = 0, + .maximum = 1, + } + } + }, + { .filename = "userui_progress_granularity", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &progress_granularity, + .minimum = 1, + .maximum = 2048, + } + } + }, + { .filename = "userui_program", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_STRING, + .data = { + .string = { + .variable = userui_program, + .max_length = 255, + } + } + } +#endif +}; + +static struct suspend_plugin_ops userui_ops = { + .type = MISC_PLUGIN, + .name = "Userspace UI Support", + .module = THIS_MODULE, + .storage_needed = userui_storage_needed, + .save_config_info = userui_save_config_info, + .load_config_info = userui_load_config_info, + .memory_needed = userui_memory_needed, +}; + +/* suspend_console_proc_init + * Description: Boot time initialisation for user interface. + */ +static __init int suspend_console_proc_init(void) +{ + int result, i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data); + + if (!(result = suspend_register_plugin(&userui_ops))) + for (i=0; i< numfiles; i++) + suspend_register_procfile(&proc_params[i]); + + userui_nl = NULL; + userui_program[0] = '\0'; + + return result; +} + +late_initcall(suspend_console_proc_init); diff -ruNp 620-userui-header.patch-old/kernel/power/suspend2_core/ui.h 620-userui-header.patch-new/kernel/power/suspend2_core/ui.h --- 620-userui-header.patch-old/kernel/power/suspend2_core/ui.h 1970-01-01 10:00:00.000000000 +1000 +++ 620-userui-header.patch-new/kernel/power/suspend2_core/ui.h 2005-07-04 23:14:19.000000000 +1000 @@ -0,0 +1,20 @@ +/* + * + */ + +extern int suspend2_prepare_console(void); +extern void suspend2_cleanup_console(void); + +extern void check_shift_keys(int pause, char * message); + +extern void suspend2_prepare_status(int printalways, int clearbar, const char *fmt, ...); +extern void check_shift_keys(int pause, char * message); +extern unsigned long suspend2_update_status(unsigned long value, unsigned long maximum, + const char *fmt, ...); + +extern void request_abort_suspend(void); +extern void abort_suspend(const char *fmt, ...); + +extern void suspend2_schedule_message (int message_number); + +extern void userui_redraw(void); diff -ruNp 620-userui-header.patch-old/kernel/power/suspend2_core/userui.h 620-userui-header.patch-new/kernel/power/suspend2_core/userui.h --- 620-userui-header.patch-old/kernel/power/suspend2_core/userui.h 1970-01-01 10:00:00.000000000 +1000 +++ 620-userui-header.patch-new/kernel/power/suspend2_core/userui.h 2005-07-04 23:14:19.000000000 +1000 @@ -0,0 +1,36 @@ +#ifndef _SUSPEND_USERUI_H_ +#define _SUSPEND_USERUI_H_ + +#define SUSPEND_USERUI_INTERFACE_VERSION 5 + +enum { + USERUI_MSG_BASE = 0x10, + + /* Userspace -> Kernel */ + USERUI_MSG_READY = 0x10, + USERUI_MSG_ABORT = 0x11, + USERUI_MSG_SET_STATE = 0x12, + USERUI_MSG_GET_STATE = 0x13, + USERUI_MSG_NOFREEZE_ME = 0x16, + USERUI_MSG_SET_PROGRESS_GRANULARITY = 0x17, + USERUI_MSG_SPACE = 0x18, + USERUI_MSG_GET_DEBUGGING = 0x19, + + /* Kernel -> Userspace */ + USERUI_MSG_MESSAGE = 0x21, + USERUI_MSG_PROGRESS = 0x22, + USERUI_MSG_CLEANUP = 0x24, + USERUI_MSG_REDRAW = 0x25, + USERUI_MSG_KEYPRESS = 0x26, + USERUI_MSG_NOFREEZE_ACK = 0x27, + USERUI_MSG_IS_DEBUGGING = 0x28, + + USERUI_MSG_MAX, +}; + +struct userui_msg_params { + unsigned long a, b, c, d; + char text[80]; +}; + +#endif /* _SUSPEND_USERUI_H_ */ - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| ![\](/images/icornerr.gif) |