| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- /*
- * kernel/power/tuxonice_pagedir.c
- *
- * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu>
- * Copyright (C) 1998,2001,2002 Pavel Machek <pavel@suse.cz>
- * Copyright (C) 2002-2003 Florent Chabaud <fchabaud@free.fr>
- * Copyright (C) 2006-2014 Nigel Cunningham (nigel at tuxonice net)
- *
- * This file is released under the GPLv2.
- *
- * Routines for handling pagesets.
- * Note that pbes aren't actually stored as such. They're stored as
- * bitmaps and extents.
- */
- #include <linux/suspend.h>
- #include <linux/highmem.h>
- #include <linux/bootmem.h>
- #include <linux/hardirq.h>
- #include <linux/sched.h>
- #include <linux/cpu.h>
- #include <asm/tlbflush.h>
- #include "tuxonice_pageflags.h"
- #include "tuxonice_ui.h"
- #include "tuxonice_pagedir.h"
- #include "tuxonice_prepare_image.h"
- #include "tuxonice.h"
- #include "tuxonice_builtin.h"
- #include "tuxonice_alloc.h"
- static int ptoi_pfn;
- static struct pbe *this_low_pbe;
- static struct pbe **last_low_pbe_ptr;
- void toi_reset_alt_image_pageset2_pfn(void)
- {
- memory_bm_position_reset(pageset2_map);
- }
- static struct page *first_conflicting_page;
- /*
- * free_conflicting_pages
- */
- static void free_conflicting_pages(void)
- {
- while (first_conflicting_page) {
- struct page *next = *((struct page **)kmap(first_conflicting_page));
- kunmap(first_conflicting_page);
- toi__free_page(29, first_conflicting_page);
- first_conflicting_page = next;
- }
- }
- /* __toi_get_nonconflicting_page
- *
- * Description: Gets order zero pages that won't be overwritten
- * while copying the original pages.
- */
- struct page *___toi_get_nonconflicting_page(int can_be_highmem)
- {
- struct page *page;
- gfp_t flags = TOI_ATOMIC_GFP;
- if (can_be_highmem)
- flags |= __GFP_HIGHMEM;
- if (test_toi_state(TOI_LOADING_ALT_IMAGE) && pageset2_map && ptoi_pfn) {
- do {
- ptoi_pfn = memory_bm_next_pfn(pageset2_map, 0);
- if (ptoi_pfn != BM_END_OF_MAP) {
- page = pfn_to_page(ptoi_pfn);
- if (!PagePageset1(page) && (can_be_highmem || !PageHighMem(page)))
- return page;
- }
- } while (ptoi_pfn);
- }
- do {
- page = toi_alloc_page(29, flags | __GFP_ZERO);
- if (!page) {
- pr_warn("Failed to get nonconflicting " "page.\n");
- return NULL;
- }
- if (PagePageset1(page)) {
- struct page **next = (struct page **)kmap(page);
- *next = first_conflicting_page;
- first_conflicting_page = page;
- kunmap(page);
- }
- } while (PagePageset1(page));
- return page;
- }
- unsigned long __toi_get_nonconflicting_page(void)
- {
- struct page *page = ___toi_get_nonconflicting_page(0);
- return page ? (unsigned long)page_address(page) : 0;
- }
- static struct pbe *get_next_pbe(struct page **page_ptr, struct pbe *this_pbe, int highmem)
- {
- if (((((unsigned long)this_pbe) & (PAGE_SIZE - 1))
- + 2 * sizeof(struct pbe)) > PAGE_SIZE) {
- struct page *new_page = ___toi_get_nonconflicting_page(highmem);
- if (!new_page)
- return ERR_PTR(-ENOMEM);
- this_pbe = (struct pbe *)kmap(new_page);
- memset(this_pbe, 0, PAGE_SIZE);
- *page_ptr = new_page;
- } else
- this_pbe++;
- return this_pbe;
- }
- /**
- * get_pageset1_load_addresses - generate pbes for conflicting pages
- *
- * We check here that pagedir & pages it points to won't collide
- * with pages where we're going to restore from the loaded pages
- * later.
- *
- * Returns:
- * Zero on success, one if couldn't find enough pages (shouldn't
- * happen).
- **/
- int toi_get_pageset1_load_addresses(void)
- {
- int pfn, highallocd = 0, lowallocd = 0;
- int low_needed = pagedir1.size - get_highmem_size(pagedir1);
- int high_needed = get_highmem_size(pagedir1);
- int low_pages_for_highmem = 0;
- gfp_t flags = GFP_ATOMIC | __GFP_NOWARN | __GFP_HIGHMEM;
- struct page *page, *high_pbe_page = NULL, *last_high_pbe_page = NULL,
- *low_pbe_page, *last_low_pbe_page = NULL;
- struct pbe **last_high_pbe_ptr = &restore_highmem_pblist, *this_high_pbe = NULL;
- unsigned long orig_low_pfn, orig_high_pfn;
- int high_pbes_done = 0, low_pbes_done = 0;
- int low_direct = 0, high_direct = 0, result = 0, i;
- int high_page = 1, high_offset = 0, low_page = 1, low_offset = 0;
- int low_for_high_count = 0, high_direct_count = 0, low_direct_count = 0;
- toi_trace_index++;
- memory_bm_position_reset(pageset1_map);
- memory_bm_position_reset(pageset1_copy_map);
- last_low_pbe_ptr = &restore_pblist;
- /* First, allocate pages for the start of our pbe lists. */
- if (high_needed) {
- high_pbe_page = ___toi_get_nonconflicting_page(1);
- if (!high_pbe_page) {
- result = -ENOMEM;
- goto out;
- }
- this_high_pbe = (struct pbe *)kmap(high_pbe_page);
- memset(this_high_pbe, 0, PAGE_SIZE);
- }
- low_pbe_page = ___toi_get_nonconflicting_page(0);
- if (!low_pbe_page) {
- result = -ENOMEM;
- goto out;
- }
- this_low_pbe = (struct pbe *)page_address(low_pbe_page);
- pr_warn("%s: %d. %d, %d, %d, %d, %d, %d, %d\n", __func__, __LINE__,
- low_needed, lowallocd, low_direct,
- high_needed, highallocd, high_direct, low_pages_for_highmem);
- /*
- * Next, allocate the number of pages we need.
- */
- i = low_needed + high_needed;
- do {
- int is_high;
- if (i == low_needed)
- flags &= ~__GFP_HIGHMEM;
- page = toi_alloc_page(30, flags);
- BUG_ON(!page);
- SetPagePageset1Copy(page);
- is_high = PageHighMem(page);
- if (PagePageset1(page)) {
- if (is_high)
- high_direct++;
- else
- low_direct++;
- } else {
- if (is_high)
- highallocd++;
- else
- lowallocd++;
- }
- } while (--i);
- pr_warn("%s: %d. %d, %d, %d, %d, %d, %d, %d\n", __func__, __LINE__,
- low_needed, lowallocd, low_direct,
- high_needed, highallocd, high_direct, low_pages_for_highmem);
- high_needed -= high_direct;
- low_needed -= low_direct;
- pr_warn("%s: %d. %d, %d, %d, %d, %d, %d, %d\n", __func__, __LINE__,
- low_needed, lowallocd, low_direct,
- high_needed, highallocd, high_direct, low_pages_for_highmem);
- /*
- * Do we need to use some lowmem pages for the copies of highmem
- * pages?
- */
- if (high_needed > highallocd) {
- low_pages_for_highmem = high_needed - highallocd;
- high_needed -= low_pages_for_highmem;
- low_needed += low_pages_for_highmem;
- }
- pr_warn("%s: %d. %d, %d, %d, %d, %d, %d, %d\n", __func__, __LINE__,
- low_needed, lowallocd, low_direct,
- high_needed, highallocd, high_direct, low_pages_for_highmem);
- /*
- * Now generate our pbes (which will be used for the atomic restore),
- * and free unneeded pages.
- */
- memory_bm_position_reset(pageset1_copy_map);
- for (pfn = memory_bm_next_pfn(pageset1_copy_map, 0); pfn != BM_END_OF_MAP;
- pfn = memory_bm_next_pfn(pageset1_copy_map, 0)) {
- int is_high;
- #ifdef CONFIG_TOI_FIXUP
- int orig_is_high;
- unsigned long orig_pfn;
- struct page *orig_page;
- #endif
- page = pfn_to_page(pfn);
- is_high = PageHighMem(page);
- if (PagePageset1(page)) {
- is_high ? high_direct_count++ : low_direct_count++;
- continue;
- }
- #ifdef CONFIG_TOI_FIXUP
- do {
- orig_pfn = memory_bm_next_pfn(pageset1_map, 0);
- BUG_ON(orig_pfn == BM_END_OF_MAP);
- orig_page = pfn_to_page(orig_pfn);
- } while (PagePageset1Copy(orig_page));
- orig_is_high = PageHighMem(orig_page);
- if (orig_is_high &&
- (is_high || low_pages_for_highmem)) {
- #else
- /* Nope. We're going to use this page. Add a pbe. */
- if (is_high || low_pages_for_highmem) {
- struct page *orig_page;
- #endif
- high_pbes_done++;
- if (!is_high)
- low_pages_for_highmem--;
- else
- low_for_high_count++;
- #ifdef CONFIG_TOI_FIXUP
- orig_high_pfn = orig_pfn;
- #else
- do {
- orig_high_pfn = memory_bm_next_pfn(pageset1_map, 0);
- BUG_ON(orig_high_pfn == BM_END_OF_MAP);
- orig_page = pfn_to_page(orig_high_pfn);
- } while (!PageHighMem(orig_page) || PagePageset1Copy(orig_page));
- #endif
- this_high_pbe->orig_address = (void *)orig_high_pfn;
- this_high_pbe->address = page;
- this_high_pbe->next = NULL;
- toi_message(TOI_PAGEDIR, TOI_VERBOSE, 0, "High pbe %d/%d: %p(%lu)=>%p",
- high_page, high_offset, page, orig_high_pfn, orig_page);
- if (last_high_pbe_page != high_pbe_page) {
- *last_high_pbe_ptr = (struct pbe *)high_pbe_page;
- if (last_high_pbe_page) {
- kunmap(last_high_pbe_page);
- high_page++;
- high_offset = 0;
- } else
- high_offset++;
- last_high_pbe_page = high_pbe_page;
- } else {
- *last_high_pbe_ptr = this_high_pbe;
- high_offset++;
- }
- last_high_pbe_ptr = &this_high_pbe->next;
- this_high_pbe = get_next_pbe(&high_pbe_page, this_high_pbe, 1);
- if (IS_ERR(this_high_pbe)) {
- pr_warn("This high pbe is an error.\n");
- return -ENOMEM;
- }
- #ifdef CONFIG_TOI_FIXUP
- } else if (!orig_is_high) {
- #else
- } else {
- struct page *orig_page;
- #endif
- low_pbes_done++;
- #ifdef CONFIG_TOI_FIXUP
- orig_low_pfn = orig_pfn;
- #else
- do {
- orig_low_pfn = memory_bm_next_pfn(pageset1_map, 0);
- BUG_ON(orig_low_pfn == BM_END_OF_MAP);
- orig_page = pfn_to_page(orig_low_pfn);
- } while (PageHighMem(orig_page) || PagePageset1Copy(orig_page));
- #endif
- this_low_pbe->orig_address = page_address(orig_page);
- this_low_pbe->address = page_address(page);
- this_low_pbe->next = NULL;
- toi_message(TOI_PAGEDIR, TOI_VERBOSE, 0, "Low pbe %d/%d: %p(%lu)=>%p",
- low_page, low_offset, this_low_pbe->orig_address,
- orig_low_pfn, this_low_pbe->address);
- TOI_TRACE_DEBUG(orig_low_pfn, "LoadAddresses (%d/%d): %p=>%p", low_page,
- low_offset, this_low_pbe->orig_address,
- this_low_pbe->address);
- *last_low_pbe_ptr = this_low_pbe;
- last_low_pbe_ptr = &this_low_pbe->next;
- this_low_pbe = get_next_pbe(&low_pbe_page, this_low_pbe, 0);
- if (low_pbe_page != last_low_pbe_page) {
- if (last_low_pbe_page) {
- low_page++;
- low_offset = 0;
- } else {
- low_offset++;
- }
- last_low_pbe_page = low_pbe_page;
- } else
- low_offset++;
- if (IS_ERR(this_low_pbe)) {
- pr_warn("this_low_pbe is an error.\n");
- return -ENOMEM;
- }
- }
- }
- pr_warn("%s: %d. %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", __func__, __LINE__,
- low_needed, lowallocd, low_direct,
- high_needed, highallocd, high_direct, low_pages_for_highmem,
- low_pbes_done, low_direct_count,
- high_pbes_done, high_direct_count, low_for_high_count);
- if (high_pbe_page)
- kunmap(high_pbe_page);
- if (last_high_pbe_page != high_pbe_page) {
- if (last_high_pbe_page)
- kunmap(last_high_pbe_page);
- toi__free_page(29, high_pbe_page);
- }
- free_conflicting_pages();
- out:
- return result;
- }
- int add_boot_kernel_data_pbe(void)
- {
- this_low_pbe->address = (char *)__toi_get_nonconflicting_page();
- if (!this_low_pbe->address) {
- pr_warn("Failed to get bkd atomic restore buffer.");
- return -ENOMEM;
- }
- toi_bkd.size = sizeof(toi_bkd);
- memcpy(this_low_pbe->address, &toi_bkd, sizeof(toi_bkd));
- *last_low_pbe_ptr = this_low_pbe;
- this_low_pbe->orig_address = (char *)boot_kernel_data_buffer;
- this_low_pbe->next = NULL;
- return 0;
- }
|