diff -ruNp 616-prepare_image.patch-old/kernel/power/suspend2_core/prepare_image.c 616-prepare_image.patch-new/kernel/power/suspend2_core/prepare_image.c --- 616-prepare_image.patch-old/kernel/power/suspend2_core/prepare_image.c 1970-01-01 10:00:00.000000000 +1000 +++ 616-prepare_image.patch-new/kernel/power/suspend2_core/prepare_image.c 2005-07-04 23:14:19.000000000 +1000 @@ -0,0 +1,585 @@ +/* + * kernel/power/prepare_image.c + * + * Copyright (C) 2003-2005 Nigel Cunningham <[EMAIL PROTECTED]> + * + * This file is released under the GPLv2. + * + * We need to eat memory until we can: + * 1. Perform the save without changing anything (RAM_NEEDED < max_mapnr) + * 2. Fit it all in available space (active_writer->available_space() >= STORAGE_NEEDED) + * 3. Reload the pagedir and pageset1 to places that don't collide with their + * final destinations, not knowing to what extent the resumed kernel will + * overlap with the one loaded at boot time. I think the resumed kernel should overlap + * completely, but I don't want to rely on this as it is an unproven assumption. We + * therefore assume there will be no overlap at all (worse case). + * 4. Meet the user's requested limit (if any) on the size of the image. + * The limit is in MB, so pages/256 (assuming 4K pages). + * + * (Final test in save_image doesn't use EATEN_ENOUGH_MEMORY) + */ + +#include <linux/highmem.h> + +#include "suspend.h" +#include "pageflags.h" +#include "plugins.h" +#include "suspend2_common.h" +#include "io.h" +#include "ui.h" +#include "extent.h" +#include "prepare_image.h" + +#define EATEN_ENOUGH_MEMORY() (amount_needed(1) < 1) +static int arefrozen = 0, numnosave = 0; +static int header_space_allocated = 0; +static int storage_allocated = 0; +static int storage_available = 0; + +static int num_pcp_pages(void) +{ + struct zone *zone; + int result = 0, i = 0; + + /* PCP lists */ + for_each_zone(zone) { + struct per_cpu_pageset *pset; + int cpu; + + if (!zone->present_pages) + continue; + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (!cpu_possible(cpu)) + continue; + + pset = &zone->pageset[cpu]; + + for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) { + struct per_cpu_pages *pcp; + + pcp = &pset->pcp[i]; + result += pcp->count; + } + } + } + return result; +} + +int real_nr_free_pages(void) +{ + return nr_free_pages() + num_pcp_pages(); +} + +/* generate_free_page_map + * + * Description: This routine generates a bitmap of free pages from the + * lists used by the memory manager. We then use the bitmap + * to quickly calculate which pages to save and in which + * pagesets. + */ +static void generate_free_page_map(void) +{ + int i, order, loop, cpu; + struct page * page; + unsigned long flags; + struct zone *zone; + struct per_cpu_pageset *pset; + + for(i=0; i < max_mapnr; i++) + SetPageInUse(pfn_to_page(i)); + + for_each_zone(zone) { + if (!zone->present_pages) + continue; + spin_lock_irqsave(&zone->lock, flags); + for (order = MAX_ORDER - 1; order >= 0; --order) { + list_for_each_entry(page, &zone->free_area[order].free_list, lru) + for(loop=0; loop < (1 << order); loop++) { + ClearPageInUse(page+loop); + ClearPagePageset2(page+loop); + } + } + + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (!cpu_possible(cpu)) + continue; + + pset = &zone->pageset[cpu]; + + for (i = 0; i < ARRAY_SIZE(pset->pcp); i++) { + struct per_cpu_pages *pcp; + struct page * page; + + pcp = &pset->pcp[i]; + list_for_each_entry(page, &pcp->list, lru) { + ClearPageInUse(page); + ClearPagePageset2(page); + } + } + } + + spin_unlock_irqrestore(&zone->lock, flags); + } +} + +/* size_of_free_region + * + * Description: Return the number of pages that are free, beginning with and + * including this one. + */ +static int size_of_free_region(struct page * page) +{ + struct page * posn = page; + + while (((page_to_pfn(posn)) < max_mapnr) && (!PageInUse(posn))) + posn++; + return (posn - page); +} + +/* count_data_pages + * + * This routine generates our lists of pages to be stored in each + * pageset. Since we store the data using extents, and adding new + * extents might allocate a new extent page, this routine may well + * be called more than once. + */ +static struct pageset_sizes_result count_data_pages(void) +{ + int chunk_size, loop, numfree = 0; + int usepagedir2; + struct pageset_sizes_result result; + + result.size1 = 0; + result.size1low = 0; + result.size2 = 0; + result.size2low = 0; + + numnosave = 0; + + clear_dyn_pageflags(pageset1_map); + clear_dyn_pageflags(pageset1_copy_map); + + generate_free_page_map(); + + if (TEST_RESULT_STATE(SUSPEND_ABORTED)) + return result; + + if (max_mapnr != num_physpages) { + abort_suspend("Max_mapnr is not equal to num_physpages."); + return result; + } + + /* + * Pages not to be saved are marked Nosave irrespective of being reserved + */ + for (loop = 0; loop < max_mapnr; loop++) { + struct page * page = pfn_to_page(loop); + if (PageNosave(page)) { + numnosave++; + continue; + } + + if (!PageReserved(page)) { + if ((chunk_size=size_of_free_region(page))!=0) { + numfree += chunk_size; + loop += chunk_size - 1; + continue; + } + } else { + if (PageHighMem(page)) { + /* HighMem pages may be marked Reserved. We ignore them. */ + numnosave++; + continue; + } + }; + + usepagedir2 = PagePageset2(page); + + if (usepagedir2) { + result.size2++; + if (!PageHighMem(page)) + result.size2low++; + SetPagePageset1Copy(page); + } else { + result.size1++; + SetPagePageset1(page); + if (!PageHighMem(page)) + result.size1low++; + } + } + + suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_MEDIUM, 0, + "Count data pages: Set1 (%d) + Set2 (%d) + Nosave (%d) + NumFree (%d) = %d.\n", + result.size1, result.size2, numnosave, numfree, + result.size1 + result.size2 + numnosave + numfree); + BITMAP_FOR_EACH_SET(allocd_pages_map, loop) + SetPagePageset1Copy(pfn_to_page(loop)); + return result; +} + +/* amount_needed + * + * Calculates the amount by which the image size needs to be reduced to meet + * our constraints. + */ +static int amount_needed(int use_image_size_limit) +{ + + int max1 = max( (int) (RAM_TO_SUSPEND - real_nr_free_pages() - + nr_free_highpages()), + ((int) (STORAGE_NEEDED(1) - + storage_available))); + if (use_image_size_limit) + return max( max1, + (image_size_limit > 0) ? + ((int) (STORAGE_NEEDED(1) - (image_size_limit << 8))) : 0); + return max1; +} + +/* display_stats + * + * Display the vital statistics.of the image. + */ +#ifdef CONFIG_PM_DEBUG +static void display_stats(void) +{ + storage_allocated = active_writer->ops.writer.storage_allocated(); + suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_MEDIUM, 1, + "Free:%d(%d). Sets:%d(%d),%d(%d). Nosave:%d-%d=%d. Storage:%d/%d+%d=%d(%lu). Needed:%d|%d|%d.\n", + + /* Free */ + real_nr_free_pages(), + real_nr_free_pages() - nr_free_highpages(), + + /* Sets */ + pageset1_size, pageset1_sizelow, + pageset2_size, pageset2_sizelow, + + /* Nosave */ + numnosave, extra_pagedir_pages_allocated, + numnosave - extra_pagedir_pages_allocated, + + /* Storage */ + storage_allocated, + MAIN_STORAGE_NEEDED(1), HEADER_STORAGE_NEEDED, + STORAGE_NEEDED(1), + storage_available, + + /* Needed */ + RAM_TO_SUSPEND - real_nr_free_pages() - nr_free_highpages(), + STORAGE_NEEDED(1) - storage_available, + (image_size_limit > 0) ? (STORAGE_NEEDED(1) - (image_size_limit << 8)) : 0); +} +#else +#define display_stats() do { } while(0) +#endif + +/* suspend2_recalculate_stats + * + * Eaten is the number of pages which have been eaten. + * Pagedirincluded is the number of pages which have been allocated for the pagedir. + */ +struct pageset_sizes_result suspend2_recalculate_stats(void) +{ + struct pageset_sizes_result result; + + suspend2_mark_pages_for_pageset2(); /* Need to call this before getting pageset1_size! */ + result = count_data_pages(); + pageset1_sizelow = result.size1low; + pageset2_sizelow = result.size2low; + pagedir1.lastpageset_size = pageset1_size = result.size1; + pagedir2.lastpageset_size = pageset2_size = result.size2; + storage_available = active_writer->ops.writer.storage_available(); + return result; +} + +/* update_image + * + * Allocate [more] memory and storage for the image. + */ +static int update_image(void) +{ + struct pageset_sizes_result result; + int result2, param_used; + + result = suspend2_recalculate_stats(); + + /* Include allowance for growth in pagedir1 while writing pagedir 2 */ + if (suspend2_allocate_extra_pagedir_memory(&pagedir1, + pageset1_size + EXTRA_PD1_PAGES_ALLOWANCE, + pageset2_sizelow)) { + suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1, + "Still need to get more pages for pagedir 1.\n"); + return 1; + } + + thaw_processes(FREEZER_KERNEL_THREADS); + + param_used = MAIN_STORAGE_NEEDED(1); + if ((result2 = active_writer->ops.writer.allocate_storage(param_used))) { + suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1, + "Still need to get more storage space for the image proper.\n"); + storage_allocated = active_writer->ops.writer.storage_allocated(); + freeze_processes(1); + return 1; + } + + param_used = HEADER_STORAGE_NEEDED; + if ((result2 = active_writer->ops.writer.allocate_header_space(HEADER_STORAGE_NEEDED))) { + suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1, + "Still need to get more storage space for header.\n"); + freeze_processes(1); + storage_allocated = active_writer->ops.writer.storage_allocated(); + return 1; + } + + header_space_allocated = HEADER_STORAGE_NEEDED; + + /* + * Allocate remaining storage space, if possible, up to the + * maximum we know we'll need. It's okay to allocate the + * maximum if the writer is the swapwriter, but + * we don't want to grab all available space on an NFS share. + * We therefore ignore the expected compression ratio here, + * thereby trying to allocate the maximum image size we could + * need (assuming compression doesn't expand the image), but + * don't complain if we can't get the full amount we're after. + */ + + active_writer->ops.writer.allocate_storage( + min(storage_available, + MAIN_STORAGE_NEEDED(0) + 100)); + + storage_allocated = active_writer->ops.writer.storage_allocated(); + + freeze_processes(1); + + suspend2_recalculate_stats(); + display_stats(); + + suspend_message(SUSPEND_EAT_MEMORY, SUSPEND_LOW, 1, + "Amount still needed (%d) > 0:%d. Header: %d < %d: %d," + " Storage allocd: %d < %d + %d: %d.\n", + amount_needed(0), + (amount_needed(0) > 0), + header_space_allocated, HEADER_STORAGE_NEEDED, + header_space_allocated < HEADER_STORAGE_NEEDED, + storage_allocated, + HEADER_STORAGE_NEEDED, MAIN_STORAGE_NEEDED(1), + storage_allocated < + (HEADER_STORAGE_NEEDED + MAIN_STORAGE_NEEDED(1))); + + check_shift_keys(0, NULL); + + return ((amount_needed(0) > 0) || + header_space_allocated < HEADER_STORAGE_NEEDED || + storage_allocated < + (HEADER_STORAGE_NEEDED + MAIN_STORAGE_NEEDED(1))); +} + +/* --------------------------------------------------------------------------- */ + +/* attempt_to_freeze + * + * Try to freeze processes. + */ + +static int attempt_to_freeze(void) +{ + int result; + + /* Stop processes before checking again */ + thaw_processes(FREEZER_ALL_THREADS); + suspend2_prepare_status(1, 1, "Freezing processes"); + result = freeze_processes(0); + + if (result) { + SET_RESULT_STATE(SUSPEND_ABORTED); + SET_RESULT_STATE(SUSPEND_FREEZING_FAILED); + } else + arefrozen = 1; + + return result; +} + +/* eat_memory + * + * Try to free some memory, either to meet hard or soft constraints on the image + * characteristics. + * + * Hard constraints: + * - Pageset1 must be < half of memory; + * - We must have enough memory free at resume time to have pageset1 + * be able to be loaded in pages that don't conflict with where it has to + * be restored. + * Soft constraints + * - User specificied image size limit. + */ +static int eat_memory(void) +{ + int orig_memory_still_to_eat, last_amount_needed = 0, times_criteria_met = 0; + int free_flags = 0, did_eat_memory = 0; + + /* + * Note that if we have enough storage space and enough free memory, we may + * exit without eating anything. We give up when the last 10 iterations ate + * no extra pages because we're not going to get much more anyway, but + * the few pages we get will take a lot of time. + * + * We freeze processes before beginning, and then unfreeze them if we + * need to eat memory until we think we have enough. If our attempts + * to freeze fail, we give up and abort. + */ + + /* ----------- Stage 1: Freeze Processes ------------- */ + + + suspend2_recalculate_stats(); + display_stats(); + + orig_memory_still_to_eat = amount_needed(1); + last_amount_needed = orig_memory_still_to_eat; + + switch (image_size_limit) { + case -1: /* Don't eat any memory */ + if (orig_memory_still_to_eat) { + SET_RESULT_STATE(SUSPEND_ABORTED); + SET_RESULT_STATE(SUSPEND_WOULD_EAT_MEMORY); + } + break; + case -2: /* Free caches only */ + free_flags = GFP_NOIO | __GFP_HIGHMEM; + break; + default: + free_flags = GFP_ATOMIC | __GFP_HIGHMEM; + } + + /* ----------- Stage 2: Eat memory ------------- */ + + while (((!EATEN_ENOUGH_MEMORY()) || (image_size_limit == -2)) && + (!TEST_RESULT_STATE(SUSPEND_ABORTED)) && + (times_criteria_met < 10)) { + int amount_freed; + int amount_wanted = orig_memory_still_to_eat - amount_needed(1); + + suspend2_prepare_status(0, 1, "Seeking to free %dMB of memory.", MB(amount_needed(1))); + + if (amount_wanted < 1) + amount_wanted = 1; /* image_size_limit == -2 */ + + if (orig_memory_still_to_eat) + suspend2_update_status(orig_memory_still_to_eat - amount_needed(1), + orig_memory_still_to_eat, + " Image size %d ", + MB(STORAGE_NEEDED(1))); + else + suspend2_update_status(0, 1, "Image size %d ", MB(STORAGE_NEEDED(1))); + + if ((last_amount_needed - amount_needed(1)) < 10) + times_criteria_met++; + else + times_criteria_met = 0; + last_amount_needed = amount_needed(1); + amount_freed = shrink_all_memory(last_amount_needed); + suspend2_recalculate_stats(); + display_stats(); + + did_eat_memory = 1; + + check_shift_keys(0, NULL); + } + + if (did_eat_memory) { + unsigned long orig_state = get_suspend_state(); + thaw_processes(FREEZER_KERNEL_THREADS); + /* Freeze_processes will call sys_sync too */ + freeze_processes(1); + restore_suspend_state(orig_state); + suspend2_recalculate_stats(); + display_stats(); + } + + /* Blank out image size display */ + suspend2_update_status(100, 100, NULL); + + if (!TEST_RESULT_STATE(SUSPEND_ABORTED)) { + /* Include image size limit when checking what to report */ + if (amount_needed(1) > 0) + SET_RESULT_STATE(SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY); + + /* But don't include it when deciding whether to abort (soft limit) */ + if ((amount_needed(0) > 0)) { + printk("Unable to free sufficient memory to suspend. Still need %d pages.\n", + amount_needed(1)); + SET_RESULT_STATE(SUSPEND_ABORTED); + } + + check_shift_keys(1, "Memory eating completed."); + } + + return 0; +} + +/* prepare_image + * + * Entry point to the whole image preparation section. + * + * We do four things: + * - Freeze processes; + * - Ensure image size constraints are met; + * - Complete all the preparation for saving the image, + * including allocation of storage. The only memory + * that should be needed when we're finished is that + * for actually storing the image (and we know how + * much is needed for that because the plugins tell + * us). + * - Make sure that all dirty buffers are written out. + */ + +#define MAX_TRIES 4 +int suspend2_prepare_image(void) +{ + int result = 1, sizesought, tries = 0; + + arefrozen = 0; + + header_space_allocated = 0; + + sizesought = 100 + memory_for_plugins(); + + if (attempt_to_freeze()) + return 0; + + storage_available = active_writer->ops.writer.storage_available(); + + if (!storage_available) { + printk(KERN_ERR "You need some storage available to be able to suspend.\n"); + SET_RESULT_STATE(SUSPEND_ABORTED); + SET_RESULT_STATE(SUSPEND_NOSTORAGE_AVAILABLE); + return 0; + } + + do { + suspend2_prepare_status(0, 1, "Preparing Image."); + + if (eat_memory() || TEST_RESULT_STATE(SUSPEND_ABORTED)) + break; + + result = update_image(); + + check_shift_keys(0, NULL); + + tries++; + + } while ((result) && (tries < MAX_TRIES) && (!TEST_RESULT_STATE(SUSPEND_ABORTED)) && + (!TEST_RESULT_STATE(SUSPEND_UNABLE_TO_FREE_ENOUGH_MEMORY))); + + if (tries == MAX_TRIES) + abort_suspend("Unable to get sufficient storage for the image.\n"); + + check_shift_keys(1, "Image preparation complete."); + + return !result; +} diff -ruNp 616-prepare_image.patch-old/kernel/power/suspend2_core/prepare_image.h 616-prepare_image.patch-new/kernel/power/suspend2_core/prepare_image.h --- 616-prepare_image.patch-old/kernel/power/suspend2_core/prepare_image.h 1970-01-01 10:00:00.000000000 +1000 +++ 616-prepare_image.patch-new/kernel/power/suspend2_core/prepare_image.h 2005-07-04 23:14:19.000000000 +1000 @@ -0,0 +1,44 @@ +/* + * kernel/power/prepare_image.h + */ + +extern int suspend2_prepare_image(void); +extern struct pageset_sizes_result suspend2_recalculate_stats(void); +extern int real_nr_free_pages(void); +extern int image_size_limit; +extern int pageset1_sizelow, pageset2_sizelow; + +struct pageset_sizes_result { + int size1; /* Can't be unsigned - breaks MAX function */ + int size1low; + int size2; + int size2low; + int needmorespace; +}; + +#define MIN_FREE_RAM (max_low_pfn >> 7) + +#define EXTRA_PD1_PAGES_ALLOWANCE 100 + +#define MAIN_STORAGE_NEEDED(USE_ECR) \ + ((pageset1_size + pageset2_size + 100 + \ + EXTRA_PD1_PAGES_ALLOWANCE) * \ + (USE_ECR ? expected_compression_ratio() : 100) / 100) + +#define HEADER_BYTES_NEEDED \ + ((extents_allocated * 2 * sizeof(unsigned long)) + \ + sizeof(struct suspend_header) + \ + sizeof(struct plugin_header) + \ + (int) header_storage_for_plugins() + \ + (PAGES_PER_BITMAP << PAGE_SHIFT) + \ + num_plugins * \ + (sizeof(struct plugin_header) + sizeof(int))) + +#define HEADER_STORAGE_NEEDED ((int) ((HEADER_BYTES_NEEDED + (int) PAGE_SIZE - 1) >> PAGE_SHIFT)) + +#define STORAGE_NEEDED(USE_ECR) \ + (MAIN_STORAGE_NEEDED(USE_ECR) + HEADER_STORAGE_NEEDED) + +#define RAM_TO_SUSPEND (1 + max((pageset1_size + EXTRA_PD1_PAGES_ALLOWANCE - pageset2_sizelow), 0) + \ + MIN_FREE_RAM + memory_for_plugins()) +
- 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/