Suspend2 2.1.9.8 for 2.6.12
Subsystems (48)
PATCH 41/48 Suspend2 2.1.9.8 for 2.6.12: 617-proc.patch
In reply to Nigel Cunninghamkernel/power/suspend2_core/suspend.c +1180 -0
@@ -0,0 +1,1180 @@ +/* +* kernel/power/suspend2.c +*/ +/** \mainpage Software Suspend 2. +* +* Suspend2 provides support for saving and restoring an image of +* system memory to an arbitrary storage device, either on the local computer, +* or across some network. The support is entirely OS based, so Suspend2 +* works without requiring BIOS, APM or ACPI support. The vast majority of the +* code is also architecture independant, so it should be very easy to port +* the code to new architectures. Suspend includes support for SMP, 4G HighMem +* and preemption. Initramfses and initrds are also supported. +* +* Suspend2 uses a modular design, in which the method of storing the image is +* completely abstracted from the core code, as are transformations on the data +* such as compression and/or encryption (multiple 'plugins' can be used to +* provide arbitrary combinations of functionality). The user interface is also +* modular, so that arbitrarily simple or complex interfaces can be used to +* provide anything from debugging information through to eye candy. +* +* \section Copyright +* +* Suspend2 is released under the GPLv2. +* +* Copyright (C) 1998-2001 Gabor Kuti <[email protected]><BR> +* Copyright (C) 1998,2001,2002 Pavel Machek <[email protected]><BR> +* Copyright (C) 2002-2003 Florent Chabaud <[email protected]><BR> +* Copyright (C) 2002-2005 Nigel Cunningham <[email protected]><BR> +* +* \section Credits +* +* Nigel would like to thank the following people for their work: +* +* Pavel Machek <[email protected]><BR> +* Modifications, defectiveness pointing, being with Gabor at the very beginning, +* suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. +* +* Steve Doddi <[email protected]><BR> +* Support the possibility of hardware state restoring. +* +* Raph <[email protected]><BR> +* Support for preserving states of network devices and virtual console +* (including X and svgatextmode) +* +* Kurt Garloff <[email protected]><BR> +* Straightened the critical function in order to prevent compilers from +* playing tricks with local variables. +* +* Andreas Mohr <[email protected]> +* +* Alex Badea <[email protected]><BR> +* Fixed runaway init +* +* Jeff Snyder <[email protected]><BR> +* ACPI patch +* +* Nathan Friess <[email protected]><BR> +* Some patches. +* +* Michael Frank <[email protected]><BR> +* Extensive testing and help with improving stability. Nigel was constantly +* amazed by the quality and quantity of Michael's help. +* +* Bernard Blackham <[email protected]><BR> +* Web page & Wiki administration, some coding. Another person without whom +* Suspend would not be where it is. +* +* ..and of course the myriads of Suspend2 users who have helped diagnose +* and fix bugs, made suggestions on how to improve the code, proofread +* documentation, and donated time and money. +* +* Thanks also to corporate sponsors: +* +* <B>Cyclades.com.</B> Nigel's employers from Dec 2004, who allow him to work on +* Suspend and PM related issues on company time. +* +* <B>LinuxFund.org.</B> Sponsored Nigel's work on Suspend for four months Oct 2003 +* to Jan 2004. +* +* <B>LAC Linux.</B> Donated P4 hardware that enabled development and ongoing +* maintenance of SMP and Highmem support. +* +* <B>OSDL.</B> Provided access to various hardware configurations, make occasional +* small donations to the project. +*/ + +#define SUSPEND_MAIN_C + +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/version.h> +#include <linux/reboot.h> +#include <linux/mm.h> +#include <linux/highmem.h> +#include <asm/uaccess.h> +#include <asm/param.h> + +#include "version.h" +#include "suspend.h" +#include "driver_model.h" +#include "plugins.h" +#include "proc.h" +#include "pageflags.h" +#include "prepare_image.h" +#include "io.h" +#include "ui.h" +#include "version.h" +#include "suspend2_common.h" +#include "extent.h" +#include "power_off.h" +#include "utility.h" +#include "smp.h" +#include "atomic_copy.h" + +#ifdef CONFIG_X86 +#include <asm/i387.h> /* for kernel_fpu_end */ +#endif + +/* Variables to be preserved over suspend */ +int pageset1_sizelow =0 , pageset2_sizelow =0 , image_size_limit =0 ; +unsigned long suspend2_orig_mem_free =0 ; + +static dyn_pageflags_t pageset1_check_map; +static dyn_pageflags_t pageset2_check_map; +static char * debug_info_buffer; +static char suspend_core_version[] =SUSPEND_CORE_VERSION ; + +extern void do_suspend2_lowlevel (int resume); +extern __nosavedata char resume_commandline[COMMAND_LINE_SIZE]; + +/* +*--------------------- Variables --------------------------- +* +* The following are used by the arch specific low level routines +* and only needed if suspend2 is compiled in. Other variables, +* used by the freezer even if suspend2 is not compiled in, are +* found in process.c +*/ + +/*! How long I/O took. */ +int suspend_io_time[2 ][2 ]; + +/* Compression ratio */ +__nosavedata unsigned long bytes_in =0 , bytes_out =0 ; + +/*! Pageset metadata. */ +struct pagedir pagedir1 = {0 ,0 }, pagedir2 = {0 ,0 }; + +/* Suspend2 variables used by built-in routines. */ + +/*! The number of suspends we have started (some may have been cancelled) */ +unsigned int nr_suspends =0 ; + +/*! The console log level we default to. */ +int suspend_default_console_level =0 ; + +/* +* For resume2= kernel option. It's pointless to compile +* suspend2 without any writers, but compilation shouldn't +* fail if you do. +*/ + +unsigned long software_suspend_state = ((1 <<SUSPEND_DISABLED ) | (1 <<SUSPEND_BOOT_TIME ) | + (1 <<SUSPEND_RESUME_NOT_DONE ) | (1 <<SUSPEND_IGNORE_LOGLEVEL )); + +mm_segment_t oldfs; + +#ifdef CONFIG_SUSPEND2_DEFAULT_RESUME2 +char resume2_file[256 ] =CONFIG_SUSPEND2_DEFAULT_RESUME2 ; +#else +char resume2_file[256] +#endif + +/* -------------------------------------------------------------------------- */ + +static atomic_t actions_running; + +/* +* Basic clean-up routine. +*/ +void suspend_finish_anything (int finishing_cycle) +{ +if (atomic_dec_and_test (&actions_running)) { +suspend2_cleanup_plugins (finishing_cycle); +suspend2_put_modules (); +clear_suspend_state (SUSPEND_RUNNING ); + } + +set_fs (oldfs); +} + +/* +* Basic set-up routine. +*/ +int suspend_start_anything (int starting_cycle) +{ + oldfs =get_fs (); + +if (atomic_add_return (1 , &actions_running) ==1 ) { +set_fs (KERNEL_DS ); + +set_suspend_state (SUSPEND_RUNNING ); + +if (suspend2_get_modules ()) { +printk ("Get modules failed!\n" ); +clear_suspend_state (SUSPEND_RUNNING ); +set_fs (oldfs); +return -EBUSY ; + } + +if (suspend2_initialise_plugins (starting_cycle)) { +printk ("Initialise plugins failed!\n" ); +suspend_finish_anything (starting_cycle); +return -EBUSY ; + } + } + +return 0 ; +} + +/* -------------------------------------------------------------------------- */ + +/* +* save_image +* Result code (int): Zero on success, non zero on failure. +* Functionality : High level routine which performs the steps necessary +* to prepare and save the image after preparatory steps +* have been taken. +* Key Assumptions : Processes frozen, sufficient memory available, drivers +* suspended. +* Called from : suspend2_suspend_2 +*/ + +static int save_image (void ) +{ +int temp_result; + +if (RAM_TO_SUSPEND > max_mapnr) { +suspend2_prepare_status (1 ,1 , +"Couldn't get enough free pages, on %ld pages short" , +RAM_TO_SUSPEND - max_mapnr); +return -1 ; + } + +suspend_message (SUSPEND_ANY_SECTION ,SUSPEND_LOW ,1 , +" - Final values: %d and %d.\n" , + pageset1_size, + pageset2_size); + +check_shift_keys (1 ,"About to write pagedir2." ); + + temp_result =write_pageset (&pagedir2,2 ); + +if (temp_result ==-1 ||TEST_RESULT_STATE (SUSPEND_ABORTED )) +return -1 ; + +check_shift_keys (1 ,"About to copy pageset 1." ); + +suspend2_prepare_status (1 ,0 ,"Doing atomic copy." ); + +do_suspend2_lowlevel (0 ); + +return 0 ; +} + +/* +* Save the second part of the image. +*/ +int save_image_part1 (void ) +{ +int temp_result, old_ps1_size = pageset1_size; +dyn_pageflags_t temp; + +/* Quick switch: We want to compare the old stats with the new ones. */ + temp = pageset1_map; + pageset1_map = pageset1_check_map; + pageset1_check_map = temp; + + temp = pageset2_map; + pageset2_map = pageset2_check_map; + pageset2_check_map = temp; + +suspend2_recalculate_stats (); + +if ((pageset1_size - old_ps1_size) >EXTRA_PD1_PAGES_ALLOWANCE ) { +abort_suspend ("Pageset1 has grown by %d pages." +" Only %d growth is allowed for!\n" , + pageset1_size - old_ps1_size, +EXTRA_PD1_PAGES_ALLOWANCE ); +return -1 ; + } + +suspend2_map_atomic_copy_pages (); + +BUG_ON (!irqs_disabled ()); + +if (!TEST_ACTION_STATE (SUSPEND_TEST_FILTER_SPEED )) +suspend2_copy_pageset1 (); + +/* +* ---- FROM HERE ON, NEED TO REREAD PAGESET2 IF ABORTING!!! ----- +* +*/ + +suspend2_unmap_atomic_copy_pages (); + +/* +* Other processors have waited for me to make the atomic copy of the +* kernel +*/ + +smp_continue (); + +#ifdef CONFIG_X86 +kernel_fpu_end (); +#endif + +preempt_enable_no_resched (); + +suspend_drivers_resume (SUSPEND_DRIVERS_IRQS_DISABLED ); + +local_irq_enable (); + +suspend_drivers_resume (SUSPEND_DRIVERS_IRQS_ENABLED ); + +suspend2_update_status (pageset2_size, pageset1_size + pageset2_size,NULL ); + +if (TEST_RESULT_STATE (SUSPEND_ABORTED )) + goto abort_reloading_pagedir_two; + +check_shift_keys (1 ,"About to write pageset1." ); + +/* +* End of critical section. +*/ + +suspend_message (SUSPEND_ANY_SECTION ,SUSPEND_LOW ,1 , +"-- Writing pageset1\n" ); + + temp_result =write_pageset (&pagedir1,1 ); + +/* We didn't overwrite any memory, so no reread needs to be done. */ +if (TEST_ACTION_STATE (SUSPEND_TEST_FILTER_SPEED )) +return -1 ; + +if (temp_result ==-1 ||TEST_RESULT_STATE (SUSPEND_ABORTED )) + goto abort_reloading_pagedir_two; + +check_shift_keys (1 ,"About to write header." ); + +if (TEST_RESULT_STATE (SUSPEND_ABORTED )) + goto abort_reloading_pagedir_two; + + temp_result =write_image_header (); + +if (temp_result || (TEST_RESULT_STATE (SUSPEND_ABORTED ))) + goto abort_reloading_pagedir_two; + +check_shift_keys (1 ,"About to power down or reboot." ); + +return 0 ; + +abort_reloading_pagedir_two: + temp_result =read_pageset2 (1 ); + +/* If that failed, we're sunk. Panic! */ +if (temp_result) +panic ("Attempt to reload pagedir 2 while aborting " +"a suspend failed." ); + +return -1 ; + +} + +#define SNPRINTF(a...) len += suspend_snprintf(debug_info_buffer + len, \ + PAGE_SIZE - len - 1, ## a) + +static inline int io_MB_per_second (int read_write) +{ +if (!suspend_io_time[read_write][1 ]) +return 0 ; + +return MB ((unsigned long ) suspend_io_time[read_write][0 ]) *HZ / + suspend_io_time[read_write][1 ]; +} + +/* get_debug_info +* Functionality: Store debug info in a buffer. +* Called from: suspend_try_suspend. +*/ + + +static int get_suspend_debug_info (void ) +{ +int len =0 ; +if (!debug_info_buffer) { + debug_info_buffer = (char *)get_zeroed_page (GFP_ATOMIC ); +if (!debug_info_buffer) { +printk ("Error! Unable to allocate buffer for" +"software suspend debug info.\n" ); +return 0 ; + } + } + +SNPRINTF ("Please include the following information in bug reports:\n" ); +SNPRINTF ("- SUSPEND core : %s\n" ,SUSPEND_CORE_VERSION ); +SNPRINTF ("- Kernel Version : %s\n" ,UTS_RELEASE ); +SNPRINTF ("- Compiler vers. : %d.%d\n" , __GNUC__, __GNUC_MINOR__); +SNPRINTF ("- Attempt number : %d\n" , nr_suspends); +SNPRINTF ("- Pageset sizes : %d (%d low) and %d (%d low).\n" , + pagedir1.lastpageset_size, + pageset1_sizelow, + pagedir2.lastpageset_size, + pageset2_sizelow); +SNPRINTF ("- Parameters : %ld %ld %ld %d %d %ld\n" , + suspend_result, + suspend_action, + suspend_debug_state, + suspend_default_console_level, + image_size_limit, + suspend2_powerdown_method); +SNPRINTF ("- Calculations : Image size: %lu. " +"Ram to suspend: %ld.\n" , +STORAGE_NEEDED (1 ),RAM_TO_SUSPEND ); +SNPRINTF ("- Limits : %lu pages RAM. Initial boot: %lu.\n" , + max_mapnr, suspend2_orig_mem_free); +SNPRINTF ("- Overall expected compression percentage: %d.\n" , +100 -expected_compression_ratio ()); + len+=print_plugin_debug_info (debug_info_buffer + len, +PAGE_SIZE - len -1 ); +#ifdef CONFIG_PM_DEBUG +SNPRINTF ("- Debugging compiled in.\n" ); +#endif +#ifdef CONFIG_PREEMPT +SNPRINTF ("- Preemptive kernel.\n" ); +#endif +#ifdef CONFIG_SMP +SNPRINTF ("- SMP kernel.\n" ); +#endif +#ifdef CONFIG_HIGHMEM +SNPRINTF ("- Highmem Support.\n" ); +#endif +SNPRINTF ("- Max extents used: %d\n" , + max_extents_used); +if (suspend_io_time[0 ][1 ]) { +if ((io_MB_per_second (0 ) <5 ) || (io_MB_per_second (1 ) <5 )) { +SNPRINTF ("- I/O speed: Write %d KB/s" , + (KB ((unsigned long ) suspend_io_time[0 ][0 ]) *HZ / + suspend_io_time[0 ][1 ])); +if (suspend_io_time[1 ][1 ]) +SNPRINTF (", Read %d KB/s" , + (KB ((unsigned long ) suspend_io_time[1 ][0 ]) *HZ / + suspend_io_time[1 ][1 ])); + }else { +SNPRINTF ("- I/O speed: Write %d MB/s" , + (MB ((unsigned long ) suspend_io_time[0 ][0 ]) *HZ / + suspend_io_time[0 ][1 ])); +if (suspend_io_time[1 ][1 ]) +SNPRINTF (", Read %d MB/s" , + (MB ((unsigned long ) suspend_io_time[1 ][0 ]) *HZ / + suspend_io_time[1 ][1 ])); + } +SNPRINTF (".\n" ); + } +else +SNPRINTF ("- No I/O speed stats available.\n" ); + +return len; +} + +/* +* debuginfo_read_proc +* Functionality : Displays information that may be helpful in debugging +* software suspend. +*/ +int debuginfo_read_proc (char * page,char ** start,off_t off,int count, +int *eof,void *data) +{ +int info_len, copy_len; + + info_len =get_suspend_debug_info (); + + copy_len =min (info_len - (int ) off, count); +if (copy_len <0 ) + copy_len =0 ; + +if (copy_len) { +memcpy (page, debug_info_buffer + off, copy_len); + *start = page; + } + +if (copy_len + off == info_len) + *eof =1 ; + +free_pages ((unsigned long ) debug_info_buffer,0 ); + debug_info_buffer =NULL ; +return copy_len; +} + +static int allocate_bitmaps (void ) +{ +suspend_message (SUSPEND_MEMORY ,SUSPEND_VERBOSE ,1 , +"Allocating in_use_map\n" ); +if (suspend_allocate_dyn_pageflags (&in_use_map)) +return 1 ; + +if (suspend_allocate_dyn_pageflags (&pageset1_map)) +return 1 ; + +if (suspend_allocate_dyn_pageflags (&pageset1_copy_map)) +return 1 ; + +if (suspend_allocate_dyn_pageflags (&allocd_pages_map)) +return 1 ; + +if (suspend_allocate_dyn_pageflags (&pageset2_map)) +return 1 ; + +#ifdef CONFIG_DEBUG_PAGEALLOC +if (suspend_allocate_dyn_pageflags (&unmap_map)) +return 1 ; + +#endif + +if (suspend_allocate_dyn_pageflags (&pageset1_check_map)) +return 1 ; + +if (suspend_allocate_dyn_pageflags (&pageset2_check_map)) +return 1 ; + +return 0 ; +} + +static void free_metadata (void ) +{ +free_dyn_pageflags (&pageset1_map); + +free_dyn_pageflags (&pageset1_copy_map); + +free_dyn_pageflags (&allocd_pages_map); + +free_dyn_pageflags (&pageset2_map); + +free_dyn_pageflags (&in_use_map); + +free_dyn_pageflags (&pageset1_check_map); +free_dyn_pageflags (&pageset2_check_map); +} + +static inline int check_still_keeping_image (void ) +{ +if (TEST_ACTION_STATE (SUSPEND_KEEP_IMAGE )) { +printk ("Image already stored: powering down immediately." ); +suspend_power_down (); +return 1 ;/* Just in case we're using S3 */ + } + +printk ("Invalidating previous image.\n" ); + active_writer->ops.writer.invalidate_image (); + +return 0 ; +} + +static inline int suspend2_init (void ) +{ + suspend_result =0 ; + +printk (name_suspend"Initiating a software suspend cycle.\n" ); + + max_extents_used =0 ; + nr_suspends++; +clear_suspend_state (SUSPEND_NOW_RESUMING ); + + suspend_io_time[0 ][0 ] = suspend_io_time[0 ][1 ] = + suspend_io_time[1 ][0 ] = + suspend_io_time[1 ][1 ] =0 ; + +suspend2_prepare_console (); + +free_metadata ();/* We might have kept it */ + +attempt_to_parse_resume_device (); + +if (test_suspend_state (SUSPEND_DISABLED )) +return 0 ; + +if (suspend_drivers_init ()) +return 0 ; + +if (allocate_bitmaps ()) +return 0 ; + +ensure_on_processor_zero (); + +return 1 ; +} + +void inline suspend2_cleanup (void ) +{ +int i; + + i =get_suspend_debug_info (); + +suspend2_free_pagedir_data (); + +thaw_processes (FREEZER_KERNEL_THREADS ); + +#ifdef CONFIG_SUSPEND2_KEEP_IMAGE +if (TEST_ACTION_STATE (SUSPEND_KEEP_IMAGE ) && + !TEST_RESULT_STATE (SUSPEND_ABORTED )) { +suspend_message (SUSPEND_ANY_SECTION ,SUSPEND_LOW ,1 , + name_suspend"Not invalidating the image due " +"to Keep Image being enabled.\n" ); +SET_RESULT_STATE (SUSPEND_KEPT_IMAGE ); + } else +#endif +if (active_writer) + active_writer->ops.writer.invalidate_image (); + +if (!TEST_ACTION_STATE (SUSPEND_KEEP_METADATA )) +free_metadata (); + +#ifdef CONFIG_DEBUG_PAGE_ALLOC +free_dyn_pageflags (&unmap_map); +#endif + +if (debug_info_buffer) { +/* Printk can only handle 1023 bytes, including +* its level mangling. */ +for (i =0 ; i <3 ; i++) +printk ("%s" , debug_info_buffer + (1023 * i)); +free_pages ((unsigned long ) debug_info_buffer,0 ); + debug_info_buffer =NULL ; + } + +suspend_drivers_cleanup (); + +thaw_processes (FREEZER_ALL_THREADS ); + +suspend2_cleanup_console (); + +return_to_all_processors (); +} + +/* +* suspend2_main +* Functionality : First level of code for software suspend invocations. +* Stores and restores load averages (to avoid a spike), +* allocates bitmaps, freezes processes and eats memory +* as required before suspending drivers and invoking +* the 'low level' code to save the state to disk. +* By the time we return from do_suspend2_lowlevel, we +* have either failed to save the image or successfully +* suspended and reloaded the image. The difference can +* be discerned by checking SUSPEND_ABORTED. +* Called From : +*/ + +void suspend2_main (void ) +{ +/* +* If kept image and still keeping image and suspending to RAM, we will +* return 1 after suspending and resuming (provided the power doesn't +* run out. +*/ +if (TEST_RESULT_STATE (SUSPEND_KEPT_IMAGE ) &&check_still_keeping_image ()) +return ; + +if (suspend2_init () &&suspend2_prepare_image () && !TEST_RESULT_STATE (SUSPEND_ABORTED ) && + !TEST_ACTION_STATE (SUSPEND_FREEZER_TEST )) + { +if (!TEST_RESULT_STATE (SUSPEND_ABORTED )) { +suspend2_prepare_status (1 ,0 ,"Starting to save the image.." ); +save_image (); + } + } + +suspend2_cleanup (); +} + +/* image_exists_read +* +* Return 0 or 1, depending on whether an image is found. +*/ +static int image_exists_read (char * page,char ** start,off_t off,int count, +int *eof,void *data) +{ +int len =0 ; + +attempt_to_parse_resume_device (); + +if (!active_writer) + len =sprintf (page,"-1\n" ); +else + len =sprintf (page,"%d\n" , active_writer->ops.writer.image_exists ()); + + *eof =1 ; +return len; +} + +/* image_exists_read +* +* Return 0 or 1, depending on whether an image is found. +*/ +static int image_exists_write (struct file *file,const char * buffer, +unsigned long count,void * data) +{ +if (active_writer && active_writer->ops.writer.image_exists ()) + active_writer->ops.writer.invalidate_image (); + +return count; +} + +/* +* Core proc entries that aren't built in. +* +* This array contains entries that are automatically registered at +* boot. Plugins and the console code register their own entries separately. +*/ +extern int toggle_pid; +void toggle_thread_nofreeze (void ); + +static struct suspend_proc_data proc_params[] = { + { .filename ="debug_info" , + .permissions =PROC_READONLY , + .type =SUSPEND_PROC_DATA_CUSTOM , + .data = { + .special = { + .read_proc = debuginfo_read_proc, + } + } + }, + + { .filename ="image_exists" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_CUSTOM , + .data = { + .special = { + .read_proc = image_exists_read, + .write_proc = image_exists_write, + } + } + }, + + { .filename ="image_size_limit" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_INTEGER , + .data = { + .integer = { + .variable = &image_size_limit, + .minimum =-2 , + .maximum =32767 , + } + } + }, + + { .filename ="last_result" , + .permissions =PROC_READONLY , + .type =SUSPEND_PROC_DATA_UL , + .data = { + .ul = { + .variable = &suspend_result, + } + } + }, + + { .filename ="reboot" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_BIT , + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit =SUSPEND_REBOOT , + } + } + }, + + { .filename ="resume2" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_STRING , + .data = { + .string = { + .variable = resume2_file, + .max_length =255 , + } + }, + .write_proc = attempt_to_parse_resume_device, + }, + + { .filename ="resume_commandline" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_STRING , + .data = { + .string = { + .variable = resume_commandline, + .max_length =COMMAND_LINE_SIZE , + } + }, + .write_proc = attempt_to_parse_resume_device, + }, + + + { .filename ="toggle_process_nofreeze" , + .permissions =PROC_WRITEONLY , + .type =SUSPEND_PROC_DATA_INTEGER , + .data = { + .integer = { + .variable = &toggle_pid, + .minimum =2 , + .maximum =32767 , + } + }, + .write_proc = toggle_thread_nofreeze, + }, + + { .filename ="version" , + .permissions =PROC_READONLY , + .type =SUSPEND_PROC_DATA_STRING , + .data = { + .string = { + .variable = suspend_core_version, + } + } + }, + +#ifdef CONFIG_PM_DEBUG + { .filename ="freezer_test" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_BIT , + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit =SUSPEND_FREEZER_TEST , + } + } + }, + + { .filename ="keep_metadata" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_BIT , + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit =SUSPEND_KEEP_METADATA , + } + } + }, + + { .filename ="test_filter_speed" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_BIT , + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit =SUSPEND_TEST_FILTER_SPEED , + } + } + }, + + { .filename ="slow" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_BIT , + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit =SUSPEND_SLOW , + } + } + }, + +#endif + +#if defined(CONFIG_ACPI) + { .filename ="powerdown_method" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_UL , + .data = { + .ul = { + .variable = &suspend2_powerdown_method, + .minimum =3 , + .maximum =5 , + } + } + }, +#endif + +#ifdef CONFIG_SUSPEND2_KEEP_IMAGE + { .filename ="keep_image" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_BIT , + .data = { + .bit = { + .bit_vector = &suspend_action, + .bit =SUSPEND_KEEP_IMAGE , + } + } + }, +#endif +}; + + +/* +* Called from init kernel_thread. +* We check if we have an image and if so we try to resume. +* We also start ksuspendd if configuration looks right. +*/ + +int suspend2_resume (void ) +{ +int read_image_result =0 ; + +if (sizeof (swp_entry_t) !=sizeof (long )) { + printk(KERN_WARNING name_suspend +"The size of swp_entry_t != size of long. " + "Please report this!\n"); +return 1 ; + } + +if (!resume2_file[0 ]) + printk(KERN_WARNING name_suspend +"You need to use a resume2= command line parameter to " +"tell Software Suspend 2 where to look for an image.\n" ); + +if (!(test_suspend_state (SUSPEND_RESUME_DEVICE_OK ))) +attempt_to_parse_resume_device (); + +if (!(test_suspend_state (SUSPEND_RESUME_DEVICE_OK ))) { +/* +* Without a usable storage device we can do nothing - +* even if noresume is given +*/ + +if (!num_writers) + printk(KERN_ALERT name_suspend +"No writers have been registered.\n" ); +else + printk(KERN_ALERT name_suspend +"Missing or invalid storage location " +"(resume2= parameter). Please correct and " +"rerun lilo (or equivalent) before " +"suspending.\n" ); +return 1 ; + } + +/* We enable the possibility of machine suspend */ + suspend2_orig_mem_free =real_nr_free_pages (); + + suspend_task = current->pid; + + read_image_result =read_pageset1 ();/* non fatal error ignored */ + +if (test_suspend_state (SUSPEND_NORESUME_SPECIFIED )) + printk(KERN_WARNING name_suspend "Resuming disabled as requested.\n"); + +if (read_image_result) { + suspend_task =0 ; +return 1 ; + } + +suspend_drivers_init (); + +suspend_atomic_restore (); + +BUG (); + +return 0 ; +} + +extern void request_abort_suspend (void ); +extern void suspend2_schedule_message (int message_number); + +static __init int core_load (void ) +{ +int i, numfiles =sizeof (proc_params) /sizeof (struct suspend_proc_data ); + +printk ("Software Suspend Core.\n" ); + +suspend_initialise_plugin_lists (); + +for (i=0 ; i< numfiles; i++) +suspend_register_procfile (&proc_params[i]); + +return 0 ; +} + +/* ------------ Functions for kickstarting a suspend or resume ----------- */ + +static int can_suspend (void ) +{ +/* +* Perhaps something has changed such that we can suspend or +* resume when we couldn't before. Check before complaining. +*/ + +if (test_suspend_state (SUSPEND_DISABLED )) +attempt_to_parse_resume_device (); + +if (test_suspend_state (SUSPEND_DISABLED )) { +printk (name_suspend"Software suspend is disabled.\n" +"This may be because you haven't put something along the " +"lines of\n\nresume2=swap:/dev/hda1\n\n" +"in lilo.conf or equivalent. (Where /dev/hda1 is your " +"swap partition).\n" ); +SET_RESULT_STATE (SUSPEND_ABORTED ); +return 0 ; + } + +return 1 ; +} + +/* +* Check if we have an image and if so try to resume. +*/ + +void __suspend2_try_resume (void ) +{ +set_suspend_state (SUSPEND_TRYING_TO_RESUME ); + +clear_suspend_state (SUSPEND_RESUME_NOT_DONE ); + +suspend2_resume (); + +clear_suspend_state (SUSPEND_IGNORE_LOGLEVEL ); +clear_suspend_state (SUSPEND_TRYING_TO_RESUME ); +} + +/* Wrapper for when called from init/do_mounts.c */ +void suspend2_try_resume (void ) +{ +if (suspend_start_anything (0 )) +return ; + +__suspend2_try_resume (); + +/* +* For initramfs, we have to clear the boot time +* flag after trying to resume +*/ +clear_suspend_state (SUSPEND_BOOT_TIME ); + +suspend_finish_anything (0 ); +} + +/* +* suspend_check_mounts +* +* Functionality: Check that the user doesn't have any mounts that +* may be corrupted by resuming. Display them if needsbe. +* On exit, CONTINUE_REQ is set if we can continue resuming. +*/ + +void suspend_check_mounts (void ) +{ +if (!mounts_are_S4_resume_safe ()) { +char *buffer = (char *)get_zeroed_page (GFP_ATOMIC ); +get_S4_resume_unsafe_mounts (buffer,PAGE_SIZE ); +suspend_early_boot_message (1 ,0 , +"You have filesystems mounted that may make resuming unsafe:\n %s" , buffer); +free_pages ((unsigned long ) buffer,0 ); +return ; + } + +set_suspend_state (SUSPEND_CONTINUE_REQ ); +return ; +} + +/* +* __suspend2_try_suspend +* Functionality : Performs the basic checking as to whether suspend is +* enabled before invoking the high level routine. +* Called From : proc.c, suspend2_try_suspend.c +*/ +void __suspend2_try_suspend (void ) +{ +if (can_suspend ()) +suspend2_main (); +} + +/* +* suspend2_try_suspend +* Functionality : Wrapper around __suspend2_try_suspend. +* Called From : drivers/acpi/sleep/main.c +* kernel/reboot.c +*/ + +void suspend2_try_suspend (void ) +{ +if (suspend_start_anything (0 )) +return ; + +__suspend2_try_suspend (); + +suspend_finish_anything (0 ); +} + +/* ------------------- Commandline Parameter Handling ----------------- +* +* Resume setup: obtain the storage device. +*/ + +static int __init resume_setup (char *str) +{ +if (str ==NULL ) +return 1 ; + +strncpy (resume2_file, str,255 ); +return 0 ; +} + +/* +* Allow the user to set the action parameter from lilo, prior to resuming. +*/ +static int __init suspend_act_setup (char *str) +{ +if (str) + suspend_action=simple_strtol (str,NULL ,0 ); +set_suspend_state (SUSPEND_ACT_USED ); +return 0 ; +} + +/* +* Allow the user to set the debug parameter from lilo, prior to resuming. +*/ +/* +* Allow the user to specify that we should ignore any image found and +* invalidate the image if necesssary. This is equivalent to running +* the task queue and a sync and then turning off the power. The same +* precautions should be taken: fsck if you're not journalled. +*/ +static int __init noresume_setup (char *str) +{ +set_suspend_state (SUSPEND_NORESUME_SPECIFIED ); +/* Message printed later */ +return 0 ; +} + +static int __init suspend2_retry_resume_setup (char *str) +{ +set_suspend_state (SUSPEND_RETRY_RESUME ); +return 0 ; +} + +__setup ("resume2=" , resume_setup); +__setup ("suspend_act=" , suspend_act_setup); + +#ifdef CONFIG_PM_DEBUG + +static int __init suspend_dbg_setup (char *str) +{ +if (str) + suspend_debug_state=simple_strtol (str,NULL ,0 ); +set_suspend_state (SUSPEND_DBG_USED ); +return 0 ; +} + +/* +* Allow the user to set the debug level parameter from lilo, prior to +* resuming. +*/ +static int __init suspend_lvl_setup (char *str) +{ +if (str) + console_loglevel = + suspend_default_console_level = +simple_strtol (str,NULL ,0 ); +set_suspend_state (SUSPEND_LVL_USED ); +clear_suspend_state (SUSPEND_IGNORE_LOGLEVEL ); +return 0 ; +} + +__setup ("suspend_dbg=" , suspend_dbg_setup); +__setup ("suspend_lvl=" , suspend_lvl_setup); +#endif + +__setup ("noresume2" , noresume_setup); +__setup ("suspend_retry_resume" , suspend2_retry_resume_setup); + +late_initcall (core_load); +EXPORT_SYMBOL (software_suspend_state);
Re: [PATCH] [41/48] Suspend2 2.1.9.8 for 2.6.12: 617-proc.patch
kernel/power/suspend2_core/suspend.c line 377
Quoted 4 lines · Nigel Cunningham
diff -ruNp
618-core.patch-old/kernel/power/suspend2_core/suspend.c618-core.patch-new/kernel/power/suspend2_core/suspend.c
---618-core.patch-old/kernel/power/suspend2_core/suspend.c1970-01-01kernel/power/suspend2_core/suspend.c +2
-0+#define SNPRINTF(a...) len += suspend_snprintf(debug_info_buffer + len, \ + PAGE_SIZE - len - 1, ## a)
Please don't introduce subsystem specific wrappers for generic string manipulation functions. Put them to lib/.
Pekka
PATCH 42/48 Suspend2 2.1.9.8 for 2.6.12: 618-core.patch
In reply to Nigel Cunninghamkernel/power/suspend2_core/userspace-nofreeze.c +69 -0
@@ -0,0 +1,69 @@ +/* +* kernel/power/suspend2_core/userspace-nofreeze.c +* +* Mark/unmark userspace processes as PF_NOFREEZE. +* +* This should be used with extreme caution! +* +* Initial purpose: make nbd-client about to be NOFREEZE. +*/ + +#include <linux/module.h> +#include <linux/suspend.h> +#include <asm/tlbflush.h> +#include "../suspend.h" + +int toggle_pid; + +/* +* toggle_thread_nofreeze +* +* Toggle a thread's PF_NOFREEZE flag +* +* Returns: +* -1: PID not found +* 0: NO_FREEZE cleared. +* 1: NO_FREEZE set. +*/ +static int __toggle_thread_nofreeze (int pid) +{ +struct task_struct *p, *g; +int result =-1 ; + +read_lock (&tasklist_lock); + + do_each_thread(g, p) { +if (p->pid == pid) { +if (p->mm) { + p->flags ^=PF_NOFREEZE ; + result = !!(p->flags &PF_NOFREEZE ); + }else +printk ("Cowardly refusing to toggle NOFREEZE on a real kernel thread!" ); + } + }while_each_thread (g, p); + +read_unlock (&tasklist_lock); + +return result; +} + +void toggle_thread_nofreeze (void ) +{ +int result; +printk ("Seeking %d... " , toggle_pid); + +switch ((result =__toggle_thread_nofreeze (toggle_pid))) + { +case -1 : +printk ("Can't toggle NO_FREEZE for thread - not found.\n" ); +break ; +case 0 : +printk ("NO_FREEZE flag cleared.\n" ); +break ; +case 1 : +printk ("NO_FREEZE flag set.\n" ); +break ; +default : +printk ("what does %d mean?!" , result); + } +}
Re: [PATCH] [42/48] Suspend2 2.1.9.8 for 2.6.12: 618-core.patch
Hi!
Have you seen Christoph's recent fixes to refrigerator? You probably have same problems..... And he wanted to move refrigerator to sched/ and make it more generic for page migration, too... what about using it, too? ;-).
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
Re: [PATCH] [42/48] Suspend2 2.1.9.8 for 2.6.12: 618-core.patch
Hi.
Quoted 6 lines · Pavel Machek
Hi!
Have you seen Christoph's recent fixes to refrigerator? You probably have same problems..... And he wanted to move refrigerator to sched/ and make it more generic for page migration, too... what about using it, too? ;-).
Yeah. I'm using them already. I must say though that I don't think sched.h is necessarily the best place for the refrigerator defines. Any change to those functions and you have to recompile most of the kernel. Is a refrigerator.h an idea?
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: [PATCH] [42/48] Suspend2 2.1.9.8 for 2.6.12: 618-core.patch
Hi!
Quoted 9 lines
Quoted 4 lines
Have you seen Christoph's recent fixes to refrigerator? You probably have same problems..... And he wanted to move refrigerator to sched/ and make it more generic for page migration, too... what about using it, too? ;-).
Yeah. I'm using them already. I must say though that I don't think
sched.his necessarily the best place for the refrigerator defines. Any change to those functions and you have to recompile most of the kernel. Is arefrigerator.han idea?
I do not think those recompiles pose a big problem; we don't want million separate headers. I'd trust Christoph with this.
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
PATCH 43/48 Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch
In reply to Nigel Cunninghaminclude/linux/netlink.h +1 -0
@@ -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 */
kernel/power/suspend2_core/ui.c +1186 -0
1,186 lines not on this page · raw message
kernel/power/suspend2_core/ui.h +20 -0
@@ -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 );
kernel/power/suspend2_core/userui.h +36 -0
@@ -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_ */
Re: [PATCH] [43/48] Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch
Quoted 2 lines · Nigel Cunningham
diff -ruNp
620-userui-header.patch-old/kernel/power/suspend2_core/ui.c620-userui-header.patch-new/kernel/power/suspend2_core/ui.c---620-userui-header.patch-old/kernel/power/suspend2_core/ui.c1970-01-01 10:00:00.000000000 +1000
Is a directory this deep really necessary? Please consider putting this under kernel/power/.
Quoted 18 lines
+++
620-userui-header.patch-new/kernel/power/suspend2_core/ui.c2005-07-05 23:48:59.000000000 +1000kernel/power/suspend2_core/ui.c +16
-0@@ -0,0 +1,1186 @@ +/* +* kernel/power/ui.c +* +* Copyright (C) 1998-2001 Gabor Kuti <[email protected]> +* Copyright (C) 1998,2001,2002 Pavel Machek <[email protected]> +* Copyright (C) 2002-2003 Florent Chabaud <[email protected]> +* Copyright (C) 2002-2005 Nigel Cunningham <[email protected]> +* +* 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:
kernel/power/suspend2_core/ui.c line 26
Quoted 10 lines
kernel/power/suspend2_core/ui.c +10
-0+* - 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 + +#include "../power.h"
Please either move this file under kernel/power/ or move the header to include/linux/.
kernel/power/suspend2_core/ui.c line 82
Quoted 7 lines
kernel/power/suspend2_core/ui.c +7
-0+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 );
Shouldn't these be extern and in a header file?
kernel/power/suspend2_core/ui.c line 226
Quoted 12 lines
kernel/power/suspend2_core/ui.c +12
-0+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 ;
Always returns zero so drop the return value.
kernel/power/suspend2_core/ui.c line 233
Quoted 7 lines
kernel/power/suspend2_core/ui.c +7
-0+} + +static int userui_nl_set_progress_granularity (int n) +{ +if (n <1 ) n =1 ; + progress_granularity = n; +return 0 ;
Same here.
Quoted 12 lines
kernel/power/suspend2_core/ui.c +12
-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 ; +
kernel/power/suspend2_core/ui.c line 255
Quoted 10 lines
kernel/power/suspend2_core/ui.c +10
-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 ;
Just return the error value and errp can go away.
kernel/power/suspend2_core/ui.c line 520
Quoted 4 lines
kernel/power/suspend2_core/ui.c +4
-0+static unsigned long userui_memory_needed (void ) +{ +/* ball park figure of 128 pages */ +return (128 *PAGE_SIZE );
Where does this magic 128 come from?
kernel/power/suspend2_core/ui.c line 528
Quoted 8 lines
kernel/power/suspend2_core/ui.c +8
-0+} + +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 ;
What's this magic 16?
kernel/power/suspend2_core/ui.c line 642
Quoted 9 lines
kernel/power/suspend2_core/ui.c +9
-0+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 ;
What's this magic 32?
kernel/power/suspend2_core/ui.c line 697
Quoted 8 lines
kernel/power/suspend2_core/ui.c +8
-0+/* 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. +*/
Please use proper kerneldoc format instead of inventing your own.
kernel/power/suspend2_core/ui.c line 841
Quoted 8 lines
kernel/power/suspend2_core/ui.c +8
-0+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" );
KERN_* constants please.
kernel/power/suspend2_core/ui.c line 915
Quoted 2 lines
kernel/power/suspend2_core/ui.c +2
-0+extern asmlinkage long sys_ioctl (unsigned int fd,unsigned int cmd, +unsigned long arg);
Looks as if you're doing quite a bit of sys_* calls in the kernel. Could this stuff be pushed out to userspace by any chance?
Re: [PATCH] [43/48] Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch
In reply to Nigel CunninghamUI support is nothing that belongs here. People won't die by having a text console for a while.
Re: [PATCH] [43/48] Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch
Hi.
Quoted 2 lines · Christoph Hellwig
UI support is nothing that belongs here. People won't die by having a text console for a while.
UI support belongs here for the following reasons:
- While driver support is imperfect, there is the potential for hangs. People need to be able to recognise when this happens. They therefore need some feedback to know that the process is not hung, and to be able to say where it hung when it does.
- Particularly for the desktop, we want the system to look professional, not half baked.
- Some support is needed for the user to be able to cancel a suspend. You might not want to. Others might not want to, but this doesn't mean that others shouldn't have the option of changing their minds.
I have already moved much of the code out to userspace in an effort to ease your concerns. I'm not going so far as to remove the functionality, though.
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: [PATCH] [43/48] Suspend2 2.1.9.8 for 2.6.12: 619-userspace-nofreeze.patch
Quoted 3 lines · Nigel Cunningham
People need to be able to recognise when this happens. They therefore need some feedback to know that the process is not hung, and to be able to say where it hung when it does.
this is called printk.
PATCH 44/48 Suspend2 2.1.9.8 for 2.6.12: 620-userui.patch
In reply to Nigel Cunninghamkernel/power/swsusp.c +3 -5
@@ -36,6 +36,8 @@* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt */ +#define KERNEL_POWER_SWSUSP_C +#include <linux/module.h> #include <linux/mm.h> #include <linux/suspend.h>
@@ -51,9 +53,7 @@#include <linux/keyboard.h> #include <linux/spinlock.h> #include <linux/genhd.h> -#include <linux/kernel.h> #include <linux/major.h> -#include <linux/swap.h> #include <linux/pm.h> #include <linux/device.h> #include <linux/buffer_head.h>
@@ -71,9 +71,7 @@#include <asm/io.h> #include "power.h" - -/* References to section boundaries */ -extern const void __nosave_begin, __nosave_end; +#include "suspend.h" /* Variables to be preserved over suspend */ static int nr_copy_pages_check;
Re: [PATCH] [44/48] Suspend2 2.1.9.8 for 2.6.12: 620-userui.patch
kernel/power/swsusp.c line 39
Quoted 8 lines · Nigel Cunningham
diff -ruNp
621-swsusp-tidy.patch-old/kernel/power/swsusp.c621-swsusp-tidy.patch-new/kernel/power/swsusp.ckernel/power/swsusp.c +1
-0@@ -36,6 +36,8 @@* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt */ +#define KERNEL_POWER_SWSUSP_C
Headers that change depending on #define's are considered evil......
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
Re: [PATCH] [44/48] Suspend2 2.1.9.8 for 2.6.12: 620-userui.patch
Hi
Quoted 11 lines · Pavel Machek
kernel/power/swsusp.c line 39
Quoted 8 lines · Nigel Cunningham
diff -ruNp
621-swsusp-tidy.patch-old/kernel/power/swsusp.c621-swsusp-tidy.patch-new/kernel/power/swsusp.ckernel/power/swsusp.c +1
-0@@ -36,6 +36,8 @@* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt */ +#define KERNEL_POWER_SWSUSP_C Headers that change depending on #define's are considered evil......
Fixed. Thanks!
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
PATCH 45/48 Suspend2 2.1.9.8 for 2.6.12: 621-swsusp-tidy.patch
In reply to Nigel Cunninghamkernel/power/suspend_swap.c +1713 -0
@@ -0,0 +1,1713 @@ +/* +* Swapwriter.c +* +* Copyright 2004-2005 Nigel Cunningham <[email protected]> +* +* Distributed under GPLv2. +* +* This file encapsulates functions for usage of swap space as a +* backing store. +*/ + +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/blkdev.h> +#include <linux/swapops.h> + +#include "suspend2_core/suspend.h" +#include "suspend2_core/suspend2_common.h" +#include "suspend2_core/version.h" +#include "suspend2_core/proc.h" +#include "suspend2_core/plugins.h" +#include "suspend2_core/io.h" +#include "suspend2_core/ui.h" +#include "suspend2_core/extent.h" +#include "suspend2_core/utility.h" + +#include "block_io.h" + +static struct suspend_plugin_ops swapwriterops; + +#define SIGNATURE_VER 6 +#define BYTES_PER_HEADER_PAGE (PAGE_SIZE - sizeof(swp_entry_t)) + +/* --- Struct of pages stored on disk */ + +struct swaplink { +char dummy[BYTES_PER_HEADER_PAGE ]; +swp_entry_t next; +}; + +union diskpage { +union swap_header swh;/* swh.magic is the only member used */ +struct swaplink link; +struct suspend_header sh; +}; + +union p_diskpage { +union diskpage *pointer; +char *ptr; +unsigned long address; +}; + +// Higher Level +static int readahead_index =0 , readahead_submit_index =0 ; +static int readahead_allocs =0 , readahead_frees =0 ; + +static char * swapwriter_buffer =NULL ; +static int swapwriter_buffer_posn =0 ; +static unsigned long * header_link =NULL ; + +/* +* --------------------------------------------------------------- +* +* Internal Data Structures +* +* --------------------------------------------------------------- +*/ + +/* header_data contains data that is needed to reload pagedir1, and +* is therefore saved in the suspend header. +* +* Pagedir2 swap comes before pagedir1 swap (save order), and the first swap +* entry for pagedir1 to use is set when pagedir2 is written (when we know how +* much swap it used). Since this first entry is almost certainly not at the +* start of a extent, the firstoffset variable below tells us where to start in +* the extent. All of this means we don't have to worry about getting different +* compression ratios for the kernel and cache (when compressing the image). +* We can simply allocate one pool of swap (size determined using expected +* compression ratio) and use it without worrying whether one pageset +* compresses better and the other worse (this is what happens). As long as the +* user gets the expected compression right, it will work. +*/ + +struct { +/* Extent chains for swap & blocks */ +struct extentchain swapextents; +struct extentchain block_chain[MAX_SWAPFILES ]; + +/* Location of start of pagedir 1 */ +struct extent * pd1start_block_extent; +int pd1start_chain; +int pd1start_extent_number; +unsigned long pd1start_block_offset; + +/* Devices used for swap */ +dev_t swapdevs[MAX_SWAPFILES ]; +char blocksizes[MAX_SWAPFILES ]; + +} header_data; + +static dev_t header_device =0 ; +static struct block_device * header_block_device =NULL ; +static int headerblocksize =PAGE_SIZE ; +static int headerblock; + +/* For swapfile automatically swapon/off'd. */ +static char swapfilename[256 ] ="" ; +extern asmlinkage long sys_swapon (const char * specialfile,int swap_flags); +extern asmlinkage long sys_swapoff (const char * specialfile); +static int suspend_swapon_status =0 ; + +/* +* --------------------------------------------------------------- +* +* Current state. +* +* --------------------------------------------------------------- +*/ + +/* Which pagedir are we saving/reloading? Needed so we can know whether to +* remember the last swap entry used at the end of writing pageset2, and +* get that location when saving or reloading pageset1.*/ +static int current_stream =0 ; + +/* Pointer to current swap entry being loaded/saved. */ +static struct extent * currentblockextent =NULL ; +static unsigned long currentblockoffset =0 ; +static int currentblockchain =0 ; +static int currentblocksperpage =0 ; + +/* Header Page Information */ +static int header_pages_allocated =0 ; +static struct submit_params * first_header_submit_info =NULL , + * last_header_submit_info =NULL , * current_header_submit_info =NULL ; + +/* +* --------------------------------------------------------------- +* +* User Specified Parameters +* +* --------------------------------------------------------------- +*/ + +static int resume_firstblock =0 ; +static int resume_firstblocksize =PAGE_SIZE ; +static dev_t resume_device =0 ; +static struct block_device * resume_block_device =NULL ; + +struct sysinfo swapinfo; +static int swapwriter_invalidate_image (void ); + +static void cleanup_opened_devices (int including_swapdevices) +{ +int i; + +/* Cleanup our open_by_devnums. +* +* Other swap devices, opened when reading pageset1, are +* either cleaned up by noresume_reset +*/ + +if (header_block_device && header_block_device != resume_block_device) +blkdev_put (header_block_device); + +if (resume_block_device) +blkdev_put (resume_block_device); + +/* When reading pageset1 header */ +if (including_swapdevices) { +for (i =0 ; i <MAX_SWAPFILES ; i++) { +struct block_device * this_dev = swap_info[i].bdev; +if (this_dev && this_dev != resume_block_device && + this_dev != header_block_device) { +blkdev_put (swap_info[i].bdev); + swap_info[i].bdev =NULL ; + } + } + } + + header_block_device = resume_block_device =NULL ; +} +/* Must be silent - might be called from cat /proc/suspend/debug_info +* Returns 0 if was off, -EBUSY if was on, error value otherwise. +*/ +static int enable_swapfile (void ) +{ +int activateswapresult = -EINVAL ; + +if (suspend_swapon_status) +return 0 ; + +if (swapfilename[0 ]) { +/* Attempt to swap on with maximum priority */ + activateswapresult =sys_swapon (swapfilename,0xFFFF ); +if ((activateswapresult) && (activateswapresult != -EBUSY )) +printk (name_suspend +"The swapfile/partition specified by " +"/proc/suspend/swapfile (%s) could not" +" be turned on (error %d). Attempting " +"to continue.\n" , + swapfilename, activateswapresult); +if (!activateswapresult) + suspend_swapon_status =1 ; + } +return activateswapresult; +} + +/* Returns 0 if was on, -EINVAL if was off, error value otherwise */ +static int disable_swapfile (void ) +{ +int result = -EINVAL ; + +if (!suspend_swapon_status) +return 0 ; + +if (swapfilename[0 ]) { + result =sys_swapoff (swapfilename); +if (result == -EINVAL ) +return 0 ;/* Wasn't on */ +if (!result) + suspend_swapon_status =0 ; + } + +return result; +} + +static int manage_swapfile (int enable) +{ +static int result; + +if (enable) + result =enable_swapfile (); +else + result =disable_swapfile (); + +return result; +} + +static void get_header_params (struct submit_params * headerpage) +{ +swp_entry_t entry = headerpage->swap_address; +int swapfilenum =swp_type (entry); +unsigned long offset =swp_offset (entry); +struct swap_info_struct * sis =get_swap_info_struct (swapfilenum); +sector_t sector =map_swap_page (sis, offset); + + headerpage->dev = sis->bdev, + headerpage->block[0 ] = sector; +//headerpage->blocks_used = 1; + headerpage->readahead_index =-1 ; +} + +static inline int get_blocks_per_page (int chain) +{ +int result =PAGE_SIZE / + suspend_bio_ops.get_block_size (swap_info[chain].bdev); +return result; +} + +static int try_to_parse_header_device (void ) +{ + header_block_device =open_by_devnum (header_device,FMODE_READ ); + +if (IS_ERR (header_block_device) || (!header_block_device)) { +if (suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Failed to get access to the " +"resume header device.\nYou could be " +"booting with a 2.6 kernel when you " +"suspended a 2.4 kernel." )) +swapwriter_invalidate_image (); +return -EINVAL ; + } + +if (set_blocksize (header_block_device,PAGE_SIZE ) <0 ) { +if (suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Failed to set the blocksize for a swap device." )) +do { }while (0 ); +swapwriter_invalidate_image (); +return -EINVAL ; + } + +return 0 ; +} + +static int try_to_open_resume_device (void ) +{ + resume_block_device =open_by_devnum (resume_device,FMODE_READ ); + +if (IS_ERR (resume_block_device) || (!resume_block_device)) +return 1 ; + +return 0 ; +} + +static int try_to_parse_resume_device (char * commandline) +{ +struct kstat stat; +int error; + + resume_device =name_to_dev_t (commandline); + +if (!resume_device) { + error =vfs_stat (commandline, &stat); +if (!error) + resume_device = stat.rdev; + } + +if (!resume_device) { +if (test_suspend_state (SUSPEND_TRYING_TO_RESUME )) +suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Failed to translate \"%s\" into a device id.\n" , + commandline); +else +printk (name_suspend"Can't translate \"%s\" into a device id yet.\n" , + commandline); +return 1 ; + } + +if (try_to_open_resume_device ()) { +suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Failed to get access to \"%s\", where" +" the swap header should be found." , + commandline); +return 1 ; + } + +return 0 ; +} + +static void open_other_swap_device (int i,dev_t thisdevice) +{ +if ((swap_info[i].bdev =open_by_devnum (thisdevice,FMODE_READ ))) +set_blocksize (swap_info[i].bdev,PAGE_SIZE ); +} + +static inline char *get_path_for_swapfile (int which,char * path_page) +{ +return d_path ( swap_info[which].swap_file->f_dentry, + swap_info[which].swap_file->f_vfsmnt, + path_page, +PAGE_SIZE ); +} + +/* +* If we have read part of the image, we might have filled header_data with +* data that should be zeroed out. +*/ +static void swapwriter_noresume_reset (void ) +{ +memset ((char *) &header_data,0 ,sizeof (header_data)); +cleanup_opened_devices (1 ); +} + +static int parse_signature (char * header,int restore) +{ +int type =-1 ; + +if (!memcmp ("SWAP-SPACE" ,header,10 )) +return 0 ; +else if (!memcmp ("SWAPSPACE2" ,header,10 )) +return 1 ; + +else if (!memcmp ("pmdisk" , header,6 )) + type =2 ; + +else if (!memcmp ("S1SUSP" ,header,6 )) + type =4 ; +else if (!memcmp ("S2SUSP" ,header,6 )) + type =5 ; + +else if (!memcmp ("z" ,header,1 )) + type =12 ; +else if (!memcmp ("Z" ,header,1 )) + type =13 ; + +/* +* Put bdev of suspend header in last byte of swap header +* (unsigned short) +*/ +if (type >11 ) { +dev_t * header_ptr = (dev_t *) &header[1 ]; +unsigned char * headerblocksize_ptr = + (unsigned char *) &header[5 ]; +unsigned long * headerblock_ptr = (unsigned long *) &header[6 ]; + header_device = *header_ptr; +/* +* We are now using the highest bit of the char to indicate +* whether we have attempted to resume from this image before. +*/ +clear_suspend_state (SUSPEND_RESUMED_BEFORE ); +if (((int ) *headerblocksize_ptr) &0x80 ) +set_suspend_state (SUSPEND_RESUMED_BEFORE ); + headerblocksize =512 * (((int ) *headerblocksize_ptr) &0xf ); + headerblock = *headerblock_ptr; + } + +if ((restore) && (type >5 )) { +/* We only reset our own signatures */ +if (type &1 ) +memcpy (header,"SWAPSPACE2" ,10 ); +else +memcpy (header,"SWAP-SPACE" ,10 ); + } + +return type; +} + +/* +* prepare_signature +*/ + +static int prepare_signature (struct submit_params * header_page_info, +char * current_header) +{ +int current_type =parse_signature (current_header,0 ); +dev_t * header_ptr = (dev_t *) (¤t_header[1 ]); +unsigned char * headerblocksize_ptr = + (unsigned char *) (¤t_header[5 ]); +unsigned long * headerblock_ptr = + (unsigned long *) (¤t_header[6 ]); + +if ((current_type >1 ) && (current_type <6 )) +return 1 ; + +if (current_type &1 ) + current_header[0 ] ='Z' ; +else + current_header[0 ] ='z' ; + *header_ptr = header_page_info->dev->bd_dev; + *headerblocksize_ptr = + (unsigned char ) (header_page_info->dev->bd_block_size >>9 ); +/* prev is the first/last swap page of the resume area */ + *headerblock_ptr = (unsigned long ) header_page_info->block[0 ]; +return 0 ; +} + +extern int signature_check (char * header,int fix); + +static int free_swap_pages_for_header (void ) +{ +if (!first_header_submit_info) +return 1 ; + +while (first_header_submit_info) { +struct submit_params * next = first_header_submit_info->next; +if (first_header_submit_info->swap_address.val) +swap_free (first_header_submit_info->swap_address); +kfree (first_header_submit_info); + first_header_submit_info = next; + } + + first_header_submit_info = last_header_submit_info =NULL ; + header_pages_allocated =0 ; +return 0 ; +} + +static void get_main_pool_phys_params (void ) +{ +struct extent * extentpointer =NULL ; +unsigned long address; +int i; +int extent_min =-1 , extent_max =-1 ; +int last_chain =-1 ; + +for (i =0 ; i <MAX_SWAPFILES ; i++) +if (header_data.block_chain[i].first) +put_extent_chain (&header_data.block_chain[i]); + +extent_for_each (&header_data.swapextents, extentpointer, address) { +swp_entry_t swap_address =extent_val_to_swap_entry (address); +int swapfilenum =swp_type (swap_address); +unsigned long offset =swp_offset (swap_address); +struct swap_info_struct * sis =get_swap_info_struct (swapfilenum); +sector_t new_sector =map_swap_page (sis, offset); + +if ((new_sector == extent_max +1 ) && + (last_chain == swapfilenum)) + extent_max++; +else { +if (extent_min >0 ) +append_extent_to_extent_chain ( + &header_data.block_chain[last_chain], + extent_min, extent_max); + extent_min = extent_max = new_sector; + last_chain = swapfilenum; + } + } +if (extent_min >-1 ) +append_extent_to_extent_chain ( + &header_data.block_chain[last_chain], + extent_min, extent_max); +} + +static int swapwriter_storage_allocated (void ) +{ +int result; + result = header_data.swapextents.size + header_pages_allocated; +return result; +} + +static int swapwriter_storage_available (void ) +{ +int result; +si_swapinfo (&swapinfo); + result = swapinfo.freeswap +swapwriter_storage_allocated (); +return result; +} + +static int swapwriter_initialise (int starting_cycle) +{ +if (starting_cycle) +manage_swapfile (1 ); + +if (!resume_block_device &&try_to_open_resume_device ()) +return 1 ; + +return 0 ; +} + +static void swapwriter_cleanup (int ending_cycle) +{ +if (ending_cycle) +manage_swapfile (0 ); + +cleanup_opened_devices (0 ); +} + +static int swapwriter_release_storage (void ) +{ +int i =0 ; + +if ((TEST_ACTION_STATE (SUSPEND_KEEP_IMAGE )) &&test_suspend_state (SUSPEND_NOW_RESUMING )) +return 0 ; + +free_swap_pages_for_header (); + +if (header_data.swapextents.first) { +/* Free swap entries */ +struct extent * extentpointer; +unsigned long extentvalue; +swp_entry_t entry; +extent_for_each (&header_data.swapextents, extentpointer, + extentvalue) { + entry =extent_val_to_swap_entry (extentvalue); +swap_free (entry); + } +put_extent_chain (&header_data.swapextents); + +for (i =0 ; i <MAX_SWAPFILES ; i++) +if (header_data.block_chain[i].first) +put_extent_chain (&header_data.block_chain[i]); + } + +return 0 ; +} + +static int swapwriter_allocate_header_space (int space_really_requested) +{ +/* space_requested was going to be in bytes... not yet */ +int i; +int ret =0 ; +long space_requested; + +/* +* Up to here in the process, we haven't taken account of the fact +* that we need an extra four bytes per 4092 bytes written for link +* to the next page on which the header will be written. We factor +* that in here. +*/ + space_requested = ((long ) space_really_requested) <<PAGE_SHIFT ; + space_requested = ((space_really_requested +4091 ) /4092 ); + space_requested = (space_requested *4 +4091 ) /4092 ; + space_requested += space_really_requested; + +for (i=(header_pages_allocated+1 ); i<=space_requested; i++) { +struct submit_params * new_submit_param; + +/* Get a submit structure */ + new_submit_param =kmalloc (sizeof (struct submit_params ),GFP_ATOMIC ); + +if (!new_submit_param) { + header_pages_allocated = i -1 ; +printk ("Failed to kmalloc a struct submit param.\n" ); + ret = -ENOMEM ; + goto out; + } + +memset (new_submit_param,0 ,sizeof (struct submit_params )); + +if (last_header_submit_info) { + last_header_submit_info->next = new_submit_param; + last_header_submit_info = new_submit_param; + }else + last_header_submit_info = first_header_submit_info = + new_submit_param; + +/* Get swap entry */ + new_submit_param->swap_address =get_swap_page (); + +if ((!new_submit_param->swap_address.val) && + (header_data.swapextents.first)) { +/* +* Steal one from pageset swap chain. If, as a result, +* it is too small, more swap will be allocated or +* memory eaten. +*/ + + new_submit_param->swap_address = +extent_val_to_swap_entry ( + header_data.swapextents.first->minimum); +if (header_data.swapextents.first->minimum < + header_data.swapextents.first->maximum) + header_data.swapextents.first->minimum++; +else { +struct extent * oldfirst = + header_data.swapextents.first; + header_data.swapextents.first = oldfirst->next; + header_data.swapextents.frees++; +if (header_data.swapextents.last == oldfirst) + header_data.swapextents.last =NULL ; +put_extent (oldfirst); + } + + header_data.swapextents.size--; + +/* +* Recalculate block chains for main pool. +* We don't assume blocks are at start of a chain and +* don't know how many blocks per swap entry. +*/ +get_main_pool_phys_params (); + } +if (!new_submit_param->swap_address.val) { +free_swap_pages_for_header (); +printk ("Unable to allocate swap page for header.\n" ); + ret = -ENOMEM ; + goto out; + } + +get_header_params (new_submit_param); + } + header_pages_allocated = space_requested; +out: +return ret; +} + +static int swapwriter_allocate_storage (int space_requested) +{ +int i, result =0 ; +int pages_to_get = space_requested - header_data.swapextents.size; +int extent_min =-1 , extent_max =-1 ; + +if (pages_to_get <1 ) +return 0 ; + +for (i=0 ; i < pages_to_get; i++) { +swp_entry_t entry; +int new_value; + + entry =get_swap_page (); +if (!entry.val) { + result = -ENOSPC ; + goto out; + } + + new_value =swap_entry_to_extent_val (entry); +if (new_value == extent_max +1 ) + extent_max++; +else { +if (extent_min >-1 ) +append_extent_to_extent_chain ( + &header_data.swapextents, + extent_min, extent_max); + extent_min = extent_max = new_value; + } + } + +if (extent_min >-1 ) +append_extent_to_extent_chain ( + &header_data.swapextents, + extent_min, extent_max); + +out: +get_main_pool_phys_params (); +return result; +} + +static int swapwriter_write_header_chunk (char * buffer,int buffer_size) +{ +int bytes_left = buffer_size; + +/* +* We buffer the writes until a page is full and to use the last +* sizeof(swp_entry_t) bytes for links between pages. This is +* totally transparent to the caller. +* +* Note also that buffer_size can be > PAGE_SIZE. +*/ + +while (bytes_left) { +char * source_start = buffer + buffer_size - bytes_left; +char * dest_start = swapwriter_buffer + swapwriter_buffer_posn; +int dest_capacity =BYTES_PER_HEADER_PAGE - swapwriter_buffer_posn; +swp_entry_t next_header_page; +if (bytes_left <= dest_capacity) { +memcpy (dest_start, source_start, bytes_left); + swapwriter_buffer_posn += bytes_left; +return 0 ; + } + +/* A page is full */ +memcpy (dest_start, source_start, dest_capacity); + bytes_left -= dest_capacity; + +BUG_ON (!current_header_submit_info); + +if (!current_header_submit_info->next) + *header_link =0 ; +else { + next_header_page = +swp_entry (swp_type ( + current_header_submit_info->next->swap_address), + current_header_submit_info->next->block[0 ]); + + *header_link = next_header_page.val; + } + + current_header_submit_info->page = +virt_to_page (swapwriter_buffer); +check_shift_keys (0 ,NULL ); + suspend_bio_ops.submit_io (WRITE , current_header_submit_info,0 ); + + swapwriter_buffer_posn =0 ; + current_header_submit_info = current_header_submit_info->next; + } + +return 0 ; +} + +static int swapwriter_write_header_init (void ) +{ +int i; +struct extent * extent; + +for (i =0 ; i <MAX_SWAPFILES ; i++) +if (swap_info[i].swap_file) { + header_data.swapdevs[i] = swap_info[i].bdev->bd_dev; + header_data.blocksizes[i] = +block_size (swap_info[i].bdev); + } + + swapwriter_buffer = (char *)get_zeroed_page (GFP_ATOMIC ); + header_link = + (unsigned long *) (swapwriter_buffer +BYTES_PER_HEADER_PAGE ); + + current_header_submit_info = first_header_submit_info; + + header_data.pd1start_extent_number =1 ; + extent = header_data.block_chain[header_data.pd1start_chain].first; + +while (extent != header_data.pd1start_block_extent) { + header_data.pd1start_extent_number++; + extent = extent->next; + } + +/* Info needed to bootstrap goes at the start of the header. +* First we save the 'header_data' struct, including the number +* of header pages. Then we save the structs containing data needed +* for reading the header pages back. +* Note that even if header pages take more than one page, when we +* read back the info, we will have restored the location of the +* next header page by the time we go to use it. +*/ +return swapwriter_write_header_chunk ((char *) &header_data, +sizeof (header_data)); +} + +static int swapwriter_write_header_cleanup (void ) +{ +/* Write any unsaved data */ +if (swapwriter_buffer_posn) { + *header_link =0 ; + + current_header_submit_info->page = +virt_to_page (swapwriter_buffer); + suspend_bio_ops.submit_io (WRITE , + current_header_submit_info,0 ); + } + +/* Adjust swap header */ + suspend_bio_ops.bdev_page_io (READ , resume_block_device, resume_firstblock, +virt_to_page (swapwriter_buffer)); + +prepare_signature (first_header_submit_info, + ((union swap_header *) swapwriter_buffer)->magic.magic); + + suspend_bio_ops.bdev_page_io (WRITE , resume_block_device, resume_firstblock, +virt_to_page (swapwriter_buffer)); + +free_pages ((unsigned long ) swapwriter_buffer,0 ); + swapwriter_buffer =NULL ; + header_link =NULL ; + + suspend_bio_ops.finish_all_io (); + +return 0 ; +} + +/* ------------------------- HEADER READING ------------------------- */ + +/* +* read_header_init() +* +* Description: +* 1. Attempt to read the device specified with resume2=. +* 2. Check the contents of the swap header for our signature. +* 3. Warn, ignore, reset and/or continue as appropriate. +* 4. If continuing, read the swapwriter configuration section +* of the header and set up block device info so we can read +* the rest of the header & image. +* +* Returns: +* May not return if user choose to reboot at a warning. +* -EINVAL if cannot resume at this time. Booting should continue +* normally. +*/ + +static int swapwriter_read_header_init (void ) +{ +int i; + +BUG_ON (!resume_block_device); + + swapwriter_buffer = (char *)get_zeroed_page (GFP_ATOMIC ); + +if (!header_device) { +printk ("read_header_init called when we haven't " +"verified there is an image!\n" ); +return -EINVAL ; + } + +/* +* If the header is not on the resume_device, get the resume device first. +*/ +if (header_device != resume_device) { +int result =try_to_parse_header_device (); + +if (result) +return result; + }else + header_block_device = resume_block_device; + +/* Read swapwriter configuration */ + suspend_bio_ops.bdev_page_io (READ , header_block_device, headerblock, +virt_to_page ((unsigned long ) swapwriter_buffer)); + +memcpy (&header_data, swapwriter_buffer,sizeof (header_data)); + +/* Restore device info */ +for (i =0 ; i <MAX_SWAPFILES ; i++) { +dev_t thisdevice = header_data.swapdevs[i]; + + swap_info[i].bdev =NULL ; + +if (!thisdevice) +continue ; + +if (thisdevice == resume_device) { + swap_info[i].bdev = resume_block_device; +/* Mark as used so the device doesn't get suspended. */ + swap_info[i].swap_file = (struct file *)0xffffff ; +continue ; + } + +if (thisdevice == header_device) { + swap_info[i].bdev = header_block_device; +/* Mark as used so the device doesn't get suspended. */ + swap_info[i].swap_file = (struct file *)0xffffff ; +continue ; + } + +open_other_swap_device (i, thisdevice); + swap_info[i].swap_file = (struct file *)0xffffff ; + } + + swapwriter_buffer_posn =sizeof (header_data); + +return 0 ; +} + +static int swapwriter_read_header_chunk (char * buffer,int buffer_size) +{ +int bytes_left = buffer_size, ret =0 ; + +check_shift_keys (0 ,"" ); + +/* Read a chunk of the header */ +while ((bytes_left) && (!ret)) { +swp_entry_t next = + ((union p_diskpage ) swapwriter_buffer).pointer->link.next; +struct block_device * dev = swap_info[swp_type (next)].bdev; +int pos =swp_offset (next); +char * dest_start = buffer + buffer_size - bytes_left; +char * source_start = + swapwriter_buffer + swapwriter_buffer_posn; +int source_capacity = +BYTES_PER_HEADER_PAGE - swapwriter_buffer_posn; + +if (bytes_left <= source_capacity) { +memcpy (dest_start, source_start, bytes_left); + swapwriter_buffer_posn += bytes_left; +return buffer_size; + } + +/* Next to read the next page */ +memcpy (dest_start, source_start, source_capacity); + bytes_left -= source_capacity; + + suspend_bio_ops.bdev_page_io (READ , dev, pos,virt_to_page (swapwriter_buffer)); + + swapwriter_buffer_posn =0 ; + } + +return buffer_size - bytes_left; +} + +static int swapwriter_read_header_cleanup (void ) +{ +free_pages ((unsigned long ) swapwriter_buffer,0 ); +return 0 ; +} + +static int swapwriter_serialise_extents (void ) +{ +int i; + +serialise_extent_chain (&header_data.swapextents); + +for (i =0 ; i <MAX_SWAPFILES ; i++) +serialise_extent_chain (&header_data.block_chain[i]); + +return 0 ; +} + +static int swapwriter_load_extents (void ) +{ +int i; +struct extent * extent; + +load_extent_chain (&header_data.swapextents); + +for (i =0 ; i <MAX_SWAPFILES ; i++) +load_extent_chain (&header_data.block_chain[i]); + + extent = header_data.block_chain[header_data.pd1start_chain].first; + + i =1 ; +while (i < header_data.pd1start_extent_number) { + extent = extent->next; + i++; + } + + header_data.pd1start_block_extent = extent; + +return 0 ; +} + +static int swapwriter_write_init (int stream_number) +{ + current_stream = stream_number; + +if (current_stream ==1 ) { + currentblockextent = header_data.pd1start_block_extent; + currentblockoffset = header_data.pd1start_block_offset; + currentblockchain = header_data.pd1start_chain; + }else +for (currentblockchain =0 ; currentblockchain <MAX_SWAPFILES ; + currentblockchain++) +if (header_data.block_chain[currentblockchain].first) { + currentblockextent = + header_data. + block_chain[currentblockchain].first; + currentblockoffset = currentblockextent->minimum; +break ; + } + +BUG_ON (!currentblockextent); + + currentblocksperpage =get_blocks_per_page (currentblockchain); + + suspend_bio_ops.reset_io_stats (); + +return 0 ; +} + +static int swapwriter_write_chunk (struct page * buffer_page) +{ +int i; +struct submit_params submit_params; + +if (TEST_ACTION_STATE (SUSPEND_TEST_FILTER_SPEED )) +return 0 ; + +if (currentblockchain ==MAX_SWAPFILES ) { +printk ("Error! We have run out of blocks for writing data.\n" ); +return -ENOSPC ; + } + +if (!currentblockextent) { +do { + currentblockchain++; + }while ((currentblockchain <MAX_SWAPFILES ) && + (!header_data.block_chain[currentblockchain].first)); + +/* We can validly not have a new blockextent. We +* might be compressing data and the user was +* too optimistic in setting the compression +* ratio or we're just copying the pageset. */ + +if (currentblockchain ==MAX_SWAPFILES ) { +printk ("Argh. Ran out of block chains.\n" ); +return -ENOSPC ; + } + + currentblockextent = + header_data.block_chain[currentblockchain].first; + currentblockoffset = currentblockextent->minimum; + currentblocksperpage =get_blocks_per_page (currentblockchain); + } + + submit_params.readahead_index =-1 ; + submit_params.page = buffer_page; + submit_params.dev = swap_info[currentblockchain].bdev; + +/* Get the blocks */ + submit_params.block[0 ] = currentblockoffset; +for (i =0 ; i < currentblocksperpage; i++) +GET_EXTENT_NEXT (currentblockextent, currentblockoffset); + + suspend_bio_ops.submit_io (WRITE , &submit_params,0 ); + +check_shift_keys (0 ,NULL ); + +return 0 ; +} + +static int swapwriter_write_cleanup (void ) +{ +if (current_stream ==2 ) { + header_data.pd1start_block_extent = currentblockextent; + header_data.pd1start_block_offset = currentblockoffset; + header_data.pd1start_chain = currentblockchain; + } + + suspend_bio_ops.finish_all_io (); + + suspend_bio_ops.check_io_stats (); + +return 0 ; +} + +static int swapwriter_read_init (int stream_number) +{ + current_stream = stream_number; + +if (current_stream ==1 ) { + currentblockextent = header_data.pd1start_block_extent; + currentblockoffset = header_data.pd1start_block_offset; + currentblockchain = header_data.pd1start_chain; + }else { + currentblockextent =NULL ; + currentblockoffset =0 ; + currentblockchain =0 ; +for (currentblockchain =0 ; currentblockchain <MAX_SWAPFILES ; + currentblockchain++) +if (header_data.block_chain[currentblockchain].first) { + currentblockextent = + header_data.block_chain[currentblockchain].first; + currentblockoffset = currentblockextent->minimum; +break ; + } + +BUG_ON (!currentblockextent); + } + + currentblocksperpage =get_blocks_per_page (currentblockchain); + + suspend_bio_ops.reset_io_stats (); + + readahead_index = readahead_submit_index =-1 ; + readahead_allocs = readahead_frees =0 ; + +return 0 ; +} + +static int swapwriter_begin_read_chunk (struct page * page, +int readahead_index,int sync) +{ +int i; +struct submit_params submit_params; + +if (currentblockchain ==MAX_SWAPFILES ) { +/* Readahead might ask us to read too many blocks */ +printk ("Currentblockchain == MAX_SWAPFILES. Begin_read_chunk returning -ENODATA.\n" ); +return -ENODATA ; + } + +if (!currentblockextent) { +do { + currentblockchain++; + }while ((currentblockchain <MAX_SWAPFILES ) && + (!header_data.block_chain[currentblockchain].first)); + +/* We can validly not have a new blockextent. We +* might have allocated exactly the right amount +* of swap for the image and be reading the last +* block now. +*/ + +/* Readahead might ask us to read too many blocks */ +if (currentblockchain ==MAX_SWAPFILES ) +return -ENODATA ; + + currentblockextent = + header_data.block_chain[currentblockchain].first; + currentblockoffset = currentblockextent->minimum; + currentblocksperpage =get_blocks_per_page (currentblockchain); + } + + submit_params.readahead_index = readahead_index; + submit_params.page = page; + submit_params.dev = swap_info[currentblockchain].bdev; + +/* Get the blocks. There is no chance that they span chains. */ + submit_params.block[0 ] = currentblockoffset; +for (i =0 ; i < currentblocksperpage; i++) +GET_EXTENT_NEXT (currentblockextent, currentblockoffset); + +if ((i = suspend_bio_ops.submit_io (READ , &submit_params, sync))) +return -EPERM ; + +check_shift_keys (0 ,NULL ); + +return 0 ; +} + +/* Note that we ignore the sync parameter. We are implementing +* read ahead, and will always wait until our readhead buffer has +* been read before returning. +*/ + +static int swapwriter_read_chunk (struct page * buffer_page,int sync) +{ +static int last_result; +unsigned long * virt; + +/* If we only use readahead when reading synchronously. */ +if (sync ==SUSPEND_ASYNC ) +return swapwriter_begin_read_chunk (buffer_page,-1 , sync); + +/* Start new readahead while we wait for our page */ +if (readahead_index ==-1 ) { + last_result =0 ; + readahead_index = readahead_submit_index =0 ; + } + +/* Start a new readahead? */ +if (last_result) { +/* We failed to submit a read, and have cleaned up +* all the readahead previously submitted */ +if (readahead_submit_index == readahead_index) +return -EIO ; + goto wait; + } + +do { +if (suspend_bio_ops.prepare_readahead (readahead_submit_index)) +break ; + + readahead_allocs++; + + last_result =swapwriter_begin_read_chunk ( + suspend_bio_ops.readahead_pages[readahead_submit_index], + readahead_submit_index,SUSPEND_ASYNC ); +if (last_result) { + suspend_bio_ops.cleanup_readahead (readahead_submit_index); + readahead_frees++; +break ; + } + + readahead_submit_index++; + +if (readahead_submit_index ==MAX_READAHEAD ) + readahead_submit_index =0 ; + + }while ((!last_result) && (readahead_submit_index != readahead_index) && + (!suspend_bio_ops.readahead_ready (readahead_index))); + +wait: + suspend_bio_ops.wait_on_readahead (readahead_index); + + virt =kmap_atomic (buffer_page,KM_USER1 ); + +memcpy (virt,page_address (suspend_bio_ops.readahead_pages[readahead_index]), +PAGE_SIZE ); +kunmap_atomic (virt,KM_USER1 ); + + suspend_bio_ops.cleanup_readahead (readahead_index); + + readahead_frees++; + + readahead_index++; +if (readahead_index ==MAX_READAHEAD ) + readahead_index =0 ; + +return 0 ; +} + +static int swapwriter_read_cleanup (void ) +{ + suspend_bio_ops.finish_all_io (); +while (readahead_index != readahead_submit_index) { + suspend_bio_ops.cleanup_readahead (readahead_index); + readahead_frees++; + readahead_index++; +if (readahead_index ==MAX_READAHEAD ) + readahead_index =0 ; + } + suspend_bio_ops.check_io_stats (); +BUG_ON (readahead_allocs != readahead_frees); + +return 0 ; +} + +/* swapwriter_invalidate_image +* +*/ +static int swapwriter_invalidate_image (void ) +{ +union p_diskpage cur; +int result =0 ; +char newsig[11 ]; + + cur.address =get_zeroed_page (GFP_ATOMIC ); +if (!cur.address) { +printk ("Unable to allocate a page for restoring the swap signature.\n" ); +return -ENOMEM ; + } + +/* +* If nr_suspends == 0, we must be booting, so no swap pages +* will be recorded as used yet. +*/ + +if (nr_suspends >0 ) +swapwriter_release_storage (); + +/* +* We don't do a sanity check here: we want to restore the swap +* whatever version of kernel made the suspend image. +* +* We need to write swap, but swap may not be enabled so +* we write the device directly +*/ + + suspend_bio_ops.bdev_page_io (READ , resume_block_device, + resume_firstblock,virt_to_page (cur.pointer)); + + result =parse_signature (cur.pointer->swh.magic.magic,1 ); + +if (result <4 ) + goto out; + +strncpy (newsig, cur.pointer->swh.magic.magic,10 ); + newsig[10 ] =0 ; + + suspend_bio_ops.bdev_page_io (WRITE , resume_block_device, resume_firstblock, +virt_to_page (cur.pointer)); + +if (!nr_suspends) + printk(KERN_WARNING name_suspend "Image invalidated.\n"); +out: + suspend_bio_ops.finish_all_io (); +free_pages (cur.address,0 ); +return 0 ; +} + +/* +* workspace_size +* +* Description: +* Returns the number of bytes of RAM needed for this +* code to do its work. (Used when calculating whether +* we have enough memory to be able to suspend & resume). +* +*/ +static unsigned long swapwriter_memory_needed (void ) +{ +return 1 ; +} + +/* Print debug info +* +* Description: +*/ + +static int swapwriter_print_debug_stats (char * buffer,int size) +{ +int len =0 ; +struct sysinfo sysinfo; + +if (active_writer != &swapwriterops) { + len =suspend_snprintf (buffer, size,"- Swapwriter inactive.\n" ); +return len; + } + + len =suspend_snprintf (buffer, size,"- Swapwriter active.\n" ); +if (swapfilename[0 ]) + len+=suspend_snprintf (buffer+len, size-len, +" Attempting to automatically swapon: %s.\n" , swapfilename); + +si_swapinfo (&sysinfo); + + len+=suspend_snprintf (buffer+len, size-len," Swap available for image: %ld pages.\n" , + sysinfo.freeswap +swapwriter_storage_allocated ()); + +return len; +} + +/* +* Storage needed +* +* Returns amount of space in the swap header required +* for the swapwriter's data. This ignores the links between +* pages, which we factor in when allocating the space. +* +* We ensure the space is allocated, but actually save the +* data from write_header_init and therefore don't also define a +* save_config_info routine. +*/ +static unsigned long swapwriter_storage_needed (void ) +{ +return sizeof (header_data); +} + +/* +* Image_exists +* +*/ + +static int swapwriter_image_exists (void ) +{ +int signature_found; +union p_diskpage diskpage; + +if (!resume_device) { +printk ("Not even trying to read header " +"because resume_device is not set.\n" ); +return 0 ; + } + +if (!resume_block_device && !try_to_open_resume_device ()) +return 0 ; + + diskpage.address =get_zeroed_page (GFP_ATOMIC ); + + suspend_bio_ops.bdev_page_io (READ , resume_block_device, + resume_firstblock,virt_to_page (diskpage.ptr)); + suspend_bio_ops.finish_all_io (); + + signature_found =parse_signature (diskpage.pointer->swh.magic.magic,0 ); +free_pages (diskpage.address,0 ); + +if (signature_found <2 ) { +return 0 ;/* Normal swap space */ + }else if (signature_found ==-1 ) { + printk(KERN_ERR name_suspend +"Unable to find a signature. Could you have moved " + "a swap file?\n"); +return 0 ; + }else if (signature_found <6 ) { +if ((!(test_suspend_state (SUSPEND_NORESUME_SPECIFIED ))) + &&suspend_early_boot_message (1 , +SUSPEND_CONTINUE_REQ , +"Detected the signature of an alternate " +"implementation.\n" )) +set_suspend_state (SUSPEND_NORESUME_SPECIFIED ); +return 0 ; + }else if ((signature_found >>1 ) !=SIGNATURE_VER ) { +if ((!(test_suspend_state (SUSPEND_NORESUME_SPECIFIED ))) && +suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Found a different style suspend image signature." )) +set_suspend_state (SUSPEND_NORESUME_SPECIFIED ); + } + +return 1 ; +} + +/* +* Mark resume attempted. +* +* Record that we tried to resume from this image. +*/ + +static void swapwriter_mark_resume_attempted (void ) +{ +union p_diskpage diskpage; +int signature_found; + +if (!resume_device) { +printk ("Not even trying to record attempt at resuming" +" because resume_device is not set.\n" ); +return ; + } + + diskpage.address =get_zeroed_page (GFP_ATOMIC ); + +/* FIXME: Make sure bdev_page_io handles wrong parameters */ + suspend_bio_ops.bdev_page_io (READ , resume_block_device, resume_firstblock,virt_to_page (diskpage.ptr)); + signature_found =parse_signature (diskpage.pointer->swh.magic.magic,0 ); + +switch (signature_found) { +case 12 : +case 13 : + diskpage.pointer->swh.magic.magic[5 ] |=0x80 ; +break ; + } + + suspend_bio_ops.bdev_page_io (WRITE , resume_block_device, resume_firstblock, +virt_to_page (diskpage.ptr)); + suspend_bio_ops.finish_all_io (); +free_pages (diskpage.address,0 ); + +cleanup_opened_devices (1 ); +return ; +} + +/* +* Parse Image Location +* +* Attempt to parse a resume2= parameter. +* Swap Writer accepts: +* resume2=swap:DEVNAME[:FIRSTBLOCK][@BLOCKSIZE] +* +* Where: +* DEVNAME is convertable to a dev_t by name_to_dev_t +* FIRSTBLOCK is the location of the first block in the swap file +* (specifying for a swap partition is nonsensical but not prohibited). +* BLOCKSIZE is the logical blocksize >= 512 & <= PAGE_SIZE, +* mod 512 == 0 of the device. +* Data is validated by attempting to read a swap header from the +* location given. Failure will result in swapwriter refusing to +* save an image, and a reboot with correct parameters will be +* necessary. +*/ + +static int swapwriter_parse_image_location (char * commandline,int only_writer) +{ +char *thischar, *devstart =NULL , *colon =NULL , *at_symbol =NULL ; +union p_diskpage diskpage; +int signature_found, result = -EINVAL , temp_result; + +if (strncmp (commandline,"swap:" ,5 )) { +if (!only_writer) +return 1 ; + }else + commandline +=5 ; + + devstart = thischar = commandline; +while ((*thischar !=':' ) && (*thischar !='@' ) && + ((thischar - commandline) <250 ) && (*thischar)) + thischar++; + +if (*thischar ==':' ) { + colon = thischar; + *colon =0 ; + thischar++; + } + +while ((*thischar !='@' ) && ((thischar - commandline) <250 ) && (*thischar)) + thischar++; + +if (*thischar =='@' ) { + at_symbol = thischar; + *at_symbol =0 ; + } + +if (colon) + resume_firstblock = (int )simple_strtoul (colon +1 ,NULL ,0 ); +else + resume_firstblock =0 ; + +if (at_symbol) { + resume_firstblocksize = (int )simple_strtoul (at_symbol +1 ,NULL ,0 ); +if (resume_firstblocksize &0x1FF ) +printk ("Swapwriter: Blocksizes are usually a multiple of 512. Don't expect this to work!\n" ); + }else + resume_firstblocksize =4096 ; + + temp_result =try_to_parse_resume_device (devstart); + +if (colon) + *colon =':' ; +if (at_symbol) + *at_symbol ='@' ; + +if (temp_result) +return -EINVAL ; + + diskpage.address =get_zeroed_page (GFP_ATOMIC ); +if (!diskpage.address) { + printk(KERN_ERR name_suspend "Swapwriter: Failed to allocate a diskpage for I/O.\n"); +return -ENOMEM ; + } + +if ((suspend_bio_ops.get_block_size (resume_block_device) + != resume_firstblocksize) && + (suspend_bio_ops.set_block_size (resume_block_device, resume_firstblocksize) + == -EINVAL )) { + printk(KERN_ERR name_suspend "Swapwriter: Failed to set requested block size.\n"); + goto invalid; + } + + temp_result = suspend_bio_ops.bdev_page_io (READ , resume_block_device, resume_firstblock,virt_to_page (diskpage.ptr)); + suspend_bio_ops.finish_all_io (); + +if (temp_result) { + printk(KERN_ERR name_suspend "Swapwriter: Failed to submit I/O.\n"); + goto invalid; + } + + signature_found =parse_signature (diskpage.pointer->swh.magic.magic,0 ); + +if (signature_found !=-1 ) { + printk(KERN_ERR name_suspend "Swapwriter: Signature found.\n"); + result =0 ; + }else + printk(KERN_ERR name_suspend "Swapwriter: No swap signature found at specified location.\n"); +invalid: +free_page ((unsigned long ) diskpage.address); +return result; + +} + +static int header_locations_read_proc (char * page,char ** start,off_t off,int count, +int *eof,void *data) +{ +int i, printedpartitionsmessage =0 , len =0 , haveswap =0 , device_block_size; +struct inode *swapf =0 ; +int zone; +char * path_page = (char *)__get_free_page (GFP_KERNEL ); +char * path; +int path_len; + + *eof =1 ; +if (!page) +return 0 ; + +for (i =0 ; i <MAX_SWAPFILES ; i++) { +if (!swap_info[i].swap_file) +continue ; + +if (S_ISBLK (swap_info[i].swap_file->f_dentry->d_inode->i_mode)) { + haveswap =1 ; +if (!printedpartitionsmessage) { + len +=sprintf (page + len, +"For swap partitions, simply use the format: resume2=swap:/dev/hda1.\n" ); + printedpartitionsmessage =1 ; + } + }else { + path_len =0 ; + + path =get_path_for_swapfile (i, path_page); + path_len =sprintf (path_page,"%-31s " , path); + + haveswap =1 ; + swapf = swap_info[i].swap_file->f_dentry->d_inode; + device_block_size =block_size (swap_info[i].bdev); +if (!(zone =bmap (swapf,0 ))) { + len+=sprintf (page + len, +"Swapfile %-31s has been corrupted. Reuse mkswap on it and try again.\n" , + path_page); + }else { + len+=sprintf (page + len,"For swapfile `%s`, use resume2=swap:/dev/<partition name>:0x%x@%d.\n" , + path_page, + zone, device_block_size); + } + + } + } + +if (!haveswap) + len =sprintf (page,"You need to turn on swap partitions before examining this file.\n" ); + +free_pages ((unsigned long ) path_page,0 ); +return len; +} + +static struct suspend_proc_data swapwriter_proc_data[] = { + { + .filename ="swapfilename" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_STRING , + .data = { + .string = { + .variable = swapfilename, + .max_length =255 , + } + } + }, + + { + .filename ="headerlocations" , + .permissions =PROC_READONLY , + .type =SUSPEND_PROC_DATA_CUSTOM , + .data = { + .special = { + .read_proc = header_locations_read_proc, + } + } + }, + + { .filename ="disable_swapwriter" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_INTEGER , + .data = { + .integer = { + .variable = &swapwriterops.disabled, + .minimum =0 , + .maximum =1 , + } + }, + .write_proc = attempt_to_parse_resume_device, + } +}; + +static struct suspend_plugin_ops swapwriterops = { + .type =WRITER_PLUGIN , + .name ="Swap Writer" , + .module =THIS_MODULE , + .memory_needed = swapwriter_memory_needed, + .print_debug_info = swapwriter_print_debug_stats, + .storage_needed = swapwriter_storage_needed, + .initialise = swapwriter_initialise, + .cleanup = swapwriter_cleanup, + + .write_init = swapwriter_write_init, + .write_cleanup = swapwriter_write_cleanup, + .read_init = swapwriter_read_init, + .read_cleanup = swapwriter_read_cleanup, + + .ops = { + .writer = { + .write_chunk = swapwriter_write_chunk, + .read_chunk = swapwriter_read_chunk, + .noresume_reset = swapwriter_noresume_reset, + .storage_available = swapwriter_storage_available, + .storage_allocated = swapwriter_storage_allocated, + .release_storage = swapwriter_release_storage, + .allocate_header_space = swapwriter_allocate_header_space, + .allocate_storage = swapwriter_allocate_storage, + .image_exists = swapwriter_image_exists, + .mark_resume_attempted = swapwriter_mark_resume_attempted, + .write_header_init = swapwriter_write_header_init, + .write_header_chunk = swapwriter_write_header_chunk, + .write_header_cleanup = swapwriter_write_header_cleanup, + .read_header_init = swapwriter_read_header_init, + .read_header_chunk = swapwriter_read_header_chunk, + .read_header_cleanup = swapwriter_read_header_cleanup, + .serialise_extents = swapwriter_serialise_extents, + .load_extents = swapwriter_load_extents, + .invalidate_image = swapwriter_invalidate_image, + .parse_image_location = swapwriter_parse_image_location, + } + } +}; + +/* ---- Registration ---- */ +static __init int swapwriter_load (void ) +{ +int result; +int i, numfiles =sizeof (swapwriter_proc_data) /sizeof (struct suspend_proc_data ); + +printk ("Software Suspend Swap Writer loading.\n" ); +if (!(result =suspend_register_plugin (&swapwriterops))) { + +for (i=0 ; i< numfiles; i++) +suspend_register_procfile (&swapwriter_proc_data[i]); + }else +printk ("Software Suspend Swap Writer unable to register!\n" ); +return result; +} + +#ifdef MODULE +static __exit void swapwriter_unload(void) +{ +int i, numfiles =sizeof (swapwriter_proc_data) /sizeof (struct suspend_proc_data ); + +printk ("Software Suspend Swap Writer unloading.\n" ); + +for (i=0 ; i< numfiles; i++) +suspend_unregister_procfile (&swapwriter_proc_data[i]); +suspend_unregister_plugin (&swapwriterops); +} + +module_init (swapwriter_load); +module_exit (swapwriter_unload); +MODULE_LICENSE ("GPL" ); +MODULE_AUTHOR ("Nigel Cunningham" ); +MODULE_DESCRIPTION ("Suspend2 swap writer" ); +#else +late_initcall (swapwriter_load); +#endif
Re: [PATCH] [45/48] Suspend2 2.1.9.8 for 2.6.12: 621-swsusp-tidy.patch
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines · Nigel Cunningham
diff -ruNp
622-swapwriter.patch-old/kernel/power/suspend_swap.c622-swapwriter.patch-new/kernel/power/suspend_swap.ckernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +7
-0+/* + * --------------------------------------------------------------- + * + * Internal Data Structures + * + * --------------------------------------------------------------- + */
Can we please tone down this banner?
Quoted 14 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +14
-0+struct { + /* Extent chains for swap & blocks */ + struct extentchain swapextents; + struct extentchain block_chain[MAX_SWAPFILES]; + + /* Location of start of pagedir 1 */ + struct extent * pd1start_block_extent; + int pd1start_chain; + int pd1start_extent_number; + unsigned long pd1start_block_offset; + + /* Devices used for swap */ + dev_t swapdevs[MAX_SWAPFILES]; + char blocksizes[MAX_SWAPFILES];
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ +} header_data; + +static dev_t header_device = 0; +static struct block_device * header_block_device = NULL; +static int headerblocksize = PAGE_SIZE; +static int headerblock; + +/* For swapfile automatically swapon/off'd. */ +static char swapfilename[256] = "";
<linux/suspend.h> already has SWAP_FILENAME_MAXLENGTH. Use that.
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 11 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +11
-0+extern asmlinkage long sys_swapon(const char * specialfile, int swap_flags); +extern asmlinkage long sys_swapoff(const char * specialfile); +static int suspend_swapon_status = 0; + +/* + * --------------------------------------------------------------- + * + * Current state. + * + * --------------------------------------------------------------- + */
Could we please tone down this banner?
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 5 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +5
-0+ +/* Which pagedir are we saving/reloading? Needed so we can know whether to + * remember the last swap entry used at the end of writing pageset2, and + * get that location when saving or reloading pageset1.*/ +static int current_stream = 0;
No need to set to zero. The compiler will do it for you.
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 11 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +11
-0+ +/* Pointer to current swap entry being loaded/saved. */ +static struct extent * currentblockextent = NULL; +static unsigned long currentblockoffset = 0; +static int currentblockchain = 0; +static int currentblocksperpage = 0; + +/* Header Page Information */ +static int header_pages_allocated = 0; +static struct submit_params * first_header_submit_info = NULL, + * last_header_submit_info = NULL, * current_header_submit_info = NULL;
Ditto. (applies to NULL as well)
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 11 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +11
-0+static void get_header_params(struct submit_params * headerpage) +{ + swp_entry_t entry = headerpage->swap_address; + int swapfilenum = swp_type(entry); + unsigned long offset = swp_offset(entry); + struct swap_info_struct * sis = get_swap_info_struct(swapfilenum); + sector_t sector = map_swap_page(sis, offset); + + headerpage->dev = sis->bdev, + headerpage->block[0] = sector; + //headerpage->blocks_used = 1;
Please drop commented out code.
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 5 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +5
-0+static int try_to_open_resume_device(void) +{ + resume_block_device = open_by_devnum(resume_device, FMODE_READ); + + if (IS_ERR(resume_block_device) || (!resume_block_device))
The second set of parenthesis are redundant.
Quoted 8 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +8
-0+ /* + * Put bdev of suspend header in last byte of swap header + * (unsigned short) + */ + if (type > 11) { + dev_t * header_ptr = (dev_t *) &header[1]; + unsigned char * headerblocksize_ptr = + (unsigned char *) &header[5];
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ unsigned long * headerblock_ptr = (unsigned long *) &header[6]; + header_device = *header_ptr; + /* + * We are now using the highest bit of the char to indicate + * whether we have attempted to resume from this image before. + */ + clear_suspend_state(SUSPEND_RESUMED_BEFORE); + if (((int) *headerblocksize_ptr) & 0x80) + set_suspend_state(SUSPEND_RESUMED_BEFORE); + headerblocksize = 512 * (((int) *headerblocksize_ptr) & 0xf);
Hardcoded sector size?
Quoted 4 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +4
-0+ + /* Restore device info */ + for (i = 0; i < MAX_SWAPFILES; i++) { + dev_t thisdevice = header_data.swapdevs[i];
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ + swap_info[i].bdev = NULL; + + if (!thisdevice) + continue; + + if (thisdevice == resume_device) { + swap_info[i].bdev = resume_block_device; + /* Mark as used so the device doesn't get suspended. */ + swap_info[i].swap_file = (struct file *) 0xffffff;
Please use constants instead of magic numbers.
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 9 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +9
-0+static int swapwriter_write_init(int stream_number) +{ + current_stream = stream_number; + + if (current_stream == 1) { + currentblockextent = header_data.pd1start_block_extent; + currentblockoffset = header_data.pd1start_block_offset; + currentblockchain = header_data.pd1start_chain; + } else
See below.
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 9 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +9
-0+ for (currentblockchain = 0; currentblockchain < MAX_SWAPFILES; + currentblockchain++) + if (header_data.block_chain[currentblockchain].first) { + currentblockextent = + header_data. + block_chain[currentblockchain].first; + currentblockoffset = currentblockextent->minimum; + break; + }
Braces would make sense here.
Quoted 52 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +52
-0+ + currentblocksperpage = get_blocks_per_page(currentblockchain); + + suspend_bio_ops.reset_io_stats(); + + return 0; +} + +static int swapwriter_write_chunk(struct page * buffer_page) +{ + int i; + struct submit_params submit_params; + + if (TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED)) + return 0; + + if (currentblockchain == MAX_SWAPFILES) { + printk("Error! We have run out of blocks for writing data.\n"); + return -ENOSPC; + } + + if (!currentblockextent) { + do { + currentblockchain++; + } while ((currentblockchain < MAX_SWAPFILES) && + (!header_data.block_chain[currentblockchain].first)); + + /* We can validly not have a new blockextent. We + * might be compressing data and the user was + * too optimistic in setting the compression + * ratio or we're just copying the pageset. */ + + if (currentblockchain == MAX_SWAPFILES) { + printk("Argh. Ran out of block chains.\n"); + return -ENOSPC; + } + + currentblockextent = + header_data.block_chain[currentblockchain].first; + currentblockoffset = currentblockextent->minimum; + currentblocksperpage = get_blocks_per_page(currentblockchain); + } + + submit_params.readahead_index = -1; + submit_params.page = buffer_page; + submit_params.dev = swap_info[currentblockchain].bdev; + + /* Get the blocks */ + submit_params.block[0] = currentblockoffset; + for (i = 0; i < currentblocksperpage; i++) + GET_EXTENT_NEXT(currentblockextent, currentblockoffset); +
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ suspend_bio_ops.submit_io(WRITE, &submit_params, 0); + + check_shift_keys(0, NULL); + + return 0; +} + +static int swapwriter_write_cleanup(void) +{ + if (current_stream == 2) {
Where did that magic number two come from?
Quoted 7 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +7
-0+ header_data.pd1start_block_extent = currentblockextent; + header_data.pd1start_block_offset = currentblockoffset; + header_data.pd1start_chain = currentblockchain; + } + + suspend_bio_ops.finish_all_io(); +
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ suspend_bio_ops.check_io_stats(); + + return 0; +} + +static int swapwriter_read_init(int stream_number) +{ + current_stream = stream_number; + + if (current_stream == 1) {
Ditto.
Quoted 5 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +5
-0+static int swapwriter_parse_image_location(char * commandline, int only_writer) +{ + char *thischar, *devstart = NULL, *colon = NULL, *at_symbol = NULL; + union p_diskpage diskpage; + int signature_found, result = -EINVAL, temp_result;
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ + if (strncmp(commandline, "swap:", 5)) { + if (!only_writer) + return 1; + } else + commandline += 5; + + devstart = thischar = commandline; + while ((*thischar != ':') && (*thischar != '@') && + ((thischar - commandline) < 250) && (*thischar))
What's the magic 250 here?
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 9 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +9
-0+ thischar++; + + if (*thischar == ':') { + colon = thischar; + *colon = 0; + thischar++; + } + + while ((*thischar != '@') && ((thischar - commandline) < 250) && (*thischar))
And here?
Quoted 6 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +6
-0+ thischar++; + + if (*thischar == '@') { + at_symbol = thischar; + *at_symbol = 0; + }
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000
Quoted 10 lines
kernel/power/suspend_swap.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_swap.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ + if (colon) + resume_firstblock = (int) simple_strtoul(colon + 1, NULL, 0); + else + resume_firstblock = 0; + + if (at_symbol) { + resume_firstblocksize = (int) simple_strtoul(at_symbol + 1, NULL, 0); + if (resume_firstblocksize & 0x1FF) + printk("Swapwriter: Blocksizes are usually a multiple of 512. Don't expect this to work!\n");
Then why do we allow it?
PATCH 46/48 Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
In reply to Nigel Cunninghamkernel/power/block_io.h +55 -0
@@ -0,0 +1,55 @@ +/* +* block_io.h +* +* Copyright 2004-2005 Nigel Cunningham <[email protected]> +* +* Distributed under GPLv2. +* +* This file contains declarations for functions exported from +* block_io.c, which contains low level io functions. +*/ + +/* +* The maximum amount of I/O we submit at once. +*/ +#define MAX_READAHEAD 1024 + +/* Forward Declarations */ + +/* +* submit_params +* +* The structure we use for tracking submitted I/O. +*/ +struct submit_params { +swp_entry_t swap_address; +struct page * page; +struct block_device * dev; +long block[8 ]; +int readahead_index; +struct submit_params * next; +}; + + +/* +* Our exported interface so the swapwriter and filewriter don't +* need these functions duplicated. +*/ +struct suspend_bio_ops { +int (*set_block_size) (struct block_device * bdev,int size); +int (*get_block_size) (struct block_device * bdev); +int (*submit_io) (int rw, +struct submit_params * submit_info,int syncio); +int (*bdev_page_io) (int rw,struct block_device * bdev,long pos, +struct page * page); +void (*wait_on_readahead) (int readahead_index); +void (*check_io_stats) (void ); +void (*reset_io_stats) (void ); +void (*finish_all_io) (void ); +int (*prepare_readahead) (int index); +void (*cleanup_readahead) (int index); +struct page ** readahead_pages; +int (*readahead_ready) (int readahead_index); +}; + +extern struct suspend_bio_ops suspend_bio_ops;
kernel/power/suspend_block_io.c +817 -0
817 lines not on this page · raw message
Re: [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 5
Quoted 9 lines · Nigel Cunningham
diff -ruNp
623-generic-block-io.patch-old/kernel/power/suspend_block_io.c623-generic-block-io.patch-new/kernel/power/suspend_block_io.ckernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +5
-0@@ -0,0 +1,817 @@ +#include "suspend2_core/suspend.h" +#include "suspend2_core/proc.h" +#include "suspend2_core/plugins.h" +#include "suspend2_core/utility.h" +#include "suspend2_core/prepare_image.h"
You've got circular dependencies. kernel/power/suspend2_core depends on kernel/power and vice versa. Please just consider putting them all in kernel/power/
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 13
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +8
-0+/* Bits in struct io_info->flags */ +#define IO_WRITING 1 +#define IO_RESTORE_PAGE_PROT 2 +#define IO_AWAITING_READ 3 +#define IO_AWAITING_WRITE 4 +#define IO_AWAITING_SUBMIT 5 +#define IO_AWAITING_CLEANUP 6 +#define IO_HANDLE_PAGE_PROT 7
Please use enums instead.
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 23
Quoted 10 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ +#define MAX_OUTSTANDING_IO 1024 + +/* + * --------------------------------------------------------------- + * + * IO in progress information storage and helpers + * + * --------------------------------------------------------------- + */
Could we please drop the above banner. Hurts my eyes...
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 27
Quoted 4 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +4
-0+ +struct io_info { + struct bio * sys_struct; + long block[PAGE_SIZE/512];
You're hardcoding sector size here, no?
Quoted 5 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +5
-0+ struct page * buffer_page; + struct page * data_page; + unsigned long flags; + struct block_device * dev; + struct list_head list;
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 42
Quoted 10 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ int readahead_index; + struct work_struct work; +}; + +/* Locks separated to allow better SMP support. + * An io_struct moves through the lists as follows. + * free -> submit_batch -> busy -> ready_for_cleanup -> free + */ +static LIST_HEAD(ioinfo_free); +static spinlock_t ioinfo_free_lock = SPIN_LOCK_UNLOCKED;
Please use DEFINE_SPINLOCK instead. It is preferred as some automatic lock checkers need it.
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 44
Quoted 2 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +2
-0+#define BITS_PER_UL (8 * sizeof(unsigned long)) +static volatile unsigned long suspend_readahead_flags[(MAX_READAHEAD + BITS_PER_UL - 1) / BITS_PER_UL];
<asm/types.h> has BITS_PER_LONG. Use it.
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 47
Quoted 3 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +3
-0+static int suspend_end_bio(struct bio * bio, unsigned int num, int err) +{ + struct io_info *io_info = (struct io_info *) bio->bi_private;
Redundant cast.
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 51
Quoted 4 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +4
-0+static void suspend_wait_on_readahead(int readahead_index) +{ + int index = readahead_index/(8 * sizeof(unsigned long)); + int bit = readahead_index - index * 8 * sizeof(unsigned long);
Use BITS_PER_LONG here.
Quoted 6 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +6
-0+ + /* read_ahead_index is the one we want to return */ + while (!test_bit(bit, &suspend_readahead_flags[index])) + do_bio_wait(6); +} +
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 67
Quoted 10 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+/* + * readahead_done + * + * Returns whether the readahead requested is ready. + */ + +static int suspend_readahead_ready(int readahead_index) +{ + int index = readahead_index/(8 * sizeof(unsigned long)); + int bit = readahead_index - (index * 8 * sizeof(unsigned long));
Ditto.
Quoted 22 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +22
-0+ + return test_bit(bit, &suspend_readahead_flags[index]); +} + +/* suspend_readahead_prepare + * Set up for doing readahead on an image */ +static int suspend_prepare_readahead(int index) +{ + unsigned long new_page = get_zeroed_page(GFP_ATOMIC); + + if(!new_page) + return -ENOMEM; + + suspend_bio_ops.readahead_pages[index] = virt_to_page(new_page); + return 0; +} + +/* suspend_readahead_cleanup + * Clean up structures used for readahead */ +static void suspend_cleanup_readahead(int page) +{ + __free_pages(suspend_bio_ops.readahead_pages[page], 0);
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 99
Quoted 10 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ suspend_bio_ops.readahead_pages[page] = 0; + return; +} + +static unsigned long suspend_bio_memory_needed(void) +{ + /* We want to have at least enough memory so as to have 128 I/O + * transactions on the fly at once. If we can to more, fine. */ + return (128 * (PAGE_SIZE + sizeof(struct request) + + sizeof(struct bio) + sizeof(struct io_info)));
Where did the magic 128 come from?
Re: [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
Hi.
Quoted 14 lines · Pekka Enberg
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 5
Quoted 9 lines · Nigel Cunningham
diff -ruNp
623-generic-block-io.patch-old/kernel/power/suspend_block_io.c623-generic-block-io.patch-new/kernel/power/suspend_block_io.ckernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +5
-0@@ -0,0 +1,817 @@ +#include "suspend2_core/suspend.h" +#include "suspend2_core/proc.h" +#include "suspend2_core/plugins.h" +#include "suspend2_core/utility.h" +#include "suspend2_core/prepare_image.h"You've got circular dependencies.
kernel/power/suspend2_coredepends on kernel/power and vice versa. Please just consider putting them all inkernel/power/
Done. They were separate just to keep things tidy.
Quoted 10 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 13
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +8
-0+/* Bits in struct io_info->flags */ +#define IO_WRITING 1 +#define IO_RESTORE_PAGE_PROT 2 +#define IO_AWAITING_READ 3 +#define IO_AWAITING_WRITE 4 +#define IO_AWAITING_SUBMIT 5 +#define IO_AWAITING_CLEANUP 6 +#define IO_HANDLE_PAGE_PROT 7Please use enums instead.
Done!
Quoted 4 lines
Quoted 4 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +4
-0+ +#define MAX_OUTSTANDING_IO 1024 + +/*
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 23
Quoted 6 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +6
-0+ * --------------------------------------------------------------- + * + * IO in progress information storage and helpers + * + * --------------------------------------------------------------- + */Could we please drop the above banner. Hurts my eyes...
Sorry! :>
Quoted 6 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 27
Quoted 4 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +4
-0+ +struct io_info { + struct bio * sys_struct; + long block[PAGE_SIZE/512];You're hardcoding sector size here, no?
As others do.
Quoted 10 lines
Quoted 10 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ struct page * buffer_page; + struct page * data_page; + unsigned long flags; + struct block_device * dev; + struct list_head list; + int readahead_index; + struct work_struct work; +}; + +/* Locks separated to allow better SMP support.
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 42
Quoted 5 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +5
-0+ * An io_struct moves through the lists as follows. + * free -> submit_batch -> busy -> ready_for_cleanup -> free + */ +static LIST_HEAD(ioinfo_free); +static spinlock_t ioinfo_free_lock = SPIN_LOCK_UNLOCKED;Please use
DEFINE_SPINLOCKinstead. It is preferred as some automatic lock checkers need it.
Done.
Quoted 4 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 44
Quoted 2 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +2
-0+#define BITS_PER_UL (8 * sizeof(unsigned long)) +static volatile unsigned long suspend_readahead_flags[(MAX_READAHEAD + BITS_PER_UL - 1) / BITS_PER_UL];<
asm/types.h> hasBITS_PER_LONG. Use it.
Thanks for educating the ignorant :>
Quoted 5 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 47
Quoted 3 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +3
-0+static int suspend_end_bio(struct bio * bio, unsigned int num, int err) +{ + struct io_info *io_info = (struct io_info *) bio->bi_private;Redundant cast.
k.
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 53
Quoted 6 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +6
-0+static unsigned long suspend_bio_memory_needed(void) +{ + /* We want to have at least enough memory so as to have 128 I/O + * transactions on the fly at once. If we can to more, fine. */ + return (128 * (PAGE_SIZE + sizeof(struct request) + + sizeof(struct bio) + sizeof(struct io_info)));Where did the magic 128 come from?
My bad. Should be MAX_OUTSTANDING_IO.
Thanks, once again!
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
Hi Nigel,
Quoted 1 line · Pekka Enberg
Quoted 1 line
You're hardcoding sector size here, no?
Quoted 1 line · Nigel Cunningham
As others do.
Even so, please use a constant.
Pekka
Re: Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
Hi.
Quoted 9 lines · Pekka J Enberg
Hi Nigel,
Quoted 1 line · Pekka Enberg
Quoted 1 line
You're hardcoding sector size here, no?
Quoted 1 line · Nigel Cunningham
As others do.
Even so, please use a constant.
Done
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
In reply to Nigel CunninghamHi Nigel,
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 3
Quoted 7 lines
diff -ruNp
623-generic-block-io.patch-old/kernel/power/suspend_block_io.c623-generic-block-io.patch-new/kernel/power/suspend_block_io.ckernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +3
-0@@ -0,0 +1,817 @@ +struct io_info { + struct bio * sys_struct; + long block[PAGE_SIZE/512];
Aah, but for this you should use block_size(io_info->dev) instead. No need to mess with sector sizes. Why is this long by the way? PAGE_SIZE/512 is block size in bytes.
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 11
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +8
-0+ struct page * buffer_page; + struct page * data_page; + unsigned long flags; + struct block_device * dev; + struct list_head list; + int readahead_index; + struct work_struct work; +};
Pekka
Re: [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
Hi.
Quoted 13 lines · Pekka Enberg
Hi Nigel,
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 3
Quoted 7 lines
diff -ruNp
623-generic-block-io.patch-old/kernel/power/suspend_block_io.c623-generic-block-io.patch-new/kernel/power/suspend_block_io.ckernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +3
-0@@ -0,0 +1,817 @@ +struct io_info { + struct bio * sys_struct; + long block[PAGE_SIZE/512];Aah, but for this you should use
block_size(io_info->dev)instead. No need to mess with sector sizes. Why is this long by the way? PAGE_SIZE/512 is block size in bytes.
No... it's the maximum number of blocks per page. Depending upon how the user has set the blocksize when they created the filesystem (in the case of filesystems), the number of blocks we use per page might be 1, 2, 4 or 8.
It's long because a sector number is stored in it.
Regards,
Nigel
Quoted 11 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 line 11
Quoted 8 lines
kernel/power/suspend_block_io.c 2005-07-05 23:48:59.000000000 +1000 renamed from kernel/power/suspend_block_io.c 1970-01-01 10:00:00.000000000 +1000 +8
-0+ struct page * buffer_page; + struct page * data_page; + unsigned long flags; + struct block_device * dev; + struct list_head list; + int readahead_index; + struct work_struct work; +};Pekka
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
On Fri, 8 Jul 2005 07:16 am, Nigel Cunningham wrote:
Hi,
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
kernel/power/suspend_block_io.c +3
-0+struct io_info { +struct bio * sys_struct; +long block[PAGE_SIZE /512 ];
Quoted 9 lines
Quoted 3 lines
Aah, but for this you should use
block_size(io_info->dev)instead. No need to mess with sector sizes. Why is this long by the way? PAGE_SIZE/512 is block size in bytes.No... it's the maximum number of blocks per page. Depending upon how the user has set the blocksize when they created the filesystem (in the case of filesystems), the number of blocks we use per page might be 1, 2, 4 or 8.
MAX_BUF_PER_PAGE
Quoted 2 lines
It's long because a sector number is stored in it.
sector_t?
Nick
Send instant messages to your online friends http://au.messenger.yahoo.com
Re: [PATCH] [46/48] Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
Hi.
Quoted 18 lines · On Fri, 2005-07-08 at 07:26, [email protected] wrote:
On Fri, 8 Jul 2005 07:16 am, Nigel Cunningham wrote:
Hi,
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
kernel/power/suspend_block_io.c +3
-0+struct io_info { +struct bio * sys_struct; +long block[PAGE_SIZE /512 ];Quoted 9 lines
Quoted 3 lines
Aah, but for this you should use
block_size(io_info->dev)instead. No need to mess with sector sizes. Why is this long by the way? PAGE_SIZE/512 is block size in bytes.No... it's the maximum number of blocks per page. Depending upon how the user has set the blocksize when they created the filesystem (in the case of filesystems), the number of blocks we use per page might be 1, 2, 4 or 8.
Quoted 6 lines
MAX_BUF_PER_PAGEQuoted 2 lines
It's long because a sector number is stored in it.
sector_t?
Fixed. Ta.
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
In reply to Nigel CunninghamQuoted 4 lines · Nigel Cunningham
No... it's the maximum number of blocks per page. Depending upon how the user has set the blocksize when they created the filesystem (in the case of filesystems), the number of blocks we use per page might be 1, 2, 4 or 8.
Yes. Sorry about that. I don't know what I was smoking. (But it must have been the good stuff (-:.)
Pekka
Re: Suspend2 2.1.9.8 for 2.6.12: 622-swapwriter.patch
In reply to Nigel CunninghamQuoted 4 lines · Pekka Enberg
Quoted 4 lines
Quoted 4 lines
kernel/power/suspend_block_io.c +3
-0@@ -0,0 +1,817 @@ +struct io_info { +struct bio * sys_struct; +long block[PAGE_SIZE /512 ];
Quoted 6 lines · Nigel Cunningham
No... it's the maximum number of blocks per page. Depending upon how the user has set the blocksize when they created the filesystem (in the case of filesystems), the number of blocks we use per page might be 1, 2, 4 or 8.
It's long because a sector number is stored in it.
The field block wants a better name. block_sectors, perhaps?
Pekka
PATCH 47/48 Suspend2 2.1.9.8 for 2.6.12: 623-generic-block-io.patch
In reply to Nigel Cunninghamkernel/power/suspend_file.c +1616 -0
@@ -0,0 +1,1616 @@ +/* +* Filewriter.c +* +* Copyright 2005 Nigel Cunningham <[email protected]> +* +* Distributed under GPLv2. +* +* This file encapsulates functions for usage of a simple file as a +* backing store. It is based upon the swapwriter, and shares the +* same basic working. Here, though, we have nothing to do with +* swapspace, and only one device to worry about. +* +* The user can just +* +* echo Suspend2 > /path/to/my_file +* +* and +* +* echo /path/to/my_file > /proc/software_suspend/filewriter_target +* +* then put what they find in /proc/software_suspend/resume2 +* as their resume2= parameter in lilo.conf (and rerun lilo if using it). +* +* Having done this, they're ready to suspend and resume. +* +* TODO: +* - File resizing. +*/ + +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/blkdev.h> +#include <linux/file.h> +#include <linux/stat.h> +#include <linux/mount.h> +#include <linux/statfs.h> + +#include "suspend2_core/suspend.h" +#include "suspend2_core/suspend2_common.h" +#include "suspend2_core/version.h" +#include "suspend2_core/proc.h" +#include "suspend2_core/plugins.h" +#include "suspend2_core/ui.h" +#include "suspend2_core/extent.h" +#include "suspend2_core/utility.h" +#include "suspend2_core/io.h" + +#include "block_io.h" + +/* +* General Declarations. +*/ + +static struct suspend_proc_data filewriter_proc_data[]; +static struct suspend_plugin_ops filewriterops; + +/* +* External Declarations +*/ + +extern asmlinkage long sys_open (const char __user * filename,int flags,int mode); +extern asmlinkage long sys_close (unsigned int fd); + +/* +* Forward Declarations +*/ + +static int filewriter_invalidate_image (void ); +static int filewriter_storage_available (void ); + +/* +* Details of our target. +*/ + +char filewriter_target[256 ]; +static struct inode * target_inode; +static int target_fd =-1 ; +static struct block_device * target_bdev; +static int used_devt =0 ; +static dev_t target_dev_t =0 ; +static int target_firstblock =0 ; +static int target_blocksize =PAGE_SIZE ; +static int target_storage_available =0 ; +static unsigned int target_blkbits; +#define target_blockshift (PAGE_SHIFT - target_blkbits) +#define target_blocksperpage (1 << target_blockshift) + +static int target_type =-1 ; + +/* +static char * description[7] = { +"Socket", +"Link", +"Regular file", +"Block device", +"Directory", +"Character device", +"Fifo", +}; +*/ + +static char HaveImage[] ="HaveImage\n" ; +static char NoImage[] ="Suspend2\n" ; +static const int resumed_before_byte =sizeof (HaveImage) +1 ; +#define sig_size resumed_before_byte + +/* Header_pages must be big enough for signature */ +static int header_pages, main_pages; + +static unsigned long * header_link =NULL ; +#define BYTES_PER_HEADER_PAGE (PAGE_SIZE - sizeof(sector_t)) + +#define target_is_normal_file () (S_ISREG(target_inode->i_mode)) + +/* +* Readahead Variables +*/ + +// Higher Level +static int readahead_index =0 , readahead_submit_index =0 ; +static int readahead_allocs =0 , readahead_frees =0 ; + +static char * filewriter_buffer =NULL ; +static int filewriter_buffer_posn =0 ; +static int filewriter_page_index =0 ; + +/* +* --------------------------------------------------------------- +* +* Internal Data Structures +* +* --------------------------------------------------------------- +*/ + +/* header_data contains data that is needed to reload pagedir1, and +* is therefore saved in the suspend header. +* +* Pagedir2 data gets stored before pagedir1 (save order), and the first +* page for pagedir1 to use is set when pagedir2 is written (when we know how +* much storage it used). Since this first entry is almost certainly not at the +* start of a extent, the firstoffset variable below tells us where to start in +* the extent. All of this means we don't have to worry about getting different +* compression ratios for the kernel and cache (when compressing the image). +* We can simply allocate one pool of storage (size determined using expected +* compression ratio) and use it without worrying whether one pageset +* compresses better and the other worse (this is what happens). As long as the +* user gets the expected compression right, it will work. +*/ + +static struct { +/* Location of start of pagedir 1 */ +struct extent * pd1start_block_extent; +int pd1start_extent_number; +unsigned long pd1start_block_offset; + +} filewriter_header_data; + +/* Extent chain for blocks */ +static struct extentchain block_chain; + +/* +* --------------------------------------------------------------- +* +* Current state. +* +* --------------------------------------------------------------- +*/ + +/* Which pagedir are we saving/reloading? Needed so we can know whether to +* remember the last block used at the end of writing pageset2, and +* get that location when saving or reloading pageset1.*/ +static int current_stream =0 ; + +/* Pointer to current entry being loaded/saved. */ +static struct extent * currentblockextent =NULL ; +static unsigned long currentblockoffset =0 ; + +/* Header Page Information */ +static struct submit_params * first_header_submit_info =NULL , + * last_header_submit_info =NULL , * current_header_submit_info =NULL ; + +/* +* Helpers. +*/ + +/* +* Return the type of target we have, an index into the descriptions +* above. +*/ +static int get_target_type (struct inode * inode) +{ +switch (inode->i_mode &S_IFMT ) { +case S_IFSOCK : + target_type =0 ; +break ; +case S_IFLNK : + target_type =1 ; +break ; +case S_IFREG : + target_type =2 ; +break ; +case S_IFBLK : + target_type =3 ; +break ; +case S_IFDIR : + target_type =4 ; +break ; +case S_IFCHR : + target_type =5 ; +break ; +case S_IFIFO : + target_type =6 ; +break ; + } +return target_type; +} + +#define target_is_usable (!(target_type == 1 || target_type == 4)) +#define target_num_sectors (target_inode->i_size >> target_blkbits) + +static int size_ignoring_sparseness (void ) +{ +int mappable =0 , i; + +if (target_is_normal_file ()) { +int extent_min =-1 , extent_max =-1 ; + +for (i =0 ; i <= target_num_sectors; i++) { +sector_t new_sector =bmap (target_inode, i); +if (!new_sector) { +if (i == extent_max +1 ) + extent_max++; +else + extent_min = extent_max = i; + }else + mappable++; + } + +return mappable >> (PAGE_SHIFT - target_blkbits); + }else +return filewriter_storage_available (); +} + +static void get_main_pool_phys_params (void ) +{ +int i; + +if (block_chain.first) +put_extent_chain (&block_chain); + +if (target_is_normal_file ()) { +int header_sectors = (header_pages << target_blockshift); +int extent_min =-1 , extent_max =-1 , real_sector =0 ; + + +for (i =0 ; i <= target_num_sectors; i++) { +sector_t new_sector = +bmap (target_inode, header_sectors + i); + +/* +* I'd love to be able to fill in holes and resize +* files, but not yet... +*/ + +if (!new_sector) +continue ; + + real_sector++; + +if (real_sector < header_sectors) +continue ; + +if (new_sector == extent_max +1 ) + extent_max++; +else { +if (extent_min >-1 ) +append_extent_to_extent_chain ( + &block_chain, + extent_min, extent_max); + extent_min = extent_max = new_sector; + } + } +if (extent_min >-1 ) +append_extent_to_extent_chain (&block_chain, + extent_min, extent_max); + }else +if (target_storage_available >0 ) { +unsigned long new_start = + last_header_submit_info ? + last_header_submit_info->block[target_blocksperpage -1 ] + +1 :0 ; + +append_extent_to_extent_chain (&block_chain, + new_start, new_start + + (min (main_pages, target_storage_available) << + target_blockshift) -1 ); + } +} + +static void get_target_info (void ) +{ +if (target_bdev) { +/* +* Don't replace the inode if we got the bdev from opening +* a file. +*/ +if (!target_inode) + target_inode = target_bdev->bd_inode; + target_type =get_target_type (target_inode); + target_blkbits = target_bdev->bd_inode->i_blkbits; + target_storage_available =size_ignoring_sparseness (); + }else { + target_type =-1 ; + target_inode =NULL ; + target_blkbits =0 ; + target_storage_available =0 ; + } +} + +static int set_target_blocksize (void ) +{ +if ((suspend_bio_ops.get_block_size (target_bdev) + != target_blocksize) && + (suspend_bio_ops.set_block_size (target_bdev, target_blocksize) + == -EINVAL )) { + printk(KERN_ERR name_suspend "Filewriter: Failed to set the blocksize.\n"); +return 1 ; + } + +return 0 ; + +} + +static int try_to_open_target_device (void ) +{ +if (!target_dev_t) +return 1 ; + +if (!target_bdev) { + target_bdev =open_by_devnum (target_dev_t,FMODE_READ ); + +if (IS_ERR (target_bdev)) { + target_bdev =NULL ; +return 1 ; + } + used_devt =1 ; + +if (set_target_blocksize ()) { +blkdev_put (target_bdev); + target_bdev =NULL ; +return 1 ; + } + } + +get_target_info (); + +return 0 ; +} + +static int try_to_parse_target_dev_t (char * commandline) +{ +struct kstat stat; +int error; + + target_dev_t =name_to_dev_t (commandline); + +if (!target_dev_t) { + error =vfs_stat (commandline, &stat); +if (!error) + target_dev_t = stat.rdev; + } + +if (!target_dev_t) { +if (test_suspend_state (SUSPEND_TRYING_TO_RESUME )) +suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Failed to translate \"%s\" into a device id.\n" , + commandline); +else +printk (name_suspend"Can't translate \"%s\" into a device id yet.\n" , + commandline); +return 1 ; + } + +try_to_open_target_device (); + +if (IS_ERR (target_bdev)) { +printk ("Open by devnum returned %p given %x.\n" , + target_bdev, target_dev_t); + target_bdev =NULL ; +if (test_suspend_state (SUSPEND_BOOT_TIME )) +suspend_early_boot_message (1 ,SUSPEND_CONTINUE_REQ , +"Failed to get access to the device on which" +" Software Suspend's header should be found." ); +else +printk ("Failed to get access to the device on which " +"Software Suspend's header should be found.\n" ); +return 1 ; + } + +return 0 ; +} + +static void filewriter_noresume_reset (void ) +{ +/* +* If we have read part of the image, we might have filled header_data with +* data that should be zeroed out. +*/ + +memset ((char *) &filewriter_header_data,0 ,sizeof (filewriter_header_data)); +} + +/* +* +*/ + +int parse_signature (char * header,int restore) +{ +int have_image = !memcmp (HaveImage, header,sizeof (HaveImage) -1 ); +int non_image_header = !memcmp (NoImage, header,sizeof (NoImage) -1 ); + +if (!have_image && !non_image_header) +return -1 ; + +if (non_image_header) +return 0 ; + +clear_suspend_state (SUSPEND_RESUMED_BEFORE ); + +if (header[resumed_before_byte] &1 ) +set_suspend_state (SUSPEND_RESUMED_BEFORE ); + +/* Invalidate Image */ +if (restore) +strcpy (header, NoImage); + +return 1 ; +} + +/* +* prepare_signature +*/ + +static int prepare_signature (struct submit_params * header_page_info, +char * current_header) +{ +/* +* Explicitly put the \0 that clears the 'tried to resume from +* this image before' flag. +*/ +strncpy (current_header, HaveImage,sizeof (HaveImage)); + current_header[resumed_before_byte] =0 ; +return 0 ; +} + +static void free_header_data (void ) +{ +if (!first_header_submit_info) +return ; + +while (first_header_submit_info) { +struct submit_params * next = first_header_submit_info->next; +kfree (first_header_submit_info); + first_header_submit_info = next; + } + +suspend_message (SUSPEND_WRITER ,SUSPEND_LOW ,1 , +" Freed swap pages in free_header_data.\n" ); + first_header_submit_info = last_header_submit_info =NULL ; +return ; +} + +static int filewriter_storage_available (void ) +{ +int result =0 ; + +if (!target_inode) +return 0 ; + +switch (target_type) { +case 0 : +case 5 : +case 6 :/* Socket, Char, Fifi */ +return -1 ; +case 2 :/* Regular file: current size - holes + free space on part */ + result = target_storage_available; +break ; +case 3 :/* Block device */ +if (target_bdev->bd_disk) { +if (target_bdev->bd_part) + result = (unsigned long )target_bdev->bd_part->nr_sects >> (PAGE_SHIFT -9 ); +else + result = (unsigned long )target_bdev->bd_disk->capacity >> (PAGE_SHIFT -9 ); + }else { +printk ("bdev->bd_disk null.\n" ); +return 0 ; + } + } + +return result; +} + +static int filewriter_storage_allocated (void ) +{ +int result; + +if (!target_inode) +return 0 ; + +if (target_is_normal_file ()) { + result = (int ) target_storage_available; + }else + result = header_pages + main_pages; + +return result; +} + +static int filewriter_initialise (int starting_cycle) +{ +if (!starting_cycle) +return 0 ; + + target_fd =sys_open (filewriter_target,O_RDWR ,0 ); + +if (target_fd <0 ) { +printk ("Open file %s returned %d.\n" , filewriter_target, target_fd); +return target_fd; + } + + target_inode = current->files->fd[target_fd]->f_dentry->d_inode; +BUG_ON (target_bdev); + target_bdev = target_inode->i_bdev ? target_inode->i_bdev : target_inode->i_sb->s_bdev; +set_target_blocksize (); +get_target_info (); + +return 0 ; +} + +static void filewriter_cleanup (int finishing_cycle) +{ +if (target_bdev) { +if (used_devt) { +blkdev_put (target_bdev); + used_devt =0 ; + } + target_bdev =NULL ; +get_target_info (); + } + +if (!finishing_cycle) +return ; + +if (target_fd >=0 ) +sys_close (target_fd); + + target_fd =-1 ; +} + +static int filewriter_release_storage (void ) +{ +if ((TEST_ACTION_STATE (SUSPEND_KEEP_IMAGE )) &&test_suspend_state (SUSPEND_NOW_RESUMING )) +return 0 ; + +/* Free metadata */ +free_header_data (); + +put_extent_chain (&block_chain); + + header_pages = main_pages =0 ; +return 0 ; +} + +static int filewriter_allocate_header_space (int space_requested) +{ +int i, j, pages_to_get; +int ret =0 ; + +/* We only steal pages from the main pool. If it doesn't have any yet... */ + +if (!block_chain.first) +return 0 ; + + pages_to_get = space_requested - header_pages; + +if (pages_to_get <1 ) +return 0 ; + +for (i= header_pages; i < space_requested; i++) { +struct submit_params * new_submit_param; + +/* Get a submit structure */ + new_submit_param =kmalloc (sizeof (struct submit_params ),GFP_ATOMIC ); + +if (!new_submit_param) { +printk ("Failed to kmalloc a struct submit param.\n" ); + ret = -ENOMEM ; + goto out; + } + +memset (new_submit_param,0 ,sizeof (struct submit_params )); + +if (last_header_submit_info) { + last_header_submit_info->next = new_submit_param; + last_header_submit_info = new_submit_param; + }else + last_header_submit_info = first_header_submit_info = + new_submit_param; + +for (j =0 ; j < target_blocksperpage; j++) { +unsigned long newvalue; + +/* +* Steal one from main extent chain. If, as a result, +* it is too small, more storage will be allocated or +* memory eaten. +*/ + +if (block_chain.first->minimum < + block_chain.first->maximum) { + newvalue = block_chain.first->minimum; + block_chain.first->minimum++; + }else { +struct extent * oldfirst = + block_chain.first; + block_chain.first = oldfirst->next; + block_chain.frees++; +if (block_chain.last == oldfirst) + block_chain.last =NULL ; + newvalue = oldfirst->minimum; +put_extent (oldfirst); + } + + block_chain.size--; + + new_submit_param->block[j] = newvalue; + } + + new_submit_param->dev = target_bdev; + new_submit_param->readahead_index =-1 ; + + header_pages++; + +suspend_message (SUSPEND_WRITER ,SUSPEND_MEDIUM ,0 , +" Got header page %d/%d. Dev is %x. Block is %lu. " +"Target block size is %d.\n" , + i, space_requested, + new_submit_param->dev, + new_submit_param->block[0 ], + new_submit_param->dev->bd_block_size); + +if (!block_chain.size) +break ; + } +out: +return ret; +} + +static int filewriter_allocate_storage (int space_requested) +{ +int result =0 ; +int blocks_to_get = (space_requested << target_blockshift) - block_chain.size; + +/* Only release_storage reduces the size */ +if (blocks_to_get <1 ) +return 0 ; + + main_pages = space_requested; + +get_main_pool_phys_params (); + +suspend_message (SUSPEND_WRITER ,SUSPEND_MEDIUM ,0 , +"Finished with block_chain.size == %d.\n" , + block_chain.size); + +if (block_chain.size < ((header_pages + main_pages) << target_blockshift)) + result = -ENOSPC ; + +return result; +} + +static int filewriter_write_header_chunk (char * buffer,int buffer_size); +static int filewriter_write_header_init (void ) +{ +char new_sig[sig_size]; +struct extent * extent; + + filewriter_buffer = (char *)get_zeroed_page (GFP_ATOMIC ); + header_link = + (unsigned long *) (filewriter_buffer +BYTES_PER_HEADER_PAGE ); + filewriter_page_index =1 ; + filewriter_buffer_posn =0 ; + + current_header_submit_info = first_header_submit_info; + +/* We change it once the whole header is written */ +strcpy (new_sig, NoImage); +filewriter_write_header_chunk (new_sig, sig_size); + +/* Must calculate extent number before writing the header! */ + filewriter_header_data.pd1start_extent_number =1 ; + extent = block_chain.first; + +while (extent != filewriter_header_data.pd1start_block_extent) { + filewriter_header_data.pd1start_extent_number++; + extent = extent->next; + } + +/* Info needed to bootstrap goes at the start of the header. +* First we save the 'header_data' struct, including the number +* of header pages. Then we save the structs containing data needed +* for reading the header pages back. +* Note that even if header pages take more than one page, when we +* read back the info, we will have restored the location of the +* next header page by the time we go to use it. +*/ +filewriter_write_header_chunk ((char *) &filewriter_header_data, +sizeof (filewriter_header_data)); + +return 0 ; +} + +static int filewriter_write_header_chunk (char * buffer,int buffer_size) +{ +int bytes_left = buffer_size; + +/* +* We buffer the writes until a page is full and to use the last +* sizeof(swp_entry_t) bytes for links between pages. This is +* totally transparent to the caller. +* +* Note also that buffer_size can be > PAGE_SIZE. +*/ + +suspend_message (SUSPEND_WRITER ,SUSPEND_HIGH ,0 , +"\nStart of write_header_chunk loop with %d bytes to store.\n" , + buffer_size); + +while (bytes_left) { +char * source_start = buffer + buffer_size - bytes_left; +char * dest_start = filewriter_buffer + filewriter_buffer_posn; +int dest_capacity =BYTES_PER_HEADER_PAGE - filewriter_buffer_posn; +sector_t next_header_page; +if (bytes_left <= dest_capacity) { +memcpy (dest_start, source_start, bytes_left); + filewriter_buffer_posn += bytes_left; +return 0 ; + } + +/* A page is full */ +memcpy (dest_start, source_start, dest_capacity); + bytes_left -= dest_capacity; + +BUG_ON (!current_header_submit_info); + +if (!current_header_submit_info->next) { + *header_link =0 ; + }else { + next_header_page = + current_header_submit_info->next->block[0 ]; + + *header_link = next_header_page; + } + +suspend_message (SUSPEND_WRITER ,SUSPEND_HIGH ,0 , +"Writing header page %d. " +"Dev is %x. Block is %lu. Blocksperpage is %d. Bd_block_size is %d.\n" , + filewriter_page_index, + current_header_submit_info->dev->bd_dev, + current_header_submit_info->block[0 ], + target_blocksperpage, + current_header_submit_info->dev->bd_block_size); + + current_header_submit_info->page = +virt_to_page (filewriter_buffer); +check_shift_keys (0 ,NULL ); + suspend_bio_ops.submit_io (WRITE , current_header_submit_info,0 ); + + filewriter_buffer_posn =0 ; + filewriter_page_index++; + current_header_submit_info = current_header_submit_info->next; + } + +return 0 ; +} + +static int filewriter_write_header_cleanup (void ) +{ +/* Write any unsaved data */ +if (filewriter_buffer_posn) { + *header_link =0 ; + +suspend_message (SUSPEND_WRITER ,SUSPEND_HIGH ,0 , +"Writing header page %d. " +"Dev is %x. Block is %lu. Blocksperpage is %d.\n" , + filewriter_page_index, + current_header_submit_info->dev->bd_dev, + current_header_submit_info->block[0 ], + target_blocksperpage); + + current_header_submit_info->page = +virt_to_page (filewriter_buffer); + suspend_bio_ops.submit_io (WRITE , + current_header_submit_info,0 ); + } + + suspend_bio_ops.finish_all_io (); + +/* Adjust image header */ + suspend_bio_ops.bdev_page_io (READ , target_bdev, target_firstblock, +virt_to_page (filewriter_buffer)); + +prepare_signature (first_header_submit_info, filewriter_buffer); + + suspend_bio_ops.bdev_page_io (WRITE , target_bdev, target_firstblock, +virt_to_page (filewriter_buffer)); + +free_pages ((unsigned long ) filewriter_buffer,0 ); + filewriter_buffer =NULL ; + header_link =NULL ; + + suspend_bio_ops.finish_all_io (); + +return 0 ; +} + +/* ------------------------- HEADER READING ------------------------- */ + +/* +* read_header_init() +* +* Description: +* 1. Attempt to read the device specified with resume2=. +* 2. Check the contents of the swap header for our signature. +* 3. Warn, ignore, reset and/or continue as appropriate. +* 4. If continuing, read the filewriter configuration section +* of the header and set up block device info so we can read +* the rest of the header & image. +* +* Returns: +* May not return if user choose to reboot at a warning. +* -EINVAL if cannot resume at this time. Booting should continue +* normally. +*/ + +static int filewriter_read_header_init (void ) +{ + filewriter_page_index =1 ; + + filewriter_buffer = (char *)get_zeroed_page (GFP_ATOMIC ); + filewriter_buffer_posn = sig_size; + +/* Read filewriter configuration */ + suspend_bio_ops.bdev_page_io (READ , target_bdev, target_firstblock, +virt_to_page ((unsigned long ) filewriter_buffer)); + +suspend_message (SUSPEND_WRITER ,SUSPEND_HIGH ,0 , +"Retrieving %d bytes from %x:%x to page %d, %p-%p.\n" , + target_bdev->bd_dev, target_firstblock, +sizeof (filewriter_header_data), + filewriter_page_index, + filewriter_buffer, filewriter_buffer +sizeof (filewriter_header_data) -1 ); +memcpy (&filewriter_header_data, + filewriter_buffer + filewriter_buffer_posn, +sizeof (filewriter_header_data)); + + filewriter_buffer_posn +=sizeof (filewriter_header_data); + +return 0 ; +} + +static int filewriter_read_header_chunk (char * buffer,int buffer_size) +{ +int bytes_left = buffer_size, ret =0 ; + +/* Read a chunk of the header */ +while ((bytes_left) && (!ret)) { +sector_t next = + *((sector_t *) (filewriter_buffer +BYTES_PER_HEADER_PAGE )); +char * dest_start = buffer + buffer_size - bytes_left; +char * source_start = + filewriter_buffer + filewriter_buffer_posn; +int source_capacity = +BYTES_PER_HEADER_PAGE - filewriter_buffer_posn; + +if (bytes_left <= source_capacity) { +memcpy (dest_start, source_start, bytes_left); + filewriter_buffer_posn += bytes_left; +return buffer_size; + } + +/* Next to read the next page */ +memcpy (dest_start, source_start, source_capacity); + bytes_left -= source_capacity; + + filewriter_page_index++; + + suspend_bio_ops.bdev_page_io (READ , target_bdev, + next,virt_to_page (filewriter_buffer)); + + filewriter_buffer_posn =0 ; + } + +return buffer_size - bytes_left; +} + +static int filewriter_read_header_cleanup (void ) +{ +free_pages ((unsigned long ) filewriter_buffer,0 ); +return 0 ; +} + +static int filewriter_serialise_extents (void ) +{ +serialise_extent_chain (&block_chain); +return 0 ; +} + +static int filewriter_load_extents (void ) +{ +int i =1 ; +struct extent * extent; + +load_extent_chain (&block_chain); + + extent = block_chain.first; + +while (i < filewriter_header_data.pd1start_extent_number) { + extent = extent->next; + i++; + } + + filewriter_header_data.pd1start_block_extent = extent; + +return 0 ; +} + +static int filewriter_write_init (int stream_number) +{ +if (stream_number ==1 ) { + currentblockextent = filewriter_header_data.pd1start_block_extent; + currentblockoffset = filewriter_header_data.pd1start_block_offset; + }else { + currentblockextent = block_chain.first; + currentblockoffset = currentblockextent->minimum; + } + +BUG_ON (!currentblockextent); + + filewriter_page_index =1 ; + current_stream = stream_number; + + suspend_bio_ops.reset_io_stats (); + +return 0 ; +} + +static int filewriter_write_chunk (struct page * buffer_page) +{ +int i; +struct submit_params submit_params; + +BUG_ON (!currentblockextent); + submit_params.readahead_index =-1 ; + submit_params.page = buffer_page; + submit_params.dev = target_bdev; + +/* Get the blocks */ +for (i =0 ; i < target_blocksperpage; i++) { + submit_params.block[i] = currentblockoffset; +GET_EXTENT_NEXT (currentblockextent, currentblockoffset); + } + +if (!submit_params.block[0 ]) +return -EIO ; + +if (TEST_ACTION_STATE (SUSPEND_TEST_FILTER_SPEED )) +return 0 ; + + suspend_bio_ops.submit_io (WRITE , &submit_params,0 ); + + filewriter_page_index++; + +return 0 ; +} + +static int filewriter_write_cleanup (void ) +{ +if (current_stream ==2 ) { + filewriter_header_data.pd1start_block_extent = currentblockextent; + filewriter_header_data.pd1start_block_offset = currentblockoffset; + } + + suspend_bio_ops.finish_all_io (); + + suspend_bio_ops.check_io_stats (); + +return 0 ; +} + +static int filewriter_read_init (int stream_number) +{ +if (stream_number ==1 ) { + currentblockextent = filewriter_header_data.pd1start_block_extent; + currentblockoffset = filewriter_header_data.pd1start_block_offset; + }else { + currentblockextent =NULL ; + currentblockoffset =0 ; + currentblockextent = + block_chain.first; + currentblockoffset = currentblockextent->minimum; + } + +BUG_ON (!currentblockextent); + + filewriter_page_index =1 ; + + suspend_bio_ops.reset_io_stats (); + + readahead_index = readahead_submit_index =-1 ; + readahead_allocs = readahead_frees =0 ; + +return 0 ; +} + +static int filewriter_begin_read_chunk (struct page * page, +int readahead_index,int sync) +{ +int i; +struct submit_params submit_params; + +BUG_ON (!currentblockextent); + + submit_params.readahead_index = readahead_index; + submit_params.page = page; + submit_params.dev = target_bdev; + +/* Get the blocks. There is no chance that they span chains. */ +for (i =0 ; i < target_blocksperpage; i++) { + submit_params.block[i] = currentblockoffset; +GET_EXTENT_NEXT (currentblockextent, currentblockoffset); + } + +if ((i = suspend_bio_ops.submit_io (READ , &submit_params, sync))) +return -EPERM ; + + filewriter_page_index++; + +check_shift_keys (0 ,NULL ); + +return 0 ; +} + +/* Note that we ignore the sync parameter. We are implementing +* read ahead, and will always wait until our readhead buffer has +* been read before returning. +*/ + +static int filewriter_read_chunk (struct page * buffer_page,int sync) +{ +static int last_result; +unsigned long * virt; + +if (sync ==SUSPEND_ASYNC ) +return filewriter_begin_read_chunk (buffer_page,-1 , sync); + +/* Start new readahead while we wait for our page */ +if (readahead_index ==-1 ) { + last_result =0 ; + readahead_index = readahead_submit_index =0 ; + } + +/* Start a new readahead? */ +if (last_result) { +/* We failed to submit a read, and have cleaned up +* all the readahead previously submitted */ +if (readahead_submit_index == readahead_index) +return -EPERM ; + goto wait; + } + +do { +if (suspend_bio_ops.prepare_readahead (readahead_submit_index)) +break ; + + readahead_allocs++; + + last_result =filewriter_begin_read_chunk ( + suspend_bio_ops.readahead_pages[readahead_submit_index], + readahead_submit_index,SUSPEND_ASYNC ); +if (last_result) { +printk ("Begin read chunk for page %d returned %d.\n" , + readahead_submit_index, last_result); + suspend_bio_ops.cleanup_readahead (readahead_submit_index); +break ; + } + + readahead_submit_index++; + +if (readahead_submit_index ==MAX_READAHEAD ) + readahead_submit_index =0 ; + + }while ((!last_result) && (readahead_submit_index != readahead_index) && + (!suspend_bio_ops.readahead_ready (readahead_index))); + +wait: + suspend_bio_ops.wait_on_readahead (readahead_index); + + virt =kmap_atomic (buffer_page,KM_USER1 ); +memcpy (virt,page_address (suspend_bio_ops.readahead_pages[readahead_index]), +PAGE_SIZE ); +kunmap_atomic (virt,KM_USER1 ); + + suspend_bio_ops.cleanup_readahead (readahead_index); + + readahead_frees++; + + readahead_index++; +if (readahead_index ==MAX_READAHEAD ) + readahead_index =0 ; + +return 0 ; +} + +static int filewriter_read_cleanup (void ) +{ + suspend_bio_ops.finish_all_io (); +while (readahead_index != readahead_submit_index) { + suspend_bio_ops.cleanup_readahead (readahead_index); + readahead_frees++; + readahead_index++; +if (readahead_index ==MAX_READAHEAD ) + readahead_index =0 ; + } + suspend_bio_ops.check_io_stats (); +BUG_ON (readahead_allocs != readahead_frees); +return 0 ; +} + +/* filewriter_invalidate_image +* +*/ +static int filewriter_invalidate_image (void ) +{ +char * cur; +int result =0 ; + + cur = (char *)get_zeroed_page (GFP_ATOMIC ); +if (!cur) { +printk ("Unable to allocate a page for restoring the image signature.\n" ); +return -ENOMEM ; + } + +/* +* If nr_suspends == 0, we must be booting, so no swap pages +* will be recorded as used yet. +*/ + +if (nr_suspends >0 ) +filewriter_release_storage (); + +/* +* We don't do a sanity check here: we want to restore the swap +* whatever version of kernel made the suspend image. +* +* We need to write swap, but swap may not be enabled so +* we write the device directly +*/ + + suspend_bio_ops.bdev_page_io (READ , target_bdev, + target_firstblock,virt_to_page (cur)); + + result =parse_signature (cur,1 ); + +if (result ==-1 ) + goto out; + +strcpy (cur, NoImage); + cur[resumed_before_byte] =0 ; + + suspend_bio_ops.bdev_page_io (WRITE , target_bdev, target_firstblock, +virt_to_page (cur)); + +if (!nr_suspends) + printk(KERN_WARNING name_suspend "Image invalidated.\n"); +out: + suspend_bio_ops.finish_all_io (); +free_pages ((unsigned long ) cur,0 ); +return 0 ; +} + +/* +* workspace_size +* +* Description: +* Returns the number of bytes of RAM needed for this +* code to do its work. (Used when calculating whether +* we have enough memory to be able to suspend & resume). +* +*/ +static unsigned long filewriter_memory_needed (void ) +{ +return 0 ; +} + +/* Print debug info +* +* Description: +*/ + +static int filewriter_print_debug_stats (char * buffer,int size) +{ +int len =0 ; +struct sysinfo sysinfo; + +if (active_writer != &filewriterops) { + len =suspend_snprintf (buffer, size,"- Filewriter inactive.\n" ); +return len; + } + + len =suspend_snprintf (buffer, size,"- Filewriter active.\n" ); + +si_swapinfo (&sysinfo); + + len+=suspend_snprintf (buffer+len, size-len," Storage available for image: %ld pages.\n" , + sysinfo.freeswap +filewriter_storage_allocated ()); + +return len; +return 0 ; +} + +/* +* Storage needed +* +* Returns amount of space in the image header required +* for the filewriter's data. +* +* We ensure the space is allocated, but actually save the +* data from write_header_init and therefore don't also define a +* save_config_info routine. +*/ +static unsigned long filewriter_storage_needed (void ) +{ +return strlen (filewriter_target) +1 ; +} + +/* +* Image_exists +* +*/ + +static int filewriter_image_exists (void ) +{ +int signature_found; +char * diskpage; + +if (try_to_open_target_device ()) +return 0 ; + + diskpage = (char *)get_zeroed_page (GFP_ATOMIC ); + +/* FIXME: Make sure bdev_page_io handles wrong parameters */ + suspend_bio_ops.bdev_page_io (READ , target_bdev, + target_firstblock,virt_to_page (diskpage)); + suspend_bio_ops.finish_all_io (); + signature_found =parse_signature (diskpage,0 ); +free_pages ((unsigned long ) diskpage,0 ); + +if (!signature_found) { +return 0 ;/* non fatal error */ + }else if (signature_found ==-1 ) { + printk(KERN_ERR name_suspend +"Unable to find a signature. Could you have moved " + "the file?\n"); +return 0 ; + } + +return 1 ; +} + +/* +* Mark resume attempted. +* +* Record that we tried to resume from this image. +*/ + +static void filewriter_mark_resume_attempted (void ) +{ +char * diskpage; +int signature_found; + +if (!target_dev_t) { +printk ("Not even trying to record attempt at resuming" +" because target_dev_t is not set.\n" ); +return ; + } + + diskpage = (char *)get_zeroed_page (GFP_ATOMIC ); + +/* FIXME: Make sure bdev_page_io handles wrong parameters */ + suspend_bio_ops.bdev_page_io (READ , target_bdev, target_firstblock,virt_to_page (diskpage)); + signature_found =parse_signature (diskpage,0 ); + +switch (signature_found) { +case 1 : + diskpage[resumed_before_byte] |=1 ; +break ; + } + + suspend_bio_ops.bdev_page_io (WRITE , target_bdev, target_firstblock, +virt_to_page (diskpage)); + suspend_bio_ops.finish_all_io (); +free_pages ((unsigned long ) diskpage,0 ); +return ; +} + +/* +* Parse Image Location +* +* Attempt to parse a resume2= parameter. +* Swap Writer accepts: +* resume2=swap:DEVNAME[:FIRSTBLOCK][@BLOCKSIZE] +* +* Where: +* DEVNAME is convertable to a dev_t by name_to_dev_t +* FIRSTBLOCK is the location of the first block in the swap file +* (specifying for a swap partition is nonsensical but not prohibited). +* BLOCKSIZE is the logical blocksize >= 512 & <= PAGE_SIZE, +* mod 512 == 0 of the device. +* Data is validated by attempting to read a swap header from the +* location given. Failure will result in filewriter refusing to +* save an image, and a reboot with correct parameters will be +* necessary. +*/ + +static int filewriter_parse_image_location (char * commandline,int only_writer) +{ +char *thischar, *devstart =NULL , *colon =NULL , *at_symbol =NULL ; +char * diskpage =NULL ; +int signature_found, result = -EINVAL , temp_result; + +if (strncmp (commandline,"file:" ,5 )) { +if (!only_writer) +return 1 ; + }else + commandline +=5 ; + + devstart = thischar = commandline; +while ((*thischar !=':' ) && (*thischar !='@' ) && + ((thischar - commandline) <250 ) && (*thischar)) + thischar++; + +if (*thischar ==':' ) { + colon = thischar; + *colon =0 ; + thischar++; + } + +while ((*thischar !='@' ) && ((thischar - commandline) <250 ) && (*thischar)) + thischar++; + +if (*thischar =='@' ) { + at_symbol = thischar; + *at_symbol =0 ; + } + +if (colon) + target_firstblock = (int )simple_strtoul (colon +1 ,NULL ,0 ); +else + target_firstblock =0 ; + +if (at_symbol) { + target_blocksize = (int )simple_strtoul (at_symbol +1 ,NULL ,0 ); +if (target_blocksize &0x1FF ) +printk ("Filewriter: Blocksizes are usually a multiple of 512. Don't expect this to work!\n" ); + }else + target_blocksize =4096 ; + + temp_result =try_to_parse_target_dev_t (devstart); + +if (colon) + *colon =':' ; +if (at_symbol) + *at_symbol ='@' ; + +if (temp_result) + goto out; + + diskpage = (char *)get_zeroed_page (GFP_ATOMIC ); + temp_result = suspend_bio_ops.bdev_page_io (READ , target_bdev, target_firstblock,virt_to_page (diskpage)); + + suspend_bio_ops.finish_all_io (); + +if (temp_result) { + printk(KERN_ERR name_suspend "Filewriter: Failed to submit I/O.\n"); + goto out; + } + + signature_found =parse_signature (diskpage,0 ); + +if (signature_found !=-1 ) { + printk(KERN_ERR name_suspend "Filewriter: File signature found.\n"); + result =0 ; + }else + printk(KERN_ERR name_suspend "Filewriter: Sorry. No signature found at specified location.\n"); + +out: +if (diskpage) +free_page ((unsigned long ) diskpage); +return result; +} + +static void set_filewriter_target (int test_it) +{ +char * buffer = (char *)get_zeroed_page (GFP_ATOMIC ); +char * buffer2 = (char *)get_zeroed_page (GFP_ATOMIC ); +int offset =0 , fd; +int sector; + +if (target_bdev) +filewriter_cleanup (0 ); + + fd =sys_open (filewriter_target,O_RDONLY ,0 ); + +if (fd <0 ) { +printk ("Filewriter: Unable to open %s.\n" , filewriter_target); + goto cleanup1; + } + + *resume2_file =0 ; + + target_inode = current->files->fd[fd]->f_dentry->d_inode; + target_type =get_target_type (target_inode); + +if (!target_is_usable) { +printk ("Filewriter: %s is a link or directory. You can't suspend to them!\n" , + filewriter_target); + goto cleanup2; + } + + target_bdev = target_inode->i_bdev ? target_inode->i_bdev : target_inode->i_sb->s_bdev; + sector =bmap (target_inode,0 ); + +if (target_bdev && sector) { + + target_blkbits = target_bdev->bd_inode->i_blkbits; + + suspend_bio_ops.bdev_page_io (READ , target_bdev, sector, +virt_to_page (buffer)); + +bdevname (target_bdev, buffer2); + offset +=snprintf (buffer + offset,PAGE_SIZE - offset, +"/dev/%s" , buffer2); + +if (sector) + offset +=snprintf (buffer + offset,PAGE_SIZE - offset, +":0x%x" , sector); + +if (target_inode->i_sb->s_blocksize !=PAGE_SIZE ) + offset +=snprintf (buffer + offset,PAGE_SIZE - offset, +"@%lu" , target_inode->i_sb->s_blocksize); + + }else + offset +=snprintf (buffer + offset,PAGE_SIZE - offset, +"%s is not a valid target." , filewriter_target); + +sprintf (resume2_file,"file:%s" , buffer); + +cleanup2: +if (test_it) +sys_close (fd); + +cleanup1: +free_pages ((unsigned long ) buffer,0 ); +free_pages ((unsigned long ) buffer2,0 ); +} + +/* filewriter_save_config_info +* +* Description: Save the target's name, not for resume time, but for all_settings. +* Arguments: Buffer: Pointer to a buffer of size PAGE_SIZE. +* Returns: Number of bytes used for saving our data. +*/ + +static int filewriter_save_config_info (char * buffer) +{ +strcpy (buffer, filewriter_target); +return strlen (filewriter_target) +1 ; +} + +/* filewriter_load_config_info +* +* Description: Reload target's name. +* Arguments: Buffer: Pointer to the start of the data. +* Size: Number of bytes that were saved. +*/ + +static void filewriter_load_config_info (char * buffer,int size) +{ +strcpy (filewriter_target, buffer); +} + +static void test_filewriter_target (void ) +{ +set_filewriter_target (1 ); +} + +extern void attempt_to_parse_resume_device (void ); + +static struct suspend_proc_data filewriter_proc_data[] = { + + { + .filename ="filewriter_target" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_STRING , + .data = { + .string = { + .variable = filewriter_target, + .max_length =256 , + } + }, + .write_proc = test_filewriter_target, + }, + + { .filename ="disable_filewriter" , + .permissions =PROC_RW , + .type =SUSPEND_PROC_DATA_INTEGER , + .data = { + .integer = { + .variable = &filewriterops.disabled, + .minimum =0 , + .maximum =1 , + } + }, + .write_proc = attempt_to_parse_resume_device, + } +}; + +static struct suspend_plugin_ops filewriterops = { + .type =WRITER_PLUGIN , + .name ="File Writer" , + .module =THIS_MODULE , + .memory_needed = filewriter_memory_needed, + .print_debug_info = filewriter_print_debug_stats, + .save_config_info = filewriter_save_config_info, + .load_config_info = filewriter_load_config_info, + .storage_needed = filewriter_storage_needed, + .initialise = filewriter_initialise, + .cleanup = filewriter_cleanup, + + .write_init = filewriter_write_init, + .write_cleanup = filewriter_write_cleanup, + .read_init = filewriter_read_init, + .read_cleanup = filewriter_read_cleanup, + + .ops = { + .writer = { + .write_chunk = filewriter_write_chunk, + .read_chunk = filewriter_read_chunk, + .noresume_reset = filewriter_noresume_reset, + .storage_available = filewriter_storage_available, + .storage_allocated = filewriter_storage_allocated, + .release_storage = filewriter_release_storage, + .allocate_header_space = filewriter_allocate_header_space, + .allocate_storage = filewriter_allocate_storage, + .image_exists = filewriter_image_exists, + .mark_resume_attempted = filewriter_mark_resume_attempted, + .write_header_init = filewriter_write_header_init, + .write_header_chunk = filewriter_write_header_chunk, + .write_header_cleanup = filewriter_write_header_cleanup, + .read_header_init = filewriter_read_header_init, + .read_header_chunk = filewriter_read_header_chunk, + .read_header_cleanup = filewriter_read_header_cleanup, + .serialise_extents = filewriter_serialise_extents, + .load_extents = filewriter_load_extents, + .invalidate_image = filewriter_invalidate_image, + .parse_image_location = filewriter_parse_image_location, + } + } +}; + +/* ---- Registration ---- */ +static __init int filewriter_load (void ) +{ +int result; +int i, numfiles =sizeof (filewriter_proc_data) /sizeof (struct suspend_proc_data ); + +printk ("Software Suspend FileWriter loading.\n" ); + +if (!(result =suspend_register_plugin (&filewriterops))) { +for (i=0 ; i< numfiles; i++) +suspend_register_procfile (&filewriter_proc_data[i]); + }else +printk ("Software Suspend FileWriter unable to register!\n" ); +return result; +} + +#ifdef MODULE +static __exit void filewriter_unload(void) +{ +int i, numfiles =sizeof (filewriter_proc_data) /sizeof (struct suspend_proc_data ); + +printk ("Software Suspend FileWriter unloading.\n" ); + +for (i=0 ; i< numfiles; i++) +suspend_unregister_procfile (&filewriter_proc_data[i]); +suspend_unregister_plugin (&filewriterops); +} + +module_init (filewriter_load); +module_exit (filewriter_unload); +MODULE_LICENSE ("GPL" ); +MODULE_AUTHOR ("Nigel Cunningham" ); +MODULE_DESCRIPTION ("Suspend2 filewriter" ); +#else +late_initcall (filewriter_load); +#endif
Re: [PATCH] [47/48] Suspend2 2.1.9.8 for 2.6.12: 623-generic-block-io.patch
Hi!
Quoted 14 lines
kernel/power/suspend_file.c +14
-0+static int target_type =-1 ; + +/* +static char * description[7] = { +"Socket", +"Link", +"Regular file", +"Block device", +"Directory", +"Character device", +"Fifo", +}; +*/ +
...
Quoted 28 lines
kernel/power/suspend_file.c +28
-0+/* +* Helpers. +*/ + +/* +* Return the type of target we have, an index into the descriptions +* above. +*/ +static int get_target_type (struct inode * inode) +{ +switch (inode->i_mode &S_IFMT ) { +case S_IFSOCK : + target_type =0 ; +break ; +case S_IFLNK : + target_type =1 ; +break ; +case S_IFREG : + target_type =2 ; +break ; +case S_IFBLK : + target_type =3 ; +break ; +case S_IFDIR : + target_type =4 ; +break ; +case S_IFCHR : + target_type =5 ;
kernel/power/suspend_file.c line 219
Quoted 10 lines
kernel/power/suspend_file.c +10
-0+break ; +case S_IFIFO : + target_type =6 ; +break ; + } +return target_type; +} + +#define target_is_usable (!(target_type == 1 || target_type == 4)) +#define target_num_sectors (target_inode->i_size >> target_blkbits)
Why do you need this?
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
PATCH 48/48 Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
In reply to Nigel Cunninghamcrypto/Kconfig +7 -0
@@ -262,6 +262,13 @@ config CRYPTO_DEFLATE You will most probably want this if using IPSec. +config CRYPTO_LZF + tristate "LZF compression algorithm" + depends on CRYPTO + help + This is the LZF algorithm. It is especially useful for Suspend2, + because it achieves good compression quickly. + config CRYPTO_MICHAEL_MIC tristate "Michael MIC keyed digest algorithm" depends on CRYPTO
crypto/lzf.c +442 -0
442 lines not on this page · raw message
crypto/Makefile +1 -0
@@ -30,5 +30,6 @@ obj-$(CONFIG_CRYPTO_ANUBIS) += anubis.o obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o +obj-$(CONFIG_CRYPTO_LZF) += lzf.o obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
Re: [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
Quoted 13 lines · Nigel Cunningham
diff -ruNp
625-crypto-api-work.patch-old/crypto/lzf.c625-crypto-api-work.patch-new/crypto/lzf.ccrypto/lzf.c 2005-07-05 23:57:15.000000000 +1000 renamed from crypto/lzf.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+static int lzf_compress(void * context, const u8 *in_data, unsigned int in_len, + u8 *out_data, unsigned int *out_len) +{ + struct lzf_ctx * ctx = (struct lzf_ctx *) context; + const u8 **htab = ctx->hbuf; + const u8 **hslot; + const u8 *ip = in_data; + u8 *op = out_data; + const u8 *in_end = ip + in_len; + u8 *out_end = op + *out_len - 3;
crypto/lzf.c 2005-07-05 23:57:15.000000000 +1000
Quoted 10 lines
crypto/lzf.c 2005-07-05 23:57:15.000000000 +1000 renamed from crypto/lzf.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ const u8 *ref; + + unsigned int hval = FRST (ip); + unsigned long off; + int lit = 0; + + if (ctx->first_call) { + ctx->first_call = 0; + } +#if INIT_HTAB
[snip, snip]
scripts/Lindent, please.
Re: [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
Hi.
Thanks very much for all your reading and commenting. I really appreciate it. By the way, you do want replies sent to both addresses?
This one is contributed code. I haven't had or needed an update since it was added though, so I guess I could reindent it. Will do.
Regards,
Nigel
Quoted 29 lines · Pekka Enberg
Quoted 13 lines · Nigel Cunningham
diff -ruNp
625-crypto-api-work.patch-old/crypto/lzf.c625-crypto-api-work.patch-new/crypto/lzf.ccrypto/lzf.c 2005-07-05 23:57:15.000000000 +1000 renamed from crypto/lzf.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+static int lzf_compress(void * context, const u8 *in_data, unsigned int in_len, + u8 *out_data, unsigned int *out_len) +{ + struct lzf_ctx * ctx = (struct lzf_ctx *) context; + const u8 **htab = ctx->hbuf; + const u8 **hslot; + const u8 *ip = in_data; + u8 *op = out_data; + const u8 *in_end = ip + in_len; + u8 *out_end = op + *out_len - 3;crypto/lzf.c 2005-07-05 23:57:15.000000000 +1000
Quoted 10 lines
crypto/lzf.c 2005-07-05 23:57:15.000000000 +1000 renamed from crypto/lzf.c 1970-01-01 10:00:00.000000000 +1000 +10
-0+ const u8 *ref; + + unsigned int hval = FRST (ip); + unsigned long off; + int lit = 0; + + if (ctx->first_call) { + ctx->first_call = 0; + } +#if INIT_HTAB[snip, snip]
scripts/Lindent, please.
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
Quoted 2 lines · Nigel Cunningham
Thanks very much for all your reading and commenting. I really appreciate it. By the way, you do want replies sent to both addresses?
I'm just browsing through it for now. Replies to cs.helsinki.fi is sufficient. I use my gmail address for replying to lkml posts only.
Pekka
Re: [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
In reply to Nigel CunninghamHi!
crypto/lzf.c line 91
Quoted 11 lines
crypto/lzf.c +11
-0+/* +* sacrifice some compression quality in favour of compression speed. +* (roughly 1-2% worse compression for large blocks and +* 9-10% for small, redundant, blocks and >>20% better speed in both cases) +* In short: enable this for binary data, disable this for text data. +*/ +#define ULTRA_FAST 1 + +#define STRICT_ALIGN 0 +#define USE_MEMCPY 1 +#define INIT_HTAB 0
Ugly, ugly, ugly. Pick one set of options and use them.
crypto/lzf.c line 102
Quoted 8 lines
crypto/lzf.c +8
-0+/* +* don't play with this unless you benchmark! +* decompression is not dependent on the hash function +* the hashing function might seem strange, just believe me +* it works ;) +*/ +#define FRST (p) (((p[0]) << 8) + p[1]) +#define NEXT (v,p) (((v) << 8) + p[2])
Dnt wrt mcr nms lk ths!
crypto/lzf.c line 214
Quoted 11 lines
crypto/lzf.c +11
-0+if (ctx->first_call) { + ctx->first_call =0 ; + } +#if INIT_HTAB +# if USE_MEMCPY +memset (htab,0 ,sizeof (htab)); +# else +for (hslot = htab; hslot < htab +HSIZE ; hslot++) + *hslot++ = ip; +# endif +#endif
Eh? Use_memcpy and then it memsets?
BTW if it is "contributed code", you may want original contributor to push it, so that you don't have to do all the lindenting/cleaning/etc yourself.
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
Re: [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
Howdy.
Quoted 3 lines · Pavel Machek
BTW if it is "contributed code", you may want original contributor to push it, so that you don't have to do all the lindenting/cleaning/etc yourself.
It is, and he's gone to ground. As requested by Pekka, I've Lindented it. Apart from that, I didn't really want to touch it for fear of breaking it :> Then again, I felt like that with suspend code at one stage!
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Re: [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
In reply to Nigel CunninghamPlease send new crytptoapi algorithms to the cryptoapi maintainers for review. It's something that's completely independent of any software suspend stuff.
Re: [PATCH] [48/48] Suspend2 2.1.9.8 for 2.6.12: 624-filewriter.patch
Hi.
Quoted 2 lines · Christoph Hellwig
Please send new crytptoapi algorithms to the cryptoapi maintainers for review. It's something that's completely independent of any software suspend stuff.
Thanks.
Did this about a week prior to sending the patches. Sorry - including this in the patches posted was an oversight.
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Hi Nigel,
Quoted 6 lines · Nigel Cunningham
As requested, here are the patches that form Suspend2, for review.
I've tried to split it up into byte size chunks, but please don't expect that these will be patches that can mutate swsusp into Suspend2. That would roughly equivalent to asking for patches that patch Reiser3 into Reiser4 - it's a redesign.
Please consider putting diffstat in the patches. They make navigating large patchsets easier.
Pekka
Hi.
Quoted 5 lines · Pekka Enberg
Hi Nigel,
Quoted 2 lines · Nigel Cunningham
As requested, here are the patches that form Suspend2, for review.
Quoted 7 lines
Quoted 4 lines
I've tried to split it up into byte size chunks, but please don't expect that these will be patches that can mutate swsusp into Suspend2. That would roughly equivalent to asking for patches that patch Reiser3 into Reiser4 - it's a redesign.
Please consider putting diffstat in the patches. They make navigating large patchsets easier.
Adjusted my patch generation script to do this.
Thanks.
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Quoted 6 lines · Nigel Cunningham
As requested, here are the patches that form Suspend2, for review.
I've tried to split it up into byte size chunks, but please don't expect that these will be patches that can mutate swsusp into Suspend2. That would roughly equivalent to asking for patches that patch Reiser3 into Reiser4 - it's a redesign.
It still has quite a big impact outside kernel/power :-(.
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
Hi!
Quoted 10 lines
As requested, here are the patches that form Suspend2, for review.
I've tried to split it up into byte size chunks, but please don't expect that these will be patches that can mutate swsusp into Suspend2. That would roughly equivalent to asking for patches that patch Reiser3 into Reiser4 - it's a redesign.
There are a few extra patches not included here, all of which are not core to Suspend2. Since I'm not expecting this code to get merged as is, I haven't worried about including them. If that's a problem, let me know.
Is swsusp1 expected to be functional after these are applied? You removed *some* of its hooks, but not all, so I'm confused.
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
Hi.
Quoted 9 lines · Pavel Machek
Hi!
Quoted 7 lines
As requested, here are the patches that form Suspend2, for review.
I've tried to split it up into byte size chunks, but please don't expect that these will be patches that can mutate swsusp into Suspend2. That would roughly equivalent to asking for patches that patch Reiser3 into Reiser4 - it's a redesign.
Quoted 6 lines
Quoted 3 lines
There are a few extra patches not included here, all of which are not core to Suspend2. Since I'm not expecting this code to get merged as is, I haven't worried about including them. If that's a problem, let me know.
Is swsusp1 expected to be functional after these are applied? You removed *some* of its hooks, but not all, so I'm confused.
It should definitely still be functional.
In the past I've gone with the logic that if people go to the trouble of patching suspend2 in, they probably want to use it instead of swsusp. I'm happy for the reboot and acpi patches to be left out for now.
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Hi again.
Quoted 2 lines · Pavel Machek
Is swsusp1 expected to be functional after these are applied? You removed *some* of its hooks, but not all, so I'm confused.
I've been thinking about this some more and wondering whether I should just replace swsusp. I really don't want to step on your toes though. What would you like to see happen?
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Quoted 3 lines · Nigel Cunningham
I've been thinking about this some more and wondering whether I should just replace swsusp. I really don't want to step on your toes though. What would you like to see happen?
Do you implement the entire swsusp userspace interface? If not, removing it probably isn't a reasonable plan without fair warning.
Signature · 1 line
Matthew Garrett | [email protected]
Hi.
Quoted 8 lines · Matthew Garrett
Quoted 3 lines · Nigel Cunningham
I've been thinking about this some more and wondering whether I should just replace swsusp. I really don't want to step on your toes though. What would you like to see happen?
Do you implement the entire swsusp userspace interface? If not, removing it probably isn't a reasonable plan without fair warning.
I'm not suggesting removing the sysfs interface or replacing system to ram - just the suspend to disk part.
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Quoted 6 lines · Nigel Cunningham
Quoted 2 lines · Matthew Garrett
Do you implement the entire swsusp userspace interface? If not, removing it probably isn't a reasonable plan without fair warning.
I'm not suggesting removing the sysfs interface or replacing system to ram - just the suspend to disk part.
Right, so you support the resume from disk trigger in sysfs and the /proc/acpi/sleep interface? If suspend2 is a complete dropin replacement then I'm much happier with the idea of dropping swsusp, but I don't want to have to tie suspend/resume scripts to kernel versions.
Signature · 1 line
Matthew Garrett | [email protected]
Hi.
Quoted 5 lines · Matthew Garrett
Quoted 4 lines · Nigel Cunningham
Quoted 2 lines · Matthew Garrett
Do you implement the entire swsusp userspace interface? If not, removing it probably isn't a reasonable plan without fair warning.
Quoted 7 lines
Quoted 2 lines
I'm not suggesting removing the sysfs interface or replacing system to ram - just the suspend to disk part.
Right, so you support the resume from disk trigger in sysfs and the
/proc/acpi/sleepinterface? If suspend2 is a complete dropin replacement then I'm much happier with the idea of dropping swsusp, but I don't want to have to tie suspend/resume scripts to kernel versions.
Suspend2 currently has a proc entry to do the same, but I can easily hook into the sysfs code. /proc/acpi/sleep is already taken care of, as is the reboot handler. Suspend2 currently uses resume2= so as to not conflict with swsusp, but that can be changed too. I always to my utmost to ease the pain for other users. If something doesn't work the way you want, just tell me and (assuming it's reasonable), I'll fix it.
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Hi,
Quoted 5 lines · Matthew Garrett
Quoted 4 lines · Nigel Cunningham
Quoted 2 lines · Matthew Garrett
Do you implement the entire swsusp userspace interface? If not, removing it probably isn't a reasonable plan without fair warning.
Quoted 7 lines
Quoted 2 lines
I'm not suggesting removing the sysfs interface or replacing system to ram - just the suspend to disk part.
Right, so you support the resume from disk trigger in sysfs and the
/proc/acpi/sleepinterface? If suspend2 is a complete dropin replacement then I'm much happier with the idea of dropping swsusp, but I don't want to have to tie suspend/resume scripts to kernel versions.
I don't think that swsusp can be replaced with suspend2 right now. First, swsusp works on x86-64, and the support in suspend2 is preliminary, AFAIK. Second, the IA64 support for swsusp is in the works, and it is not supported by suspend2.
Please don't plan to drop swsusp until you are able to replace it _completely_.
Greets,
Rafael
Signature · 3 lines
- Would you tell me, please, which way I ought to go from here? - That depends a good deal on where you want to get to. -- Lewis Carroll "Alice's Adventures in Wonderland"
Hi.
Quoted 16 lines · Rafael J. Wysocki
Hi,
Quoted 5 lines · Matthew Garrett
Quoted 4 lines · Nigel Cunningham
Quoted 2 lines · Matthew Garrett
Do you implement the entire swsusp userspace interface? If not, removing it probably isn't a reasonable plan without fair warning.
Quoted 7 lines
Quoted 2 lines
I'm not suggesting removing the sysfs interface or replacing system to ram - just the suspend to disk part.
Right, so you support the resume from disk trigger in sysfs and the
/proc/acpi/sleepinterface? If suspend2 is a complete dropin replacement then I'm much happier with the idea of dropping swsusp, but I don't want to have to tie suspend/resume scripts to kernel versions.
Quoted 6 lines
I don't think that swsusp can be replaced with suspend2 right now. First, swsusp works on x86-64, and the support in suspend2 is preliminary, AFAIK. Second, the IA64 support for swsusp is in the works, and it is not supported by suspend2.
Please don't plan to drop swsusp until you are able to replace it _completely_.
Fair enough. I didn't realise Pavel had IA64 support too :>
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Quoted 4 lines · Matthew Garrett
Right, so you support the resume from disk trigger in sysfs and the
/proc/acpi/sleepinterface? If suspend2 is a complete dropin replacement then I'm much happier with the idea of dropping swsusp, but I don't want to have to tie suspend/resume scripts to kernel versions.
JFTR: i second this. There is already enough hackery involved if one wants to provide a smooth user experience ;-)
Signature · 3 lines
Stefan Seyfried \ "I didn't want to write for pay. I QA / R&D Team Mobile Devices \ wanted to be paid for what I write." SUSE LINUX Products GmbH, Nürnberg \ -- Leonard Cohen
Hi.
Quoted 9 lines · Stefan Seyfried
Quoted 4 lines · Matthew Garrett
Right, so you support the resume from disk trigger in sysfs and the
/proc/acpi/sleepinterface? If suspend2 is a complete dropin replacement then I'm much happier with the idea of dropping swsusp, but I don't want to have to tie suspend/resume scripts to kernel versions.JFTR: i second this. There is already enough hackery involved if one wants to provide a smooth user experience ;-)
Consider it promised, and feel free to poke me if I forget! (That said, I won't now!).
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Hi!
Quoted 6 lines
Quoted 2 lines
Is swsusp1 expected to be functional after these are applied? You removed *some* of its hooks, but not all, so I'm confused.
I've been thinking about this some more and wondering whether I should just replace swsusp. I really don't want to step on your toes though. What would you like to see happen?
When/if suspend2 is merged and working, there's no reason to keep swsusp around. So patches for removing swsusp1 are quite okay at the end of series. OTOH it is one more patch for you to maintain, so... do whatever you want.
Pavel
Signature · 1 line
teflon -- maybe it is a trademark, but it should not be.
This submissions is completely useless. 48 patches without description and a subbject lines that hides the little information even more.
Please start again, and only with useful patches that on their own, against -mm and with detailed explanations of each patch.
Hi.
Quoted 2 lines · Christoph Hellwig
This submissions is completely useless. 48 patches without description and a subbject lines that hides the little information even more.
Yes. My mistake with the subject lines. Sorry. I'll also add better descriptions when I repost.
Quoted 2 lines
Please start again, and only with useful patches that on their own, against -mm and with detailed explanations of each patch.
First I'll act on feedback from this round. Regarding diffing against -mm, I though Andrew wanted patches against Linus' tree. I'm happy to do both. Andrew?
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Quoted 4 lines · Nigel Cunningham
First I'll act on feedback from this round. Regarding diffing against
-mm, I though Andrew wanted patches against Linus' tree. I'm happy to do both. Andrew?
Is it anywhere near being ready?
Am still futzing with the ongoing pm_message_t saga here. I hope there's no overlap with that.
Hi.
Quoted 7 lines · Andrew Morton
Quoted 4 lines · Nigel Cunningham
First I'll act on feedback from this round. Regarding diffing against
-mm, I though Andrew wanted patches against Linus' tree. I'm happy to do both. Andrew?Is it anywhere near being ready?
Yes. Development has almost stopped. I'm just addressing the responses from last week's email and then will send again, so yes. Here's my todo list (some done already):
Todo list as at 12 July 2005.
FIX.
- Check for redundant variables & functions and remove.
- Ensure driver model calls are right (Bug 45).
- NFS issue (Bug 52)
COMPLETE.
- Opteron support (Bug 1).
- Documentation cleanups.
IMPLEMENT.
- 64GB support.
- IA64 support.
- Implement remembering compression ratio acheived using another method apart
from LZF plugin.
HCH/Pavel Replies.
- Setting NoSave pages early.
- Amount needed parameter.
- Add underscores to variable names.
- Inline functions for image preparation (storage needed etc).
- Debugging code from extents.
- extenchain -> extent_chain
- Patch descriptions/rationales.
- Against mm.
- Remove #define XXX_C
Quoted 2 lines
Am still futzing with the ongoing
pm_message_tsaga here. I hope there's no overlap with that.
We've been using pm_message_t in Suspend2 since February, so we should already be where you're heading.
Regards,
Nigel
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.
Hi.
Quoted 2 lines · Andrew Morton
Am still futzing with the ongoing
pm_message_tsaga here. I hope there's no overlap with that.
Perhaps I can help there. I've been preparing a patchset against the latest mm. Here's a patch with additional changes I know about.
Hope it's helpful.
Regards,
Nigel
arch/ppc/platforms/pmac_pic.c | 2 +- arch/ppc/syslib/open_pic.c | 2 +- drivers/base/power/resume.c | 8 ++++---- drivers/base/power/runtime.c | 8 ++++---- drivers/base/power/suspend.c | 14 +++++++------- drivers/base/power/sysfs.c | 9 +++++---- drivers/ide/ide.c | 2 +- drivers/ide/pci/sc1200.c | 8 ++++---- drivers/ide/ppc/pmac.c | 16 ++++++++-------- drivers/macintosh/mediabay.c | 6 +++--- drivers/macintosh/via-pmu.c | 2 +- drivers/media/dvb/cinergyT2/cinergyT2.c | 2 +- drivers/media/video/bttv-driver.c | 2 +- drivers/net/irda/vlsi_ir.c | 18 ++++++++++-------- drivers/net/wireless/airo.c | 3 ++- drivers/net/wireless/orinoco_pci.c | 2 +- drivers/net/wireless/prism54/islpci_hotplug.c | 2 +- drivers/pci/pci.c | 11 +++++++---- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/serial/pmac_zilog.c | 6 +++--- drivers/usb/core/hub.c | 18 +++++++++--------- drivers/usb/core/usb.c | 2 +- drivers/usb/host/ehci-dbg.c | 2 +- drivers/usb/host/ohci-dbg.c | 2 +- drivers/usb/host/sl811-hcd.c | 6 +++--- drivers/video/aty/aty128fb.c | 12 ++++++------ drivers/video/aty/atyfb_base.c | 10 +++++----- drivers/video/aty/radeon_pm.c | 12 ++++++------ drivers/video/i810/i810_main.c | 22 ++++++++-------------- drivers/video/savage/savagefb_driver.c | 2 +- include/linux/pm.h | 18 ++++++++++++++---- sound/pci/atiixp.c | 4 ++-- 32 files changed, 123 insertions(+), 112 deletions(-)
arch/ppc/platforms/pmac_pic.c +1 -1
@@ -619,7 +619,7 @@ not_found:return viaint; } -static int pmacpic_suspend (struct sys_device *sysdev,u32 state) +static int pmacpic_suspend (struct sys_device *sysdev,pm_message_t state) {int viaint =pmacpic_find_viaint ();
arch/ppc/syslib/open_pic.c +1 -1
@@ -948,7 +948,7 @@ static void openpic_cached_disable_irq(u* we need something better to deal with that... Maybe switch to S1 for * cpufreq changes */ -int openpic_suspend (struct sys_device *sysdev,u32 state) +int openpic_suspend (struct sys_device *sysdev,pm_message_t state) {int i;unsigned long flags;
drivers/base/power/resume.c +4 -4
@@ -26,11 +26,11 @@ int resume_device(struct device * dev)down (&dev->sem);if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state) { + && dev->power.pm_parent->power.power_state.event) {dev_err (dev,"PM: resume from %d, parent %s still %d\n" , - dev->power.power_state, + dev->power.power_state.event, dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state); + dev->power.pm_parent->power.power_state.event); }if (dev->bus && dev->bus->resume) {dev_dbg (dev,"resuming\n" );
@@ -54,7 +54,7 @@ void dpm_resume(void)list_add_tail (entry, &dpm_active);up (&dpm_list_sem); -if (!dev->power.prev_state) +if (!dev->power.prev_state.event)resume_device (dev);down (&dpm_list_sem);put_device (dev);
drivers/base/power/runtime.c +4 -4
@@ -13,10 +13,10 @@static void runtime_resume (struct device * dev) {dev_dbg (dev,"resuming\n" ); -if (!dev->power.power_state) +if (!dev->power.power_state.event)return ;if (!resume_device (dev)) - dev->power.power_state =0 ; + dev->power.power_state =PMSG_ON ; }
@@ -49,10 +49,10 @@ int dpm_runtime_suspend(struct device *int error =0 ;down (&dpm_sem); -if (dev->power.power_state == state) +if (dev->power.power_state.event == state.event) goto Done; -if (dev->power.power_state) +if (dev->power.power_state.event)runtime_resume (dev);if (!(error =suspend_device (dev, state)))
drivers/base/power/suspend.c +7 -7
@@ -40,22 +40,22 @@ int suspend_device(struct device * dev,int error =0 ;down (&dev->sem); -if (dev->power.power_state) { +if (dev->power.power_state.event) {dev_dbg (dev,"PM: suspend %d-->%d\n" , - dev->power.power_state, state); + dev->power.power_state.event, state.event); }if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state) { + && dev->power.pm_parent->power.power_state.event) {dev_err (dev,"PM: suspend %d->%d, parent %s already %d\n" , - dev->power.power_state, state, + dev->power.power_state.event, state.event, dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state); + dev->power.pm_parent->power.power_state.event); } - dev->power.prev_state = dev->power.power_state; + dev->power.prev_state.event = dev->power.power_state.event; -if (dev->bus && dev->bus->suspend && !dev->power.power_state) { +if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {dev_dbg (dev,"suspending\n" ); error = dev->bus->suspend (dev, state); }
drivers/base/power/sysfs.c +5 -4
@@ -26,19 +26,20 @@static ssize_t state_show (struct device * dev,struct device_attribute *attr,char * buf) { -return sprintf (buf,"%u\n" , dev->power.power_state); +return sprintf (buf,"%u\n" , dev->power.power_state.event); }static ssize_t state_store (struct device * dev,struct device_attribute *attr,const char * buf,size_t n) { -u32 state; +pm_message_t state;char * rest;int error =0 ; - state =simple_strtoul (buf, &rest,10 ); + state.event =simple_strtoul (buf, &rest,10 ); + state.flags =PFL_RUNTIME ;if (*rest)return -EINVAL ; -if (state) +if (state.event) error =dpm_runtime_suspend (dev, state);else dpm_runtime_resume (dev);
drivers/ide/ide.c +1 -1
@@ -1229,7 +1229,7 @@ static int generic_ide_suspend(struct de rq.special = &args; rq.pm = &rqpm; rqpm.pm_step = ide_pm_state_start_suspend; - rqpm.pm_state = state; + rqpm.pm_state = state.event;return ide_do_drive_cmd (drive, &rq, ide_wait); }
drivers/ide/pci/sc1200.c +4 -4
@@ -350,9 +350,9 @@ static int sc1200_suspend (struct pci_de {ide_hwif_t *hwif =NULL ; -printk ("SC1200: suspend(%u)\n" , state); +printk ("SC1200: suspend(%u)\n" , state.event); -if (state ==0 ) { +if (state.event ==EVENT_ON ) {// we only save state when going from full power to less //
@@ -386,8 +386,8 @@ static int sc1200_suspend (struct pci_de/* You don't need to iterate over disks -- sysfs should have done that for you already */ pci_disable_device (dev); -pci_set_power_state (dev,state); - dev->current_state = state; +pci_set_power_state (dev,pci_choose_state (dev, state)); + dev->current_state = state.event;return 0 ; }
drivers/ide/ppc/pmac.c +8 -8
@@ -1504,12 +1504,12 @@ pmac_ide_macio_attach(struct macio_dev * }static int -pmac_ide_macio_suspend (struct macio_dev *mdev,u32 state) +pmac_ide_macio_suspend (struct macio_dev *mdev,pm_message_t state) {ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata (&mdev->ofdev.dev);int rc =0 ; -if (state != mdev->ofdev.dev.power.power_state && state >=2 ) { +if (state.event != mdev->ofdev.dev.power.power_state.event && state.event >=EVENT_SUSPEND ) { rc =pmac_ide_do_suspend (hwif);if (rc ==0 ) mdev->ofdev.dev.power.power_state = state;
@@ -1524,10 +1524,10 @@ pmac_ide_macio_resume(struct macio_dev *ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata (&mdev->ofdev.dev);int rc =0 ; -if (mdev->ofdev.dev.power.power_state !=0 ) { +if (mdev->ofdev.dev.power.power_state.event !=EVENT_ON ) { rc =pmac_ide_do_resume (hwif);if (rc ==0 ) - mdev->ofdev.dev.power.power_state =0 ; + mdev->ofdev.dev.power.power_state =PMSG_ON ; }return rc;
@@ -1608,12 +1608,12 @@ pmac_ide_pci_attach(struct pci_dev *pdev }static int -pmac_ide_pci_suspend (struct pci_dev *pdev,u32 state) +pmac_ide_pci_suspend (struct pci_dev *pdev,pm_message_t state) {ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata (pdev);int rc =0 ; -if (state != pdev->dev.power.power_state && state >=2 ) { +if (state.event != pdev->dev.power.power_state.event && state.event >=2 ) { rc =pmac_ide_do_suspend (hwif);if (rc ==0 ) pdev->dev.power.power_state = state;
@@ -1628,10 +1628,10 @@ pmac_ide_pci_resume(struct pci_dev *pdevide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata (pdev);int rc =0 ; -if (pdev->dev.power.power_state !=0 ) { +if (pdev->dev.power.power_state.event !=EVENT_ON ) { rc =pmac_ide_do_resume (hwif);if (rc ==0 ) - pdev->dev.power.power_state =0 ; + pdev->dev.power.power_state =PMSG_ON ; }return rc;
drivers/macintosh/mediabay.c +3 -3
@@ -708,7 +708,7 @@ static int __pmac media_bay_suspend(stru {struct media_bay_info *bay =macio_get_drvdata (mdev); -if (state != mdev->ofdev.dev.power.power_state && state ==PM_SUSPEND_MEM ) { +if (state.event != mdev->ofdev.dev.power.power_state.event && state.event ==EVENT_SUSPEND ) {down (&bay->lock); bay->sleeping =1 ;set_mb_power (bay,0 );
@@ -723,8 +723,8 @@ static int __pmac media_bay_resume(struc {struct media_bay_info *bay =macio_get_drvdata (mdev); -if (mdev->ofdev.dev.power.power_state !=0 ) { - mdev->ofdev.dev.power.power_state =0 ; +if (mdev->ofdev.dev.power.power_state.event !=EVENT_ON ) { + mdev->ofdev.dev.power.power_state =PMSG_ON ;/* We re-enable the bay using it's previous content only if it did not change. Note those bozo timings,
drivers/macintosh/via-pmu.c +1 -1
@@ -3065,7 +3065,7 @@ static int pmu_sys_suspended = 0;static int pmu_sys_suspend (struct sys_device *sysdev,pm_message_t state) { -if (state !=PM_SUSPEND_DISK || pmu_sys_suspended) +if (state.event !=EVENT_SUSPEND || pmu_sys_suspended)return 0 ;/* Suspend PMU event interrupts */
drivers/media/dvb/cinergyT2/cinergyT2.c +1 -1
@@ -888,7 +888,7 @@ static int cinergyt2_suspend (struct usbif (down_interruptible (&cinergyt2->sem))return -ERESTARTSYS ; -if (state >0 ) {/* state 0 seems to mean DEVICE_PM_ON */ +if (state.event >0 ) {/* state 0 seems to mean DEVICE_PM_ON */ struct cinergyt2 *cinergyt2 =usb_get_intfdata (intf);#ifdef ENABLE_RC cancel_delayed_work (&cinergyt2->rc_query_work);
drivers/media/video/bttv-driver.c +1 -1
@@ -4043,7 +4043,7 @@ static int bttv_suspend(struct pci_dev *struct bttv_buffer_set idle;unsigned long flags; -dprintk ("bttv%d: suspend %d\n" , btv->c.nr, state); +dprintk ("bttv%d: suspend %d\n" , btv->c.nr, state.event);/* stop dma + irqs */ spin_lock_irqsave (&btv->s_lock,flags);
drivers/net/irda/vlsi_ir.c +10 -8
@@ -1748,10 +1748,11 @@ static int vlsi_irda_suspend(struct pci_ {struct net_device *ndev =pci_get_drvdata (pdev);vlsi_irda_dev_t *idev; +pci_power_t new_state; -if (state <1 || state >3 ) { +if (state.event <EVENT_FREEZE || state.event >EVENT_SUSPEND ) {IRDA_ERROR ("%s - %s: invalid pm state request: %u\n" , - __FUNCTION__,PCIDEV_NAME (pdev), state); + __FUNCTION__,PCIDEV_NAME (pdev), state.event);return 0 ; }if (!ndev) {
@@ -1760,14 +1761,15 @@ static int vlsi_irda_suspend(struct pci_return 0 ; } idev = ndev->priv; + new_state =pci_choose_state (pdev, state);down (&idev->sem);if (pdev->current_state !=0 ) {/* already suspended */ -if (state > pdev->current_state) {/* simply go deeper */ -pci_set_power_state (pdev,state); - pdev->current_state = state; +if (new_state > pdev->current_state) {/* simply go deeper */ +pci_set_power_state (pdev, new_state); + pdev->current_state = new_state; }else -IRDA_ERROR ("%s - %s: invalid suspend request %u -> %u\n" , __FUNCTION__,PCIDEV_NAME (pdev), pdev->current_state, state); +IRDA_ERROR ("%s - %s: invalid suspend request %u -> %u\n" , __FUNCTION__,PCIDEV_NAME (pdev), pdev->current_state, state.event);up (&idev->sem);return 0 ; }
@@ -1781,8 +1783,8 @@ static int vlsi_irda_suspend(struct pci_ idev->new_baud = idev->baud; } -pci_set_power_state (pdev,state); - pdev->current_state = state; +pci_set_power_state (pdev,new_state); + pdev->current_state = new_state; idev->resume_ok =1 ;up (&idev->sem);return 0 ;
drivers/net/wireless/airo.c +2 -1
@@ -5487,6 +5487,7 @@ static int airo_pci_suspend(struct pci_dstruct airo_info *ai = dev->priv;Cmd cmd;Resp rsp; +int new_state =pci_choose_state (pdev, state);if ((ai->APList ==NULL ) && (ai->APList =kmalloc (sizeof (APListRid),GFP_KERNEL )) ==NULL )
@@ -5502,7 +5503,7 @@ static int airo_pci_suspend(struct pci_dreturn -EAGAIN ;disable_MAC (ai,0 );netif_device_detach (dev); - ai->power = state; + ai->power = state.event; cmd.cmd=HOSTSLEEP ;issuecommand (ai, &cmd, &rsp);
drivers/net/wireless/orinoco_pci.c +1 -1
@@ -302,7 +302,7 @@ static int orinoco_pci_suspend(struct pcint err;printk (KERN_DEBUG "%s: Orinoco-PCI entering sleep mode (state=%d)\n" , - dev->name, state); + dev->name, state.event); err =orinoco_lock (priv, &flags);if (err) {
drivers/net/wireless/prism54/islpci_hotplug.c +1 -1
@@ -268,7 +268,7 @@ prism54_suspend(struct pci_dev *pdev, pmBUG_ON (!priv);printk (KERN_NOTICE "%s: got suspend request (state %d)\n" , - ndev->name, state); + ndev->name, state.event);pci_save_state (pdev);
drivers/pci/pci.c +7 -4
@@ -333,11 +333,14 @@ pci_power_t pci_choose_state(struct pci_if (ret >=0 ) state = ret; } -switch (state) { -case 0 :return PCI_D0 ; -case 3 :return PCI_D3hot; +switch (state.event) { +case EVENT_ON : +case EVENT_FREEZE : +return PCI_D0 ; +case EVENT_SUSPEND : +return PCI_D3hot;default : -printk ("They asked me for state %d\n" , state); +printk ("They asked me for state %d\n" , state.event);BUG (); }return PCI_D0 ;
drivers/scsi/lpfc/lpfc_init.c +1 -1
@@ -1475,7 +1475,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING *4 ));/* Startup the kernel thread for this host adapter. */ - phba->worker_thread =kthread_run (lpfc_do_work, phba, + phba->worker_thread =kthread_run (lpfc_do_work, phba,PF_NOFREEZE ,"lpfc_worker_%d" , phba->brd_no);if (IS_ERR (phba->worker_thread)) { error =PTR_ERR (phba->worker_thread);
drivers/serial/pmac_zilog.c +3 -3
@@ -1601,7 +1601,7 @@ static int pmz_suspend(struct macio_devreturn 0 ; } -if (pm_state == mdev->ofdev.dev.power.power_state || pm_state <2 ) +if (pm_state.event == mdev->ofdev.dev.power.power_state.event || pm_state.event <EVENT_SUSPEND )return 0 ;pmz_debug ("suspend, switching to state %d\n" , pm_state);
@@ -1661,7 +1661,7 @@ static int pmz_resume(struct macio_dev *if (uap ==NULL )return 0 ; -if (mdev->ofdev.dev.power.power_state ==0 ) +if (mdev->ofdev.dev.power.power_state.event ==EVENT_ON )return 0 ;pmz_debug ("resume, switching to state 0\n" );
@@ -1714,7 +1714,7 @@ static int pmz_resume(struct macio_dev *pmz_debug ("resume, switching complete\n" ); - mdev->ofdev.dev.power.power_state =0 ; + mdev->ofdev.dev.power.power_state =PMSG_ON ;return 0 ; }
drivers/usb/core/hub.c +9 -9
@@ -1570,7 +1570,7 @@ static int __usb_suspend_device (structstruct usb_driver *driver; intf = udev->actconfig->interface[i]; -if (state <= intf->dev.power.power_state) +if (state.event <= intf->dev.power.power_state.event)continue ;if (!intf->dev.driver)continue ;
@@ -1578,11 +1578,11 @@ static int __usb_suspend_device (structif (driver->suspend) { status = driver->suspend (intf, state); -if (intf->dev.power.power_state != state +if (intf->dev.power.power_state.event != state.event || status)dev_err (&intf->dev,"suspend %d fail, code %d\n" , - state, status); + state.event, status); }/* only drivers with suspend() can ever resume();
@@ -1595,7 +1595,7 @@ static int __usb_suspend_device (struct* since we know every driver's probe/disconnect works * even for drivers that can't suspend. */ -if (!driver->suspend || state >PM_SUSPEND_MEM ) { +if (!driver->suspend || state.event >EVENT_SUSPEND ) {#if 1 dev_warn (&intf->dev,"resume is unsafe!\n" );#else
@@ -1616,7 +1616,7 @@ static int __usb_suspend_device (struct* policies (when HNP doesn't apply) once we have mechanisms to * turn power back on! (Likely not before 2.7...) */ -if (state >PM_SUSPEND_MEM ) { +if (state.event >EVENT_SUSPEND ) {dev_warn (&udev->dev,"no poweroff yet, suspending instead\n" ); }
@@ -1733,7 +1733,7 @@ static int finish_port_resume(struct usbstruct usb_driver *driver; intf = udev->actconfig->interface[i]; -if (intf->dev.power.power_state ==PMSG_ON ) +if (intf->dev.power.power_state.event ==EVENT_SUSPEND )continue ;if (!intf->dev.driver) {/* FIXME maybe force to alt 0 */
@@ -1747,11 +1747,11 @@ static int finish_port_resume(struct usb/* can we do better than just logging errors? */ status = driver->resume (intf); -if (intf->dev.power.power_state !=PMSG_ON +if (intf->dev.power.power_state.event !=EVENT_ON || status)dev_dbg (&intf->dev,"resume fail, state %d code %d\n" , - intf->dev.power.power_state, status); + intf->dev.power.power_state.event, status); } status =0 ;
@@ -1934,7 +1934,7 @@ static int hub_resume(struct usb_interfaunsigned port1;int status; -if (intf->dev.power.power_state ==PM_SUSPEND_ON ) +if (intf->dev.power.power_state.event ==EVENT_ON )return 0 ;for (port1 =1 ; port1 <= hdev->maxchild; port1++) {
drivers/usb/core/usb.c +1 -1
@@ -1400,7 +1400,7 @@ static int usb_generic_suspend(struct de driver =to_usb_driver (dev->driver);/* there's only one USB suspend state */ -if (intf->dev.power.power_state) +if (intf->dev.power.power_state.event)return 0 ;if (driver->suspend)
drivers/usb/host/ehci-dbg.c +1 -1
@@ -643,7 +643,7 @@ show_registers (struct class_device *claspin_lock_irqsave (&ehci->lock, flags); -if (bus->controller->power.power_state) { +if (bus->controller->power.power_state.event) { size =scnprintf (next, size,"bus %s, device %s (driver " DRIVER_VERSION ")\n" "%s\n"
drivers/usb/host/ohci-dbg.c +1 -1
@@ -631,7 +631,7 @@ show_registers (struct class_device *cla hcd->product_desc, hcd_name); -if (bus->controller->power.power_state) { +if (bus->controller->power.power_state.event) { size -=scnprintf (next, size,"SUSPENDED (no register access)\n" ); goto done;
drivers/usb/host/sl811-hcd.c +3 -3
@@ -1781,9 +1781,9 @@ sl811h_suspend(struct device *dev, pm_meif (phase !=SUSPEND_POWER_DOWN )return retval; -if (state <=PM_SUSPEND_MEM ) +if (state.event ==EVENT_FREEZE ) retval =sl811h_hub_suspend (hcd); -else +else if (state.event ==EVENT_SUSPEND )port_power (sl811,0 );if (retval ==0 ) dev->power.power_state = state;
@@ -1802,7 +1802,7 @@ sl811h_resume(struct device *dev, u32 ph/* with no "check to see if VBUS is still powered" board hook, * let's assume it'd only be powered to enable remote wakeup. */ -if (dev->power.power_state >PM_SUSPEND_MEM +if (dev->power.power_state.event >EVENT_FREEZE || !hcd->can_wakeup) { sl811->port1 =0 ;port_power (sl811,1 );
drivers/video/aty/aty128fb.c +6 -6
@@ -2329,11 +2329,11 @@ static int aty128_pci_suspend(struct pci* that. On laptops, the AGP slot is just unclocked, so D2 is * expected, while on desktops, the card is powered off */ -if (state >=3 ) - state =2 ; +if (state.event >=3 ) + state.event =2 ;#endif /* CONFIG_PPC_PMAC */ -if (state !=2 || state == pdev->dev.power.power_state) +if (state.event !=2 || state.event == pdev->dev.power.power_state.event)return 0 ;printk (KERN_DEBUG "aty128fb: suspending...\n" );
@@ -2367,7 +2367,7 @@ static int aty128_pci_suspend(struct pci* used dummy fb ops, 2.5 need proper support for this at the * fbdev level */ -if (state ==2 ) +if (state.event ==2 )aty128_set_suspend (par,1 );release_console_sem ();
@@ -2382,11 +2382,11 @@ static int aty128_do_resume(struct pci_dstruct fb_info *info =pci_get_drvdata (pdev);struct aty128fb_par *par = info->par; -if (pdev->dev.power.power_state ==0 ) +if (!pdev->dev.power.power_state.event)return 0 ;/* Wakeup chip */ -if (pdev->dev.power.power_state ==2 ) +if (pdev->dev.power.power_state.event ==2 )aty128_set_suspend (par,0 ); par->asleep =0 ;
drivers/video/aty/atyfb_base.c +5 -5
@@ -2028,11 +2028,11 @@ static int atyfb_pci_suspend(struct pci_* that. On laptops, the AGP slot is just unclocked, so D2 is * expected, while on desktops, the card is powered off */ -if (state >=3 ) - state =2 ; +if (state.event >=3 ) + state.event =2 ;#endif /* CONFIG_PPC_PMAC */ -if (state !=2 || state == pdev->dev.power.power_state) +if (state.event !=2 || state.event == pdev->dev.power.power_state.event)return 0 ;acquire_console_sem ();
@@ -2071,12 +2071,12 @@ static int atyfb_pci_resume(struct pci_dstruct fb_info *info =pci_get_drvdata (pdev);struct atyfb_par *par = (struct atyfb_par *) info->par; -if (pdev->dev.power.power_state ==0 ) +if (pdev->dev.power.power_state.event ==EVENT_ON )return 0 ;acquire_console_sem (); -if (pdev->dev.power.power_state ==2 ) +if (pdev->dev.power.power_state.event ==2 )aty_power_mgmt (0 , par); par->asleep =0 ;
drivers/video/aty/radeon_pm.c +6 -6
@@ -2526,18 +2526,18 @@ int radeonfb_pci_suspend(struct pci_devstruct radeonfb_info *rinfo = info->par;int i; -if (state == pdev->dev.power.power_state) +if (state.event == pdev->dev.power.power_state.event)return 0 ;printk (KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n" , -pci_name (pdev), state); +pci_name (pdev), state.event);/* For suspend-to-disk, we cheat here. We don't suspend anything and * let fbcon continue drawing until we are all set. That shouldn't * really cause any problem at this point, provided that the wakeup * code knows that any state in memory may not match the HW */ -if (state !=PM_SUSPEND_MEM ) +if (state.event !=EVENT_SUSPEND ) goto done;acquire_console_sem ();
@@ -2616,7 +2616,7 @@ int radeonfb_pci_resume(struct pci_dev *struct radeonfb_info *rinfo = info->par;int rc =0 ; -if (pdev->dev.power.power_state ==0 ) +if (!pdev->dev.power.power_state.event)return 0 ;if (rinfo->no_schedule) {
@@ -2626,7 +2626,7 @@ int radeonfb_pci_resume(struct pci_dev *acquire_console_sem ();printk (KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n" , -pci_name (pdev), pdev->dev.power.power_state); +pci_name (pdev), pdev->dev.power.power_state.event);if (pci_enable_device (pdev)) {
@@ -2637,7 +2637,7 @@ int radeonfb_pci_resume(struct pci_dev * }pci_set_master (pdev); -if (pdev->dev.power.power_state ==PM_SUSPEND_MEM ) { +if (pdev->dev.power.power_state.event ==EVENT_SUSPEND ) {/* Wakeup chip. Check from config space if we were powered off * (todo: additionally, check CLK_PIN_CNTL too) */
drivers/video/i810/i810_main.c +8 -14
@@ -1506,23 +1506,17 @@ static int i810fb_suspend(struct pci_devstruct i810fb_par *par = (struct i810fb_par *) info->par;int blank =0 , prev_state = par->cur_state; -if (state == prev_state) +if (state.event == prev_state)return 0 ; - par->cur_state = state; + par->cur_state = state.event; -switch (state) { -case 1 : - blank =VESA_VSYNC_SUSPEND ; -break ; -case 2 : - blank =VESA_HSYNC_SUSPEND ; -break ; -case 3 : - blank =VESA_POWERDOWN ; -break ; -default : -return -EINVAL ; +switch (state.event) { +case EVENT_SUSPEND : + blank =VESA_POWERDOWN ; +break ; +default : +return -EINVAL ; } info->fbops->fb_blank (blank, info);
drivers/video/savage/savagefb_driver.c +1 -1
@@ -2110,7 +2110,7 @@ static int savagefb_suspend (struct pci_struct savagefb_par *par = (struct savagefb_par *)info->par;DBG ("savagefb_suspend" ); -printk (KERN_DEBUG "state: %u\n" , state); +printk (KERN_DEBUG "state: %u\n" , state.event);acquire_console_sem ();fb_set_suspend (info,pci_choose_state (dev, state));
include/linux/pm.h +14 -4
@@ -186,7 +186,10 @@ extern int pm_suspend(suspend_state_t ststruct device ; -typedef u32 __bitwise pm_message_t ; +typedef struct pm_message { +int event; +int flags; +}pm_message_t ;/* * There are 4 important states driver can be in:
@@ -207,9 +210,16 @@ typedef u32 __bitwise pm_message_t;* or something similar soon. */ -#define PMSG_FREEZE ((__force pm_message_t) 3) -#define PMSG_SUSPEND ((__force pm_message_t) 3) -#define PMSG_ON ((__force pm_message_t) 0) +#define EVENT_ON 0 +#define EVENT_FREEZE 1 +#define EVENT_SUSPEND 2 + +#define PFL_RUNTIME 1 + +#define PMSG_FREEZE ({struct pm_message m; m.event = EVENT_FREEZE; m.flags = 0; m; }) +#define PMSG_SUSPEND ({struct pm_message m; m.event = EVENT_SUSPEND; m.flags = 0; m; }) +#define PMSG_ON ({struct pm_message m; m.event = EVENT_ON; m.flags = 0; m; }) +struct dev_pm_info {pm_message_t power_state;
sound/pci/atiixp.c +2 -2
@@ -1419,7 +1419,7 @@ static int snd_atiixp_suspend(snd_card_tsnd_atiixp_aclink_down (chip);snd_atiixp_chip_stop (chip); -pci_set_power_state (chip->pci,3 ); +pci_set_power_state (chip->pci, PCI_D3hot);pci_disable_device (chip->pci);return 0 ; }
@@ -1430,7 +1430,7 @@ static int snd_atiixp_resume(snd_card_tint i;pci_enable_device (chip->pci); -pci_set_power_state (chip->pci,0 ); +pci_set_power_state (chip->pci,PCI_D0 );pci_set_master (chip->pci);snd_atiixp_aclink_reset (chip);
Signature · 5 lines
Evolution. Enumerate the requirements. Consider the interdependencies. Calculate the probabilities. Be amazed that people believe it happened.