diff -ruNp 611-io.patch-old/kernel/power/suspend2_core/io.c 611-io.patch-new/kernel/power/suspend2_core/io.c --- 611-io.patch-old/kernel/power/suspend2_core/io.c 1970-01-01 10:00:00.000000000 +1000 +++ 611-io.patch-new/kernel/power/suspend2_core/io.c 2005-07-05 23:48:59.000000000 +1000 @@ -0,0 +1,1006 @@ +/* + * kernel/power/io.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. + * + * It contains high level IO routines for suspending. + * + */ + +#include <linux/suspend.h> +#include <linux/version.h> +#include <linux/utsname.h> + +#include "version.h" +#include "plugins.h" +#include "pageflags.h" +#include "io.h" +#include "ui.h" +#include "suspend2_common.h" +#include "suspend.h" + +/* attempt_to_parse_resume_device + * + * Can we suspend, using the current resume2= parameter? + */ +void attempt_to_parse_resume_device(void) +{ + struct list_head *writer; + struct suspend_plugin_ops * this_writer; + int result = 0; + + active_writer = NULL; + clear_suspend_state(SUSPEND_RESUME_DEVICE_OK); + set_suspend_state(SUSPEND_DISABLED); + CLEAR_RESULT_STATE(SUSPEND_ABORTED); + + if (!num_writers) { + printk(name_suspend "No writers have been registered. Suspending will be disabled.\n"); + return; + } + + if (!resume2_file[0]) { + printk(name_suspend "Resume2 parameter is empty. Suspending will be disabled.\n"); + return; + } + + list_for_each(writer, &suspend_writers) { + this_writer = list_entry(writer, struct suspend_plugin_ops, + ops.writer.writer_list); + + /* + * Not sure why you'd want to disable a writer, but + * we should honour the flag if we're providing it + */ + if (this_writer->disabled) { + printk(name_suspend + "Writer '%s' is disabled. Ignoring it.\n", + this_writer->name); + continue; + } + + result = this_writer->ops.writer.parse_image_location( + resume2_file, (num_writers == 1)); + + switch (result) { + case -EINVAL: + /* + * For this writer, but not a valid + * configuration. Error already printed. + */ + + return; + + case 0: + /* + * For this writer and valid. + */ + + active_writer = this_writer; + + set_suspend_state(SUSPEND_RESUME_DEVICE_OK); + clear_suspend_state(SUSPEND_DISABLED); + printk(name_suspend "Suspending enabled.\n"); + + return; + } + } + printk(name_suspend "No matching enabled writer found. Suspending disabled.\n"); +} + +/* suspend2_cleanup_finished_io + * + * Description: Very simple helper function to save #including all the + * suspend code in fs/buffer.c and anywhere else we might + * want to wait on suspend I/O in future. + */ + +void suspend2_cleanup_finished_io(void) +{ + active_writer->ops.writer.wait_on_io(0); +} + +/* noresume_reset_plugins + * + * Description: When we read the start of an image, plugins (and especially the + * active writer) might need to reset data structures if we decide + * to invalidate the image rather than resuming from it. + */ + +static void noresume_reset_plugins(void) +{ + struct suspend_plugin_ops * this_filter; + + list_for_each_entry(this_filter, &suspend_filters, ops.filter.filter_list) { + if (this_filter->ops.filter.noresume_reset) + this_filter->ops.filter.noresume_reset(); + } + + if (active_writer && active_writer->ops.writer.noresume_reset) + active_writer->ops.writer.noresume_reset(); +} + +/* fill_suspend_header() + * + * Description: Fill the suspend header structure. + * Arguments: struct suspend_header: Header data structure to be filled. + */ + +static void fill_suspend_header(struct suspend_header *sh) +{ + int i; + + memset((char *)sh, 0, sizeof(*sh)); + + sh->version_code = LINUX_VERSION_CODE; + sh->num_physpages = num_physpages; + sh->orig_mem_free = suspend2_orig_mem_free; + strncpy(sh->machine, system_utsname.machine, 65); + strncpy(sh->version, system_utsname.version, 65); + sh->num_cpus = num_online_cpus(); + sh->page_size = PAGE_SIZE; + sh->pagedir = pagedir1; + sh->pageset_2_size = pagedir2.pageset_size; + sh->param0 = suspend_result; + sh->param1 = suspend_action; + sh->param2 = suspend_debug_state; + sh->param3 = console_loglevel; + for (i = 0; i < 4; i++) + sh->io_time[i/2][i%2] = + suspend_io_time[i/2][i%2]; +} + +/* write_pageset() + * + * Description: Write a pageset to disk. + * Arguments: pagedir: Pointer to the pagedir to be saved. + * whichtowrite: Controls what debugging output is printed. + * Returns: Zero on success or -1 on failure. + */ + +int write_pageset(struct pagedir * pagedir, int whichtowrite) +{ + int nextupdate = 0, size, ret = 0, i, base = 0; + int barmax = pagedir1.pageset_size + pagedir2.pageset_size; + int start_time, end_time, pc, step = 1; + long error = 0; + struct suspend_plugin_ops * this_plugin, * first_filter = get_next_filter(NULL); + dyn_pageflags_t *pageflags; + int current_page_index = -1; + + size = pagedir->pageset_size; + if (!size) + return 0; + + if (whichtowrite == 1) { + suspend2_prepare_status(1, 0, "Writing kernel & process data..."); + base = pagedir2.pageset_size; + if (TEST_ACTION_STATE(SUSPEND_TEST_FILTER_SPEED)) + pageflags = &pageset1_map; + else + pageflags = &pageset1_copy_map; + } else { + suspend2_prepare_status(1, 1, "Writing caches..."); + pageflags = &pageset2_map; + bytes_in = bytes_out = 0; + } + + start_time = jiffies; + + /* Initialise page transformers */ + list_for_each_entry(this_plugin, &suspend_filters, ops.filter.filter_list) { + if (this_plugin->disabled) + continue; + if (this_plugin->write_init) + if (this_plugin->write_init(whichtowrite)) { + SET_RESULT_STATE(SUSPEND_ABORTED); + goto write_pageset_free_buffers; + } + } + + /* Initialise writer */ + active_writer->write_init(whichtowrite); + + /* Initialise other plugins */ + list_for_each_entry(this_plugin, &suspend_plugins, plugin_list) { + if (this_plugin->disabled) + continue; + if ((this_plugin->type == FILTER_PLUGIN) || + (this_plugin->type == WRITER_PLUGIN)) + continue; + if (this_plugin->write_init) + if (this_plugin->write_init(whichtowrite)) { + SET_RESULT_STATE(SUSPEND_ABORTED); + goto write_pageset_free_buffers; + } + } + + current_page_index = __get_next_bit_on(*pageflags, -1); + + pc = size / 5; + + /* Write the data */ + for (i=0; i<size; i++) { + int was_mapped = 0; + struct page * page = pfn_to_page(current_page_index); + + /* Status update */ + if ((i+base) >= nextupdate) + nextupdate = suspend2_update_status(i + base, barmax, + " %d/%d MB ", MB(base+i+1), MB(barmax)); + + if ((i + 1) == pc) { + printk("%d%%...", 20 * step); + step++; + pc = size * step / 5; + } + + /* Write */ + was_mapped = suspend_map_kernel_page(page, 1); + ret = first_filter->ops.filter.write_chunk(page); + if (!was_mapped) + suspend_map_kernel_page(page, 0); + + if (ret) { + printk("Write chunk returned %d.\n", ret); + abort_suspend("Failed to write a chunk of the " + "image."); + error = -1; + goto write_pageset_free_buffers; + } + + /* Interactivity */ + check_shift_keys(0, NULL); + + if (TEST_RESULT_STATE(SUSPEND_ABORTED)) { + abort_suspend("Aborting as requested."); + error = -1; + goto write_pageset_free_buffers; + } + + /* Prepare next */ + current_page_index = __get_next_bit_on(*pageflags, current_page_index); + } + + printk("done.\n"); + + suspend2_update_status(base+size, barmax, " %d/%d MB ", + MB(base+size), MB(barmax)); + +write_pageset_free_buffers: + + /* Cleanup other plugins */ + list_for_each_entry(this_plugin, &suspend_plugins, plugin_list) { + if (this_plugin->disabled) + continue; + if ((this_plugin->type == FILTER_PLUGIN) || + (this_plugin->type == WRITER_PLUGIN)) + continue; + if (this_plugin->write_cleanup) + this_plugin->write_cleanup(); + } + + /* Flush data and cleanup */ + list_for_each_entry(this_plugin, &suspend_filters, ops.filter.filter_list) { + if (this_plugin->disabled) + continue; + if (this_plugin->write_cleanup) + this_plugin->write_cleanup(); + } + active_writer->write_cleanup(); + + /* Statistics */ + end_time = jiffies; + + if ((end_time - start_time) && (!TEST_RESULT_STATE(SUSPEND_ABORTED))) { + suspend_io_time[0][0] += size, + suspend_io_time[0][1] += (end_time - start_time); + } + + return error; +} + +/* read_pageset() + * + * Description: Read a pageset from disk. + * Arguments: pagedir: Pointer to the pagedir to be saved. + * whichtowrite: Controls what debugging output is printed. + * overwrittenpagesonly: Whether to read the whole pageset or + * only part. + * Returns: Zero on success or -1 on failure. + */ + +static int read_pageset(struct pagedir * pagedir, int whichtoread, + int overwrittenpagesonly) +{ + int nextupdate = 0, result = 0, base = 0; + int start_time, end_time, finish_at = pagedir->pageset_size; + int barmax = pagedir1.pageset_size + pagedir2.pageset_size; + int i, pc, step = 1; + struct suspend_plugin_ops * this_plugin, * first_filter = get_next_filter(NULL); + dyn_pageflags_t *pageflags; + int current_page_index; + + if (whichtoread == 1) { + suspend2_prepare_status(1, 1, "Reading kernel & process data..."); + pageflags = &pageset1_copy_map; + } else { + suspend2_prepare_status(1, 0, "Reading caches..."); + if (overwrittenpagesonly) + barmax = finish_at = min(pageset1_size, pageset2_size); + else { + base = pagedir1.pageset_size; + } + pageflags = &pageset2_map; + } + + start_time=jiffies; + + /* Initialise page transformers */ + list_for_each_entry(this_plugin, &suspend_filters, ops.filter.filter_list) { + if (this_plugin->disabled) + continue; + if (this_plugin->read_init && + this_plugin->read_init(whichtoread)) { + abort_suspend("Failed to initialise a filter."); + result = 1; + goto read_pageset_free_buffers; + } + } + + /* Initialise writer */ + if (active_writer->read_init(whichtoread)) { + abort_suspend("Failed to initialise the writer."); + result = 1; + goto read_pageset_free_buffers; + } + + /* Initialise other plugins */ + list_for_each_entry(this_plugin, &suspend_plugins, plugin_list) { + if (this_plugin->disabled) + continue; + if ((this_plugin->type == FILTER_PLUGIN) || + (this_plugin->type == WRITER_PLUGIN)) + continue; + if (this_plugin->read_init) + if (this_plugin->read_init(whichtoread)) { + SET_RESULT_STATE(SUSPEND_ABORTED); + goto read_pageset_free_buffers; + } + } + + current_page_index = __get_next_bit_on(*pageflags, -1); + + pc = finish_at / 5; + + /* Read the pages */ + for (i=0; i< finish_at; i++) { + int was_mapped = 0; + struct page * page = pfn_to_page(current_page_index); + + /* Status */ + if ((i+base) >= nextupdate) + nextupdate = suspend2_update_status(i+base, barmax, + " %d/%d MB ", MB(base+i+1), MB(barmax)); + + if ((i + 1) == pc) { + printk("%d%%...", 20 * step); + step++; + pc = finish_at * step / 5; + } + + was_mapped = suspend_map_kernel_page(page, 1); + result = first_filter->ops.filter.read_chunk(page, SUSPEND_ASYNC); + if (!was_mapped) + suspend_map_kernel_page(page, 0); + + if (result) { + panic("Failed to read chunk %d/%d of the image. (%d)", + i, finish_at, result); + goto read_pageset_free_buffers; + } + + /* Interactivity*/ + check_shift_keys(0, NULL); + + /* Prepare next */ + current_page_index = __get_next_bit_on(*pageflags, current_page_index); + } + + printk("done.\n"); + + suspend2_update_status(base+finish_at, barmax, " %d/%d MB ", + MB(base+finish_at), MB(barmax)); + +read_pageset_free_buffers: + + /* Cleanup other plugins */ + list_for_each_entry(this_plugin, &suspend_plugins, plugin_list) { + if (this_plugin->disabled) + continue; + if ((this_plugin->type == FILTER_PLUGIN) || + (this_plugin->type == WRITER_PLUGIN)) + continue; + if (this_plugin->read_cleanup) + this_plugin->read_cleanup(); + } + + /* Finish I/O, flush data and cleanup reads. */ + list_for_each_entry(this_plugin, &suspend_filters, ops.filter.filter_list) { + if (this_plugin->disabled) + continue; + if (this_plugin->read_cleanup && + this_plugin->read_cleanup()) { + abort_suspend("Failed to cleanup a filter."); + result = 1; + } + } + + if (active_writer->read_cleanup()) { + abort_suspend("Failed to cleanup the writer."); + result = 1; + } + + /* Statistics */ + end_time=jiffies; + if ((end_time - start_time) && (!TEST_RESULT_STATE(SUSPEND_ABORTED))) { + suspend_io_time[1][0] += finish_at, + suspend_io_time[1][1] += (end_time - start_time); + } + + return result; +} + +/* write_plugin_configs() + * + * Description: Store the configuration for each plugin in the image header. + * Returns: Int: Zero on success, Error value otherwise. + */ +static int write_plugin_configs(void) +{ + struct suspend_plugin_ops * this_plugin; + char * buffer = (char *) get_zeroed_page(GFP_ATOMIC); + int len, index = 1; + struct plugin_header plugin_header; + + if (!buffer) { + printk("Failed to allocate a buffer for saving " + "plugin configuration info.\n"); + return -ENOMEM; + } + + /* + * We have to know which data goes with which plugin, so we at + * least write a length of zero for a plugin. Note that we are + * also assuming every plugin's config data takes <= PAGE_SIZE. + */ + + /* For each plugin (in registration order) */ + list_for_each_entry(this_plugin, &suspend_plugins, plugin_list) { + + /* Get the data from the plugin */ + len = 0; + if (this_plugin->save_config_info) + len = this_plugin->save_config_info(buffer); + + /* Save the details of the plugin */ + plugin_header.disabled = this_plugin->disabled; + plugin_header.type = this_plugin->type; + plugin_header.index = index++; + strncpy(plugin_header.name, this_plugin->name, + sizeof(plugin_header.name)); + active_writer->ops.writer.write_header_chunk( + (char *) &plugin_header, + sizeof(plugin_header)); + + /* Save the size of the data and any data returned */ + active_writer->ops.writer.write_header_chunk((char *) &len, + sizeof(int)); + if (len) + active_writer->ops.writer.write_header_chunk( + buffer, len); + } + + /* Write a blank header to terminate the list */ + plugin_header.name[0] = '\0'; + active_writer->ops.writer.write_header_chunk( + (char *) &plugin_header, + sizeof(plugin_header)); + + free_pages((unsigned long) buffer, 0); + return 0; +} + +/* read_plugin_configs() + * + * Description: Reload plugin configurations from the image header. + * Returns: Int. Zero on success, error value otherwise. + */ + +static int read_plugin_configs(void) +{ + struct suspend_plugin_ops * this_plugin; + char * buffer = (char *) get_zeroed_page(GFP_ATOMIC); + int len, result = 0; + struct plugin_header plugin_header; + + if (!buffer) { + printk("Failed to allocate a buffer for reloading plugin " + "configuration info.\n"); + return -ENOMEM; + } + + /* All plugins are initially disabled. That way, if we have a plugin + * loaded now that wasn't loaded when we suspended, it won't be used + * in trying to read the data. + */ + list_for_each_entry(this_plugin, &suspend_plugins, plugin_list) + this_plugin->disabled = 1; + + /* Get the first plugin header */ + result = active_writer->ops.writer.read_header_chunk( + (char *) &plugin_header, sizeof(plugin_header)); + if (!result) { + printk("Failed to read the next plugin header.\n"); + free_pages((unsigned long) buffer, 0); + return -EINVAL; + } + + /* For each plugin (in registration order) */ + while (plugin_header.name[0]) { + + /* Find the plugin */ + this_plugin = find_plugin_given_name(plugin_header.name); + + if (!this_plugin) { + /* + * Is it used? Only need to worry about filters. The active + * writer must be loaded! + */ + if ((!plugin_header.disabled) && + (plugin_header.type == FILTER_PLUGIN)) { + suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "It looks like we need plugin %s for " + "reading the image but it hasn't been " + "registered.\n", + plugin_header.name); + if (!(test_suspend_state(SUSPEND_CONTINUE_REQ))) { + active_writer->ops.writer.invalidate_image(); + result = -EINVAL; + noresume_reset_plugins(); + free_pages((unsigned long) buffer, 0); + return -EINVAL; + } + } else + printk("Plugin %s configuration data found, but the plugin " + "hasn't registered. Looks like it was disabled, so " + "we're ignoring it's data.", + plugin_header.name); + } + + /* Get the length of the data (if any) */ + result = active_writer->ops.writer.read_header_chunk( + (char *) &len, sizeof(int)); + if (!result) { + printk("Failed to read the length of the plugin %s's" + " configuration data.\n", + plugin_header.name); + free_pages((unsigned long) buffer, 0); + return -EINVAL; + } + + /* Read any data and pass to the plugin (if we found one) */ + if (len) { + active_writer->ops.writer.read_header_chunk(buffer, len); + if (this_plugin) { + if (!this_plugin->save_config_info) { + printk("Huh? Plugin %s appears to have a " + "save_config_info, but not a " + "load_config_info function!\n", + this_plugin->name); + } else + this_plugin->load_config_info(buffer, len); + } + } + + if (this_plugin) { + /* Now move this plugin to the tail of its lists. This will put it + * in order. Any new plugins will end up at the top of the lists. + * They should have been set to disabled when loaded (people will + * normally not edit an initrd to load a new module and then + * suspend without using it!). + */ + + suspend_move_plugin_tail(this_plugin); + + /* + * We apply the disabled state; plugins don't need to save whether they + * were disabled and if they do, we override them anyway. + */ + this_plugin->disabled = plugin_header.disabled; + } + + /* Get the next plugin header */ + result = active_writer->ops.writer.read_header_chunk( + (char *) &plugin_header, sizeof(plugin_header)); + + if (!result) { + printk("Failed to read the next plugin header.\n"); + free_pages((unsigned long) buffer, 0); + return -EINVAL; + } + + } + + free_pages((unsigned long) buffer, 0); + return 0; +} + +/* write_image_header() + * + * Description: Write the image header after write the image proper. + * Returns: Int. Zero on success or -1 on failure. + */ + +int write_image_header(void) +{ + int ret; + int total = pagedir1.pageset_size + pagedir2.pageset_size+2; + char * header_buffer = NULL; + + /* Now prepare to write the header */ + if ((ret = active_writer->ops.writer.write_header_init())) { + abort_suspend("Active writer's write_header_init" + " function failed."); + goto write_image_header_abort; + } + + /* Get a buffer */ + header_buffer = (char *) get_zeroed_page(GFP_ATOMIC); + if (!header_buffer) { + abort_suspend("Out of memory when trying to get page " + "for header!"); + goto write_image_header_abort; + } + + /* Write suspend header */ + fill_suspend_header((struct suspend_header *) header_buffer); + active_writer->ops.writer.write_header_chunk(header_buffer, + sizeof(struct suspend_header)); + + free_pages((unsigned long) header_buffer, 0); + + /* Write plugin configurations */ + if ((ret = write_plugin_configs())) { + abort_suspend("Failed to write plugin configs."); + goto write_image_header_abort; + } + + save_dyn_pageflags(pageset1_map); + + if ((ret = active_writer->ops.writer.serialise_extents())) { + abort_suspend("Active writer's prepare_save_extents " + "function failed."); + goto write_image_header_abort; + } + + /* Flush data and let writer cleanup */ + if (active_writer->ops.writer.write_header_cleanup()) { + abort_suspend("Failed to cleanup writing header."); + goto write_image_header_abort_no_cleanup; + } + + if (TEST_RESULT_STATE(SUSPEND_ABORTED)) + goto write_image_header_abort_no_cleanup; + + suspend_message(SUSPEND_IO, SUSPEND_VERBOSE, 1, "|\n"); + suspend2_update_status(total, total, NULL); + + return 0; + +write_image_header_abort: + active_writer->ops.writer.write_header_cleanup(); +write_image_header_abort_no_cleanup: + return -1; +} + +/* sanity_check() + * + * Description: Perform a few checks, seeking to ensure that the kernel being + * booted matches the one suspended. They need to match so we can + * be _sure_ things will work. It is not absolutely impossible for + * resuming from a different kernel to work, just not assured. + * Arguments: Struct suspend_header. The header which was saved at suspend + * time. + */ +static int sanity_check(struct suspend_header *sh) +{ + if (sh->version_code != LINUX_VERSION_CODE) + return suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "Incorrect kernel version"); + + if (sh->num_physpages != num_physpages) + return suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "Incorrect memory size"); + + if (strncmp(sh->machine, system_utsname.machine, 65)) + return suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "Incorrect machine type"); + + if (strncmp(sh->version, system_utsname.version, 65)) + return suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "Incorrect version"); + + if (sh->num_cpus != num_online_cpus()) + return suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "Incorrect number of cpus"); + + if (sh->page_size != PAGE_SIZE) + return suspend_early_boot_message(1, SUSPEND_CONTINUE_REQ, + "Incorrect PAGE_SIZE"); + + return 0; +} + +/* __read_pageset1 + * + * Description: Test for the existence of an image and attempt to load it. + * Returns: Int. Zero if image found and pageset1 successfully loaded. + * Error if no image found or loaded. + */ +static int __read_pageset1(void) +{ + int i, result = 0; + char * header_buffer = (char *) get_zeroed_page(GFP_ATOMIC); + struct suspend_header * suspend_header; + + if (!header_buffer) + return -ENOMEM; + + /* Check for an image */ + if (!(result = active_writer->ops.writer.image_exists())) { + result = -ENODATA; + noresume_reset_plugins(); + goto out; + } + + /* Check for noresume command line option */ + if (test_suspend_state(SUSPEND_NORESUME_SPECIFIED)) { + active_writer->ops.writer.invalidate_image(); + result = -EINVAL; + noresume_reset_plugins(); + goto out; + } + +#ifdef CONFIG_SOFTWARE_SUSPEND_CHECK_RESUME_SAFE + /* Check whether we've got filesystems mounted that make + * resuming unsafe */ + + suspend_check_mounts(); + + if (!test_suspend_state(SUSPEND_CONTINUE_REQ)) { + active_writer->ops.writer.invalidate_image(); + result = -EINVAL; + noresume_reset_plugins(); + goto out; + } + + clear_suspend_state(SUSPEND_CONTINUE_REQ); +#endif + + /* Check whether we've resumed before */ + if (test_suspend_state(SUSPEND_RESUMED_BEFORE)) { + int resumed_before_default = 0; + if (test_suspend_state(SUSPEND_RETRY_RESUME)) + resumed_before_default = SUSPEND_CONTINUE_REQ; + suspend_early_boot_message(1, resumed_before_default, NULL); + clear_suspend_state(SUSPEND_RETRY_RESUME); + if (!(test_suspend_state(SUSPEND_CONTINUE_REQ))) { + active_writer->ops.writer.invalidate_image(); + result = -EINVAL; + noresume_reset_plugins(); + goto out; + } + } + + clear_suspend_state(SUSPEND_CONTINUE_REQ); + + /* + * Prepare the active writer for reading the image header. The + * activate writer might read its own configuration or set up + * a network connection here. + * + * NB: This call may never return because there might be a signature + * for a different image such that we warn the user and they choose + * to reboot. (If the device ids look erroneous (2.4 vs 2.6) or the + * location of the image might be unavailable if it was stored on a + * network connection. + */ + + if ((result = active_writer->ops.writer.read_header_init())) { + noresume_reset_plugins(); + goto out; + } + + /* Read suspend header */ + if ((result = active_writer->ops.writer.read_header_chunk( + header_buffer, sizeof(struct suspend_header))) < 0) { + noresume_reset_plugins(); + goto out; + } + + suspend_header = (struct suspend_header *) header_buffer; + + /* + * NB: This call may also result in a reboot rather than returning. + */ + + if (sanity_check(suspend_header)) { /* Is this the same machine? */ + active_writer->ops.writer.invalidate_image(); + result = -EINVAL; + noresume_reset_plugins(); + goto out; + } + + /* + * ---------------------------------------------------- + * We have an image and it looks like it will load okay. + * ---------------------------------------------------- + */ + + /* Get metadata from header. Don't override commandline parameters. + * + * We don't need to save the image size limit because it's not used + * during resume and will be restored with the image anyway. + */ + + suspend2_orig_mem_free = suspend_header->orig_mem_free; + memcpy((char *) &pagedir1, + (char *) &suspend_header->pagedir, sizeof(pagedir1)); + suspend_result = suspend_header->param0; + if (!test_suspend_state(SUSPEND_ACT_USED)) + suspend_action = suspend_header->param1; + if (!test_suspend_state(SUSPEND_DBG_USED)) + suspend_debug_state = suspend_header->param2; + if (!test_suspend_state(SUSPEND_LVL_USED)) + suspend_default_console_level = suspend_header->param3; + clear_suspend_state(SUSPEND_IGNORE_LOGLEVEL); + pagedir2.pageset_size = suspend_header->pageset_2_size; + for (i = 0; i < 4; i++) + suspend_io_time[i/2][i%2] = + suspend_header->io_time[i/2][i%2]; + + set_suspend_state(SUSPEND_NOW_RESUMING); + + /* Read plugin configurations */ + if ((result = read_plugin_configs())) { + noresume_reset_plugins(); + pagedir1.pageset_size = + pagedir2.pageset_size = 0; + goto out; + } + + suspend2_prepare_console(); + + check_shift_keys(1, "About to read original pageset1 locations."); + /* Read original pageset1 locations. These are the addresses we can't use for + * the data to be restored */ + suspend_allocate_dyn_pageflags(&pageset1_map); + load_dyn_pageflags(pageset1_map); + + /* Relocate it so that it's not overwritten while we're using it to + * copy the original contents back */ + relocate_dyn_pageflags(&pageset1_map); + + suspend_allocate_dyn_pageflags(&pageset1_copy_map); + relocate_dyn_pageflags(&pageset1_copy_map); + + /* Read extent pages */ + if ((result = active_writer->ops.writer.load_extents())) { + noresume_reset_plugins(); + abort_suspend("Active writer's load_extents " + "function failed."); + goto out; + } + + /* Clean up after reading the header */ + if ((result = active_writer->ops.writer.read_header_cleanup())) { + noresume_reset_plugins(); + goto out; + } + + check_shift_keys(1, "About to read pagedir."); + + /* + * Get the addresses of pages into which we will load the kernel to + * be copied back + */ + if (suspend2_get_pageset1_load_addresses()) { + result = -ENOMEM; + noresume_reset_plugins(); + goto out; + } + + /* Read the original kernel back */ + check_shift_keys(1, "About to read pageset 1."); + + if (read_pageset(&pagedir1, 1, 0)) { + suspend2_prepare_status(1, 1, "Failed to read pageset 1."); + result = -EPERM; + noresume_reset_plugins(); + goto out; + } + + check_shift_keys(1, "About to restore original kernel."); + result = 0; + + if (active_writer->ops.writer.mark_resume_attempted) + active_writer->ops.writer.mark_resume_attempted(); + +out: + free_pages((unsigned long) header_buffer, 0); + return result; +} + +/* read_pageset1() + * + * Description: Attempt to read the header and pageset1 of a suspend image. + * Handle the outcome, complaining where appropriate. + */ +int read_pageset1(void) +{ + int error; + + error = __read_pageset1(); + + switch (error) { + case 0: + case -ENODATA: + case -EINVAL: /* non fatal error */ + return error; + case -EIO: + printk(KERN_CRIT name_suspend "I/O error\n"); + break; + case -ENOENT: + printk(KERN_CRIT name_suspend "No such file or directory\n"); + break; + case -EPERM: + printk(KERN_CRIT name_suspend "Sanity check error\n"); + break; + default: + printk(KERN_CRIT name_suspend "Error %d resuming\n", error); + break; + } + abort_suspend("Error %d in read_pageset1",error); + return error; +} + +/* read_pageset2() + * + * Description: Read in part or all of pageset2 of an image, depending upon + * whether we are suspending and have only overwritten a portion + * with pageset1 pages, or are resuming and need to read them + * all. + * Arguments: Int. Boolean. Read only pages which would have been + * overwritten by pageset1? + * Returns: Int. Zero if no error, otherwise the error value. + */ +int read_pageset2(int overwrittenpagesonly) +{ + int result = 0; + + if (!pageset2_size) + return 0; + + result = read_pageset(&pagedir2, 2, overwrittenpagesonly); + + suspend2_update_status(100, 100, NULL); + check_shift_keys(1, "Pagedir 2 read."); + + return result; +} diff -ruNp 611-io.patch-old/kernel/power/suspend2_core/io.h 611-io.patch-new/kernel/power/suspend2_core/io.h --- 611-io.patch-old/kernel/power/suspend2_core/io.h 1970-01-01 10:00:00.000000000 +1000 +++ 611-io.patch-new/kernel/power/suspend2_core/io.h 2005-07-04 23:14:19.000000000 +1000 @@ -0,0 +1,44 @@ +/* + * kernel/power/io.h + */ + +#include "pagedir.h" + +/* Non-plugin data saved in our image header */ +struct suspend_header { + u32 version_code; + unsigned long num_physpages; + unsigned long orig_mem_free; + char machine[65]; + char version[65]; + int num_cpus; + int page_size; + int pageset_2_size; + int param0; + int param1; + int param2; + int param3; + int progress0; + int progress1; + int progress2; + int progress3; + int io_time[2][2]; + + /* Implementation specific variables */ +#ifdef KERNEL_POWER_SWSUSP_C + suspend_pagedir_t *suspend_pagedir; + unsigned int num_pbes; +#else + struct pagedir pagedir; +#endif +}; + +extern int write_pageset(struct pagedir * pagedir, int whichtowrite); +extern int write_image_header(void); +extern int read_pageset1(void); +extern int read_pageset2(int overwrittenpagesonly); + +extern void attempt_to_parse_resume_device(void); +extern dev_t name_to_dev_t(char *line); +extern __nosavedata unsigned long bytes_in, bytes_out; +
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/