* PATCH swsusp: page rellocation speed up
@ 2005-01-11 1:01 Lukas Hejtmanek
2005-01-12 12:49 ` hugang
0 siblings, 1 reply; 5+ messages in thread
From: Lukas Hejtmanek @ 2005-01-11 1:01 UTC (permalink / raw)
To: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 140 bytes --]
Hello,
attached patch should speed up page rellocation at time of resume. Please test.
The diff is against 2.6.10-bk8
--
Lukáš Hejtmánek
[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 2105 bytes --]
--- a/kernel/power/swsusp.c 2005-01-06 14:08:33.000000000 +0100
+++ b/kernel/power/swsusp.c 2005-01-11 01:03:24.000000000 +0100
@@ -885,25 +885,18 @@
}
-
/* More restore stuff */
-#define does_collide(addr) does_collide_order(pagedir_nosave, addr, 0)
-
/*
* Returns true if given address/order collides with any orig_address
*/
-static int __init does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr,
- int order)
+static int __init does_collide_order(unsigned long addr, int order)
{
int i;
- unsigned long addre = addr + (PAGE_SIZE<<order);
- for (i=0; i < nr_copy_pages; i++)
- if ((pagedir+i)->orig_address >= addr &&
- (pagedir+i)->orig_address < addre)
+ for (i=0; i < (1<<order); i++)
+ if(!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE)))
return 1;
-
return 0;
}
@@ -922,7 +915,7 @@
addr = get_zeroed_page(GFP_ATOMIC);
if(!addr)
return -ENOMEM;
- } while (does_collide(addr));
+ } while (does_collide_order(addr,0));
(pagedir_nosave+i)->address = addr;
}
@@ -939,16 +932,34 @@
void **eaten_memory = NULL;
void **c = eaten_memory, *m, *f;
int ret = 0;
+ struct zone *zone;
+ int i;
+ struct pbe *p;
+ unsigned long zone_pfn;
printk("Relocating pagedir ");
- if (!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) {
+ /* Set page flags */
+
+ for_each_zone(zone) {
+ for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn)
+ SetPageNosaveFree(pfn_to_page(zone_pfn +
+ zone->zone_start_pfn));
+ }
+
+ /* Clear orig address */
+
+ for(i = 0, p = pagedir_nosave; i < nr_copy_pages; i++, p++) {
+ ClearPageNosaveFree(virt_to_page(p->orig_address));
+ }
+
+ if (!does_collide_order((unsigned long)old_pagedir, pagedir_order)) {
printk("not necessary\n");
return check_pagedir();
}
while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) {
- if (!does_collide_order(old_pagedir, (unsigned long)m, pagedir_order))
+ if (!does_collide_order((unsigned long)m, pagedir_order))
break;
eaten_memory = m;
printk( "." );
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: PATCH swsusp: page rellocation speed up 2005-01-11 1:01 PATCH swsusp: page rellocation speed up Lukas Hejtmanek @ 2005-01-12 12:49 ` hugang 2005-01-12 13:01 ` hugang ` (2 more replies) 0 siblings, 3 replies; 5+ messages in thread From: hugang @ 2005-01-12 12:49 UTC (permalink / raw) To: Lukas Hejtmanek; +Cc: linux-kernel On Tue, Jan 11, 2005 at 02:01:23AM +0100, Lukas Hejtmanek wrote: > Hello, > > attached patch should speed up page rellocation at time of resume. Please test. > The diff is against 2.6.10-bk8 > .... really cool, Passed in my x86 and ppc. Here is a patch to make pagedir using non-continuity page, 2.6.10 -> mm1 -> this patch -> my patch I temporary split pbe function to pbe.h, useful merge. diff -Nurp 2.6.10-mm1-one-pbe/arch/i386/power/swsusp.S 2.6.10-mm1-one-pbe-hg/arch/i386/power/swsusp.S --- 2.6.10-mm1-one-pbe/arch/i386/power/swsusp.S 2004-12-30 14:56:21.000000000 +0800 +++ 2.6.10-mm1-one-pbe-hg/arch/i386/power/swsusp.S 2005-01-12 20:09:01.000000000 +0800 @@ -31,24 +31,36 @@ ENTRY(swsusp_arch_resume) movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - movl pagedir_nosave, %ebx - xorl %eax, %eax - xorl %edx, %edx - .p2align 4,,7 + movl pagedir_nosave, %eax + test %eax, %eax + je copy_loop_end + movl $1024, %edx -copy_loop: - movl 4(%ebx,%edx),%edi - movl (%ebx,%edx),%esi - - movl $1024, %ecx - rep - movsl - - incl %eax - addl $16, %edx - cmpl nr_copy_pages,%eax - jb copy_loop .p2align 4,,7 +copy_loop_start: + movl 0xc(%eax), %ebp + xorl %ebx, %ebx + leal 0x0(%esi),%esi + +copy_one_pgdir: + movl 0x4(%eax),%edi + test %edi, %edi + je copy_loop_end + + movl (%eax), %esi + movl %edx, %ecx + repz movsl %ds:(%esi),%es:(%edi) + + incl %ebx + addl $0x10, %eax + cmpl $0xff, %ebx + jbe copy_one_pgdir + test %ebp, %ebp + movl %ebp, %eax + jne copy_loop_start + + .p2align 4,,7 +copy_loop_end: movl saved_context_esp, %esp movl saved_context_ebp, %ebp diff -Nurp 2.6.10-mm1-one-pbe/kernel/power/pbe.h 2.6.10-mm1-one-pbe-hg/kernel/power/pbe.h --- 2.6.10-mm1-one-pbe/kernel/power/pbe.h 1970-01-01 07:00:00.000000000 +0700 +++ 2.6.10-mm1-one-pbe-hg/kernel/power/pbe.h 2005-01-12 20:42:17.000000000 +0800 @@ -0,0 +1,380 @@ +static int mod_progress = 1; + +static void inline mod_printk_progress(int i) +{ + if (mod_progress == 0) mod_progress = 1; + if (!(i%100)) + printk( "\b\b\b\b%3d%%", i / mod_progress ); +} + +#define ONE_PAGE_PBE_NUM (PAGE_SIZE/sizeof(struct pbe)) +#define PBE_IS_PAGE_END(x) \ + ( PAGE_SIZE - sizeof(struct pbe) == ((x) - ((~(PAGE_SIZE - 1)) & (x))) ) + +#define pgdir_for_each(pos, n, head) \ + for(pos = head, n = pos ? (suspend_pagedir_t*)pos->dummy.val : NULL; \ + pos != NULL; \ + pos = n, n = pos ? (suspend_pagedir_t *)pos->dummy.val : NULL) + +#define pbe_for_each(pos, n, index, max, head) \ + for(pos = head, index = 0,\ + n = pos ? (struct pbe *)pos->dummy.val : NULL; \ + (pos != NULL) && (index < max); \ + pos = (PBE_IS_PAGE_END((unsigned long)pos)) ? n : \ + ((struct pbe *)((unsigned long)pos + sizeof(struct pbe))), \ + index ++, \ + n = pos ? (struct pbe*)pos->dummy.val : NULL) + +static inline struct pbe *find_pbe_by_index(struct pbe *pgdir, int index) +{ + unsigned long p = 0; + struct pbe *pbe, *next; + + pr_debug("find_pbe_by_index: %p, 0x%03x", pgdir, index); + pgdir_for_each(pbe, next, pgdir) { + if (p == index / ONE_PAGE_PBE_NUM) { + pbe = (struct pbe *)((unsigned long)pbe + + (index % ONE_PAGE_PBE_NUM) * sizeof(struct pbe)); + pr_debug(" %p, o{%p} c{%p}\n", + pbe, (void*)pbe->orig_address, (void*)pbe->address); + return pbe; + } + p ++; + } + return (NULL); +} + +static inline void pagedir_free(suspend_pagedir_t *head) +{ + suspend_pagedir_t *next, *cur; + pgdir_for_each(cur, next, head) + free_page((unsigned long)cur); +} + +/** + * write_one_pbe - + * @p: + * @data: + * @cur: + * + */ +static int inline write_one_pbe(struct pbe *p, void *data, int cur) +{ + int error = 0; + + mod_printk_progress(cur); + + pr_debug("write_one_pbe: %p, o{%p} c{%p} %d ", + p, (void *)p->orig_address, (void *)p->address, cur); + error = write_page((unsigned long)data, &p->swap_address); + if (error) return error; + pr_debug("%lu\n", swp_offset(p->swap_address)); + + return 0; +} + +static int bio_read_page(pgoff_t page_off, void * page); + +/** + * read_one_pbe - + * @p: + * @data: + * @cur + * + */ +static int inline read_one_pbe(struct pbe *p, void *data, int cur) +{ + int error = 0; + + mod_printk_progress(cur); + + pr_debug("read_one_pbe: %p, o{%p} c{%p} %lu\n", + p, (void *)p->orig_address, data, + swp_offset(p->swap_address)); + + error = bio_read_page(swp_offset(p->swap_address), data); + if (error) return error; + + return 0; +} + +/* + * Returns true if given address/order collides with any orig_address + */ +static int does_collide_order(unsigned long addr, int order) +{ + int i; + + for (i=0; i < (1<<order); i++) + if(!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE))) + return 1; + return 0; +} + +static void **eaten_memory = NULL; + +static void *swsusp_get_safe_free_page(int collide) +{ + void *addr = NULL; + void **c = eaten_memory; + + do { + if (addr) { + eaten_memory = (void**)addr; + *eaten_memory = c; + c = eaten_memory; + } + addr = (void*)__get_free_pages(GFP_ATOMIC | __GFP_COLD, 0); + if (!addr) + return NULL; + } while (collide && does_collide_order((unsigned long)addr, 0)); + + if (collide) + ClearPageNosaveFree(virt_to_page(addr)); + + return addr; +} + +/** + * alloc_one_pagedir - + * @prev: + * @collide: + * + */ +static suspend_pagedir_t * alloc_one_pagedir(suspend_pagedir_t *prev, + int collide) +{ + suspend_pagedir_t *pgdir = NULL; + int i; + + pgdir = (suspend_pagedir_t *)swsusp_get_safe_free_page(collide); + + /*pr_debug("pgdir: %p, %p, %d\n", + pgdir, prev, sizeof(suspend_pagedir_t)); */ + for (i = 0; i < ONE_PAGE_PBE_NUM; i++) { + pgdir[i].dummy.val = 0; + pgdir[i].address = 0; + pgdir[i].orig_address = 0; + if (prev) + prev[i].dummy.val= (unsigned long)pgdir; + } + + return (pgdir); +} + +/* calc_nums - Determine the nums of allocation needed for pagedir_save. */ +static int calc_nums(int nr_copy) +{ + int diff = 0, ret = 0; + do { + diff = (nr_copy / ONE_PAGE_PBE_NUM) - ret + 1; + if (diff) { + ret += diff; + nr_copy += diff; + } + } while (diff); + return nr_copy; +} + +/** + * alloc_pagedir - Allocate the page directory. + * @pbe: + * @pbe_nums: + * @collide: + * @page_nums: + */ +static int alloc_pagedir(struct pbe **pbe, int pbe_nums, + int collide, int page_nums) +{ + unsigned int nums = 0; + unsigned int after_alloc = pbe_nums; + suspend_pagedir_t *prev = NULL, *cur = NULL; + + if (page_nums) + after_alloc = ONE_PAGE_PBE_NUM * page_nums; + else + after_alloc = calc_nums(after_alloc); + pr_debug("alloc_pagedir: %d, %d\n", pbe_nums, after_alloc); + for (nums = 0 ; nums < after_alloc ; nums += ONE_PAGE_PBE_NUM) { + cur = alloc_one_pagedir(prev, collide); + pr_debug("alloc_one_pagedir: %p\n", cur); + if (!cur) { /* get page failed */ + goto no_mem; + } + if (nums == 0) { /* setup the head */ + *pbe = cur; + } + prev = cur; + } + return after_alloc - pbe_nums; + +no_mem: + pagedir_free(*pbe); + *pbe = NULL; + + return (-ENOMEM); +} + +static void __init eat_progress(void) +{ + char *eaten_progess = "-\\|/"; + static int eaten_i = 0; + + printk("\b%c", eaten_progess[eaten_i]); + eaten_i ++; + if (eaten_i > 3) eaten_i = 0; +} + +static int __init check_one_pbe(struct pbe *p, int cur) +{ + unsigned long addr = 0; + + pr_debug("check_one_pbe: %p %lu o{%p} ", + p, p->swap_address.val, (void*)p->orig_address); + addr = (unsigned long)swsusp_get_safe_free_page(1); + if(!addr) + return -ENOMEM; + pr_debug("c{%p} done\n", (void*)addr); + p->address = addr; + + return 0; +} + +static void __init swsusp_copy_pagedir(suspend_pagedir_t *d_pgdir, + suspend_pagedir_t *s_pgdir) +{ + int i = 0; + + while (s_pgdir != NULL) { + suspend_pagedir_t *s_next = (suspend_pagedir_t *)s_pgdir->dummy.val; + suspend_pagedir_t *d_next = (suspend_pagedir_t *)d_pgdir->dummy.val; + for (i = 0; i < ONE_PAGE_PBE_NUM; i++) { + d_pgdir->address = s_pgdir->address; + d_pgdir->orig_address = s_pgdir->orig_address; + d_pgdir->swap_address = s_pgdir->swap_address; + s_pgdir ++; d_pgdir ++; + } + d_pgdir = d_next; + s_pgdir = s_next; + }; +} + +/** + * 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 + */ +static int __init check_pagedir(void) +{ + void **c, *f; + struct pbe *next, *pos; + int error, index, i; + suspend_pagedir_t *addr = NULL; + struct zone *zone; + unsigned long zone_pfn; + + printk("Relocating pagedir ... "); + /* Set page flags */ + for_each_zone(zone) { + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) + SetPageNosaveFree(pfn_to_page(zone_pfn + zone->zone_start_pfn)); + } + + /* Clear orig address */ + pbe_for_each(pos, next, i, nr_copy_pages, pagedir_nosave) { + pr_debug("clear <%p>\n", (void*)pos->orig_address); + ClearPageNosaveFree(virt_to_page(pos->orig_address)); + } + + error = alloc_pagedir(&addr, nr_copy_pages, 1, swsusp_info.pagedir_pages); + if (error < 0) { + return error; + } + swsusp_copy_pagedir(addr, pagedir_nosave); + pagedir_free(pagedir_nosave); + + /* check copy address */ + pbe_for_each(pos, next, index, nr_copy_pages, addr) { + error = check_one_pbe(pos, index); + BUG_ON(error); + } + + /* free eaten memory */ + c = eaten_memory; + while (c) { + eat_progress(); + f = c; + c = *c; + free_pages((unsigned long)f, 0); + } + + printk(" done\n"); + + pagedir_nosave = addr; + + return 0; +} + + +static int __init read_one_pagedir(suspend_pagedir_t *pgdir, int i) +{ + unsigned long offset = swp_offset(swsusp_info.pagedir[i]); + unsigned long next; + int error = 0; + + next = pgdir->dummy.val; + pr_debug("read_one_pagedir: %p, %d, %lu, %p\n", + pgdir, i, offset, (void*)next); + if ((error = bio_read_page(offset, (void *)pgdir))) { + return error; + } + pgdir->dummy.val = next; + + return error; +} + +/** + * for_each_pbe_copy_back - + * + * That usefuly for help us writing the code in assemble code + * + */ +/* #define CREATE_ASM_CODE */ +#ifdef CREATE_ASM_CODE +#if 0 /* if your copy back code is running in real mode, enable it */ +#define GET_ADDRESS(x) __pa(x) +#else +#define GET_ADDRESS(x) (x) +#endif +asmlinkage void for_each_pbe_copy_back(void) +{ + struct pbe *pgdir, *next; + + pgdir = pagedir_nosave; + while (pgdir != NULL) { + unsigned long nums, i; + pgdir = (struct pbe *)GET_ADDRESS(pgdir); + next = (struct pbe*)pgdir->dummy.val; + for (nums = 0; nums < ONE_PAGE_PBE_NUM; nums++) { + register unsigned long *orig, *copy; + orig = (unsigned long *)pgdir->orig_address; + if (orig == 0) goto end; + orig = (unsigned long *)GET_ADDRESS(orig); + copy = (unsigned long *)GET_ADDRESS(pgdir->address); +#if 0 + memcpy(orig, copy, PAGE_SIZE); +#else + for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i+=4) { + *(orig + i) = *(copy + i); + *(orig + i+1) = *(copy + i+1); + *(orig + i+2) = *(copy + i+2); + *(orig + i+3) = *(copy + i+3); + } +#endif + pgdir ++; + } + pgdir = next; + } +end: + panic("just asm code"); +} +#endif diff -Nurp 2.6.10-mm1-one-pbe/kernel/power/swsusp.c 2.6.10-mm1-one-pbe-hg/kernel/power/swsusp.c --- 2.6.10-mm1-one-pbe/kernel/power/swsusp.c 2005-01-11 22:42:58.000000000 +0800 +++ 2.6.10-mm1-one-pbe-hg/kernel/power/swsusp.c 2005-01-12 20:40:20.000000000 +0800 @@ -76,7 +76,6 @@ extern const void __nosave_begin, __nosave_end; /* Variables to be preserved over suspend */ -static int pagedir_order_check; static int nr_copy_pages_check; extern char resume_file[]; @@ -99,7 +98,6 @@ unsigned int nr_copy_pages __nosavedata */ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; static suspend_pagedir_t *pagedir_save; -static int pagedir_order __nosavedata = 0; #define SWSUSP_SIG "S1SUSPEND" @@ -260,6 +258,8 @@ static int write_page(unsigned long addr } +#include "pbe.h" + /** * data_free - Free the swap entries used by the saved image. * @@ -271,14 +271,15 @@ static void data_free(void) { swp_entry_t entry; int i; + struct pbe *next, *pos; - for (i = 0; i < nr_copy_pages; i++) { - entry = (pagedir_nosave + i)->swap_address; + pbe_for_each(pos, next, i, nr_copy_pages, pagedir_nosave) { + entry = pos->swap_address; if (entry.val) swap_free(entry); else break; - (pagedir_nosave + i)->swap_address = (swp_entry_t){0}; + pos->swap_address = (swp_entry_t){0}; } } @@ -293,17 +294,15 @@ static int data_write(void) { int error = 0; int i; - unsigned int mod = nr_copy_pages / 100; + struct pbe *pos, *next; - if (!mod) - mod = 1; + mod_progress = nr_copy_pages / 100; printk( "Writing data to swap (%d pages)... ", nr_copy_pages ); - for (i = 0; i < nr_copy_pages && !error; i++) { - if (!(i%mod)) - printk( "\b\b\b\b%3d%%", i / mod ); - error = write_page((pagedir_nosave+i)->address, - &((pagedir_nosave+i)->swap_address)); + pbe_for_each(pos, next, i, nr_copy_pages, pagedir_nosave) { + BUG_ON(pos->orig_address == 0); + error = write_one_pbe(pos, (void*)pos->address, i); + if (error) break; } printk("\b\b\b\bdone\n"); return error; @@ -373,15 +372,17 @@ static void free_pagedir_entries(void) static int write_pagedir(void) { - unsigned long addr = (unsigned long)pagedir_nosave; int error = 0; - int n = SUSPEND_PD_PAGES(nr_copy_pages); - int i; + int n = 0; + suspend_pagedir_t *pgdir, *next; - swsusp_info.pagedir_pages = n; + pgdir_for_each(pgdir, next, pagedir_nosave) { + error = write_page((unsigned long)pgdir, &swsusp_info.pagedir[n]); + if (error) break; + n ++; + } printk( "Writing pagedir (%d pages)\n", n); - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = write_page(addr, &swsusp_info.pagedir[i]); + swsusp_info.pagedir_pages = n; return error; } @@ -566,9 +567,9 @@ static void copy_data_pages(void) { struct zone *zone; unsigned long zone_pfn; - struct pbe * pbe = pagedir_nosave; + struct pbe * pbe = NULL; int to_copy = nr_copy_pages; - + for_each_zone(zone) { if (is_highmem(zone)) continue; @@ -576,92 +577,31 @@ static void copy_data_pages(void) for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) { if (saveable(zone, &zone_pfn)) { struct page * page; + pbe = find_pbe_by_index(pagedir_nosave, nr_copy_pages-to_copy); + BUG_ON(pbe == NULL); page = pfn_to_page(zone_pfn + zone->zone_start_pfn); pbe->orig_address = (long) page_address(page); + BUG_ON(pbe->orig_address == 0); + BUG_ON(pbe->address == 0); /* copy_page is not usable for copying task structs. */ memcpy((void *)pbe->address, (void *)pbe->orig_address, PAGE_SIZE); - pbe++; - to_copy--; + to_copy --; } } } BUG_ON(to_copy); } - -/** - * calc_order - Determine the order of allocation needed for pagedir_save. - * - * This looks tricky, but is just subtle. Please fix it some time. - * Since there are %nr_copy_pages worth of pages in the snapshot, we need - * to allocate enough contiguous space to hold - * (%nr_copy_pages * sizeof(struct pbe)), - * which has the saved/orig locations of the page.. - * - * SUSPEND_PD_PAGES() tells us how many pages we need to hold those - * structures, then we call get_bitmask_order(), which will tell us the - * last bit set in the number, starting with 1. (If we need 30 pages, that - * is 0x0000001e in hex. The last bit is the 5th, which is the order we - * would use to allocate 32 contiguous pages). - * - * Since we also need to save those pages, we add the number of pages that - * we need to nr_copy_pages, and in case of an overflow, do the - * calculation again to update the number of pages needed. - * - * With this model, we will tend to waste a lot of memory if we just cross - * an order boundary. Plus, the higher the order of allocation that we try - * to do, the more likely we are to fail in a low-memory situtation - * (though we're unlikely to get this far in such a case, since swsusp - * requires half of memory to be free anyway). - */ - - -static void calc_order(void) -{ - int diff = 0; - int order = 0; - - do { - diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; - if (diff) { - order += diff; - nr_copy_pages += 1 << diff; - } - } while(diff); - pagedir_order = order; -} - - -/** - * alloc_pagedir - Allocate the page directory. - * - * First, determine exactly how many contiguous pages we need and - * allocate them. - */ - -static int alloc_pagedir(void) -{ - calc_order(); - pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, - pagedir_order); - if (!pagedir_save) - return -ENOMEM; - memset(pagedir_save, 0, (1 << pagedir_order) * PAGE_SIZE); - pagedir_nosave = pagedir_save; - return 0; -} - /** * free_image_pages - Free pages allocated for snapshot */ static void free_image_pages(void) { - struct pbe * p; + struct pbe * p, * n; int i; - p = pagedir_save; - for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { + pbe_for_each(p, n, i, nr_copy_pages, pagedir_save) { if (p->address) { ClearPageNosave(virt_to_page(p->address)); free_page(p->address); @@ -677,10 +617,10 @@ static void free_image_pages(void) static int alloc_image_pages(void) { - struct pbe * p; + struct pbe * p, * n; int i; - for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { + pbe_for_each(p, n, i, nr_copy_pages, pagedir_save) { p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); if (!p->address) return -ENOMEM; @@ -694,7 +634,7 @@ void swsusp_free(void) BUG_ON(PageNosave(virt_to_page(pagedir_save))); BUG_ON(PageNosaveFree(virt_to_page(pagedir_save))); free_image_pages(); - free_pages((unsigned long) pagedir_save, pagedir_order); + pagedir_free(pagedir_save); } @@ -752,18 +692,20 @@ static int swsusp_alloc(void) if (!enough_swap()) return -ENOSPC; - if ((error = alloc_pagedir())) { + if ((error = alloc_pagedir(&pagedir_save, nr_copy_pages, 0, 0)) < 0) { pr_debug("suspend: Allocating pagedir failed.\n"); return error; } + pr_debug("alloc_pagedir: addon %d\n", error); + nr_copy_pages += error; if ((error = alloc_image_pages())) { pr_debug("suspend: Allocating image pages failed.\n"); swsusp_free(); return error; } + pagedir_nosave = pagedir_save; nr_copy_pages_check = nr_copy_pages; - pagedir_order_check = pagedir_order; return 0; } @@ -856,7 +798,6 @@ int swsusp_suspend(void) asmlinkage int swsusp_restore(void) { BUG_ON (nr_copy_pages_check != nr_copy_pages); - BUG_ON (pagedir_order_check != pagedir_order); /* Even mappings of "global" things (vmalloc) need to be fixed */ __flush_tlb_global(); @@ -880,110 +821,6 @@ int swsusp_resume(void) return error; } - -/* More restore stuff */ - -/* - * Returns true if given address/order collides with any orig_address - */ -static int __init does_collide_order(unsigned long addr, int order) -{ - int i; - - for (i=0; i < (1<<order); i++) - if(!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE))) - return 1; - return 0; -} - -/* - * 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 - */ -static int __init check_pagedir(void) -{ - int i; - - for(i=0; i < nr_copy_pages; i++) { - unsigned long addr; - - do { - addr = get_zeroed_page(GFP_ATOMIC); - if(!addr) - return -ENOMEM; - } while (does_collide_order(addr,0)); - - (pagedir_nosave+i)->address = addr; - } - return 0; -} - -static int __init swsusp_pagedir_relocate(void) -{ - /* - * We have to avoid recursion (not to overflow kernel stack), - * and that's why code looks pretty cryptic - */ - suspend_pagedir_t *old_pagedir = pagedir_nosave; - void **eaten_memory = NULL; - void **c = eaten_memory, *m, *f; - int ret = 0; - struct zone *zone; - int i; - struct pbe *p; - unsigned long zone_pfn; - - printk("Relocating pagedir "); - - /* Set page flags */ - - for_each_zone(zone) { - for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) - SetPageNosaveFree(pfn_to_page(zone_pfn + - zone->zone_start_pfn)); - } - - /* Clear orig address */ - - for(i = 0, p = pagedir_nosave; i < nr_copy_pages; i++, p++) { - ClearPageNosaveFree(virt_to_page(p->orig_address)); - } - - if (!does_collide_order((unsigned long)old_pagedir, pagedir_order)) { - printk("not necessary\n"); - return check_pagedir(); - } - - while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) { - if (!does_collide_order((unsigned long)m, pagedir_order)) - break; - eaten_memory = m; - printk( "." ); - *eaten_memory = c; - c = eaten_memory; - } - - if (!m) { - printk("out of memory\n"); - ret = -ENOMEM; - } else { - pagedir_nosave = - memcpy(m, old_pagedir, PAGE_SIZE << pagedir_order); - } - - c = eaten_memory; - while (c) { - printk(":"); - f = c; - c = *c; - free_pages((unsigned long)f, pagedir_order); - } - if (ret) - return ret; - printk("|\n"); - return check_pagedir(); -} - /** * Using bio to read from swap. * This code requires a bit more work than just using buffer heads @@ -1098,7 +935,6 @@ static int __init check_header(void) return -EPERM; } nr_copy_pages = swsusp_info.image_pages; - pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); return error; } @@ -1134,23 +970,20 @@ static int __init check_sig(void) static int __init data_read(void) { - struct pbe * p; + struct pbe * p, * n; int error; int i; - int mod = nr_copy_pages / 100; - if (!mod) - mod = 1; + if ((error = check_pagedir())) { + return -ENOMEM; + } - if ((error = swsusp_pagedir_relocate())) - return error; + mod_progress = nr_copy_pages / 100; printk( "Reading image data (%d pages): ", nr_copy_pages ); - for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { - if (!(i%mod)) - printk( "\b\b\b\b%3d%%", i / mod ); - error = bio_read_page(swp_offset(p->swap_address), - (void *)p->address); + pbe_for_each(p, n, i, nr_copy_pages, pagedir_nosave) { + error = read_one_pbe(p, (void*)p->address, i); + if (error) break; } printk(" %d done.\n",i); return error; @@ -1161,26 +994,23 @@ extern dev_t __init name_to_dev_t(const static int __init read_pagedir(void) { - unsigned long addr; - int i, n = swsusp_info.pagedir_pages; + int i = 0, n = swsusp_info.pagedir_pages; int error = 0; + suspend_pagedir_t *pgdir, *next; - addr = __get_free_pages(GFP_ATOMIC, pagedir_order); - if (!addr) + error = alloc_pagedir(&pagedir_nosave, nr_copy_pages, 0, n); + if (error < 0) return -ENOMEM; - pagedir_nosave = (struct pbe *)addr; pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); - - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { - unsigned long offset = swp_offset(swsusp_info.pagedir[i]); - if (offset) - error = bio_read_page(offset, (void *)addr); - else - error = -EFAULT; + pgdir_for_each(pgdir, next, pagedir_nosave) { + error = read_one_pagedir(pgdir, i); + if (error) break; + i++; } + BUG_ON(i != n); if (error) - free_pages((unsigned long)pagedir_nosave, pagedir_order); + pagedir_free(pagedir_nosave); return error; } @@ -1195,7 +1025,7 @@ static int __init read_suspend_image(voi if ((error = read_pagedir())) return error; if ((error = data_read())) - free_pages((unsigned long)pagedir_nosave, pagedir_order); + pagedir_free(pagedir_nosave); return error; } -- Hu Gang .-. /v\ // \\ Linux User /( )\ [204016] GPG Key ID ^^-^^ http://soulinfo.com/~hugang/hugang.asc ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: PATCH swsusp: page rellocation speed up 2005-01-12 12:49 ` hugang @ 2005-01-12 13:01 ` hugang 2005-01-12 13:46 ` hugang 2005-01-12 15:14 ` hugang 2 siblings, 0 replies; 5+ messages in thread From: hugang @ 2005-01-12 13:01 UTC (permalink / raw) To: Lukas Hejtmanek; +Cc: linux-kernel On Wed, Jan 12, 2005 at 08:49:49PM +0800, hugang@soulinfo.com wrote: > On Tue, Jan 11, 2005 at 02:01:23AM +0100, Lukas Hejtmanek wrote: > > Hello, > > > > attached patch should speed up page rellocation at time of resume. Please test. > > The diff is against 2.6.10-bk8 > > > .... > > really cool, Passed in my x86 and ppc. > > Here is a patch to make pagedir using non-continuity page, > 2.6.10 -> mm1 -> this patch -> my patch > support for PowerPc. 2.6.10 -> mm1 -> Lukas Hejtmanek's patch -> agx's patch -> pbe core -> this patch agx swsusp patch from http://honk.physik.uni-konstanz.de/~agx/linux-ppc/kernel/ --- 2.6.10-mm1-axg-swap_mem/arch/ppc/kernel/swsusp.S~old 2005-01-12 20:55:31.000000000 +0800 +++ 2.6.10-mm1-axg-swap_mem/arch/ppc/kernel/swsusp.S 2005-01-12 20:57:59.000000000 +0800 @@ -159,43 +159,57 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) sync isync - /* Load ptr the list of pages to copy in r3 */ - lis r11,(pagedir_nosave - KERNELBASE)@h - ori r11,r11,pagedir_nosave@l - lwz r10,0(r11) - tophys(r3,r10) - - /* Load the count of pages to copy in r4 */ - lis r11,(nr_copy_pages - KERNELBASE)@h - ori r11,r11,nr_copy_pages@l - lwz r4,0(r11) + /* Load ptr the list of pages to copy in r11 */ + lis r9,pagedir_nosave@ha + addi r9,r9,pagedir_nosave@l + tophys(r9,r9) + lwz r9, 0(r9) +#if 0 + twi 31,r0,0 /* triger trap */ +#endif + cmpwi r9, 0 + beq copy_loop_end - /* Copy the pages. This is a very basic implementation, to - * be replaced by something more cache efficient */ -1: - li r0,256 - mtctr r0 - lwz r11,0(r3) /* source */ - tophys(r5,r11) - lwz r10,4(r3) /* destination */ - tophys(r6,r10) -2: - lwz r8,0(r5) - lwz r9,4(r5) - lwz r10,8(r5) - lwz r11,12(r5) - addi r5,r5,16 - stw r8,0(r6) - stw r9,4(r6) - stw r10,8(r6) - stw r11,12(r6) - addi r6,r6,16 - bdnz 2b - addi r3,r3,16 - subi r4,r4,1 - cmpwi 0,r4,0 - bne 1b +copy_loop: + tophys(r9,r9) + lwz r6, 12(r9) + li r10, 0 + +copy_one_pgdir: + lwz r11, 4(r9) + addi r8,r10,1 + cmpwi r11, 0 + addi r7,r9,16 + beq copy_loop_end + li r0, 256 + mtctr r0 + lwz r9,0(r9) +#if 0 + twi 31,r0,0 /* triger trap */ +#endif + tophys(r10,r11) + tophys(r11,r9) + +copy_one_page: + lwz r0, 0(r11) + stw r0, 0(r10) + lwz r9, 4(r11) + stw r9, 4(r10) + lwz r0, 8(r11) + stw r0, 8(r10) + lwz r9, 12(r11) + addi r11,r11,16 + stw r9, 12(r10) + addi r10,r10,16 + bdnz copy_one_page + mr r10, r8 + cmplwi r10, 255 + mr r9, r7 + ble copy_one_pgdir + mr r9, r6 + bne copy_loop +copy_loop_end: /* Do a very simple cache flush/inval of the L1 to ensure * coherency of the icache -- Hu Gang .-. /v\ // \\ Linux User /( )\ [204016] GPG Key ID ^^-^^ http://soulinfo.com/~hugang/hugang.asc ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: PATCH swsusp: page rellocation speed up 2005-01-12 12:49 ` hugang 2005-01-12 13:01 ` hugang @ 2005-01-12 13:46 ` hugang 2005-01-12 15:14 ` hugang 2 siblings, 0 replies; 5+ messages in thread From: hugang @ 2005-01-12 13:46 UTC (permalink / raw) To: Lukas Hejtmanek; +Cc: linux-kernel On Wed, Jan 12, 2005 at 08:49:49PM +0800, hugang@soulinfo.com wrote: > On Tue, Jan 11, 2005 at 02:01:23AM +0100, Lukas Hejtmanek wrote: > > Hello, > > > > attached patch should speed up page rellocation at time of resume. Please test. > > The diff is against 2.6.10-bk8 > > > .... > > really cool, Passed in my x86 and ppc. > > Here is a patch to make pagedir using non-continuity page, > 2.6.10 -> mm1 -> this patch -> my patch > ... After more deep test, I found A bug, Here is a patch to fix it. --- 2.6.10-mm1-one-pbe-hg/kernel/power/pbe.h~mail 2005-01-12 21:10:39.000000000 +0800 +++ 2.6.10-mm1-one-pbe-hg/kernel/power/pbe.h 2005-01-12 21:21:41.000000000 +0800 @@ -267,7 +267,7 @@ static int __init check_pagedir(void) { void **c, *f; struct pbe *next, *pos; - int error, index, i; + int error, index; suspend_pagedir_t *addr = NULL; struct zone *zone; unsigned long zone_pfn; @@ -280,8 +280,9 @@ static int __init check_pagedir(void) } /* Clear orig address */ - pbe_for_each(pos, next, i, nr_copy_pages, pagedir_nosave) { - pr_debug("clear <%p>\n", (void*)pos->orig_address); + pbe_for_each(pos, next, index, nr_copy_pages, pagedir_nosave) { + pr_debug("clear <%p>, <%p>, %d\n", + pos, (void*)pos->orig_address, index); ClearPageNosaveFree(virt_to_page(pos->orig_address)); } --- 2.6.10-mm1-one-pbe-hg/kernel/power/swsusp.c~mail 2005-01-12 21:10:43.000000000 +0800 +++ 2.6.10-mm1-one-pbe-hg/kernel/power/swsusp.c 2005-01-12 21:43:41.000000000 +0800 @@ -380,6 +380,8 @@ static int write_pagedir(void) pgdir_for_each(pgdir, next, pagedir_nosave) { error = write_page((unsigned long)pgdir, &swsusp_info.pagedir[n]); + pr_debug("write_pagedir: <%p>, %lu\n", + pgdir, swp_offset(swsusp_info.pagedir[n])); if (error) break; n ++; } @@ -1008,6 +1010,7 @@ static int __init read_pagedir(void) pgdir_for_each(pgdir, next, pagedir_nosave) { error = read_one_pagedir(pgdir, i); if (error) break; + pgdir[ONE_PAGE_PBE_NUM-1].dummy.val = next ? next->dummy.val : 0; i++; } BUG_ON(i != n); -- Hu Gang .-. /v\ // \\ Linux User /( )\ [204016] GPG Key ID ^^-^^ http://soulinfo.com/~hugang/hugang.asc ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: PATCH swsusp: page rellocation speed up 2005-01-12 12:49 ` hugang 2005-01-12 13:01 ` hugang 2005-01-12 13:46 ` hugang @ 2005-01-12 15:14 ` hugang 2 siblings, 0 replies; 5+ messages in thread From: hugang @ 2005-01-12 15:14 UTC (permalink / raw) To: Pavel Machek; +Cc: linux-kernel On Wed, Jan 12, 2005 at 08:49:49PM +0800, hugang@soulinfo.com wrote: > On Tue, Jan 11, 2005 at 02:01:23AM +0100, Lukas Hejtmanek wrote: > > Hello, > > > > attached patch should speed up page rellocation at time of resume. Please test. > > The diff is against 2.6.10-bk8 > > > .... > > really cool, Passed in my x86 and ppc. > > Here is a patch to make pagedir using non-continuity page, > 2.6.10 -> mm1 -> this patch -> my patch > > I temporary split pbe function to pbe.h, useful merge. Here is my lastest patch for this. changes Fix a big bug in core code, Now it works in my PPC and X86, please test. 2.6.10 -> mm1 -> axg's patch -> Hejtmanek's -> core.diff diff -Nurp 2.6.10-mm1-one-pbe/arch/i386/power/swsusp.S 2.6.10-mm1-one-pbe-hg/arch/i386/power/swsusp.S --- 2.6.10-mm1-one-pbe/arch/i386/power/swsusp.S 2004-12-30 14:56:21.000000000 +0800 +++ 2.6.10-mm1-one-pbe-hg/arch/i386/power/swsusp.S 2005-01-12 20:09:01.000000000 +0800 @@ -31,24 +31,36 @@ ENTRY(swsusp_arch_resume) movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - movl pagedir_nosave, %ebx - xorl %eax, %eax - xorl %edx, %edx - .p2align 4,,7 + movl pagedir_nosave, %eax + test %eax, %eax + je copy_loop_end + movl $1024, %edx -copy_loop: - movl 4(%ebx,%edx),%edi - movl (%ebx,%edx),%esi - - movl $1024, %ecx - rep - movsl - - incl %eax - addl $16, %edx - cmpl nr_copy_pages,%eax - jb copy_loop .p2align 4,,7 +copy_loop_start: + movl 0xc(%eax), %ebp + xorl %ebx, %ebx + leal 0x0(%esi),%esi + +copy_one_pgdir: + movl 0x4(%eax),%edi + test %edi, %edi + je copy_loop_end + + movl (%eax), %esi + movl %edx, %ecx + repz movsl %ds:(%esi),%es:(%edi) + + incl %ebx + addl $0x10, %eax + cmpl $0xff, %ebx + jbe copy_one_pgdir + test %ebp, %ebp + movl %ebp, %eax + jne copy_loop_start + + .p2align 4,,7 +copy_loop_end: movl saved_context_esp, %esp movl saved_context_ebp, %ebp diff -Nurp 2.6.10-mm1-one-pbe/kernel/power/pbe.h 2.6.10-mm1-one-pbe-hg/kernel/power/pbe.h --- 2.6.10-mm1-one-pbe/kernel/power/pbe.h 1970-01-01 07:00:00.000000000 +0700 +++ 2.6.10-mm1-one-pbe-hg/kernel/power/pbe.h 2005-01-12 21:21:41.000000000 +0800 @@ -0,0 +1,381 @@ +static int mod_progress = 1; + +static void inline mod_printk_progress(int i) +{ + if (mod_progress == 0) mod_progress = 1; + if (!(i%100)) + printk( "\b\b\b\b%3d%%", i / mod_progress ); +} + +#define ONE_PAGE_PBE_NUM (PAGE_SIZE/sizeof(struct pbe)) +#define PBE_IS_PAGE_END(x) \ + ( PAGE_SIZE - sizeof(struct pbe) == ((x) - ((~(PAGE_SIZE - 1)) & (x))) ) + +#define pgdir_for_each(pos, n, head) \ + for(pos = head, n = pos ? (suspend_pagedir_t*)pos->dummy.val : NULL; \ + pos != NULL; \ + pos = n, n = pos ? (suspend_pagedir_t *)pos->dummy.val : NULL) + +#define pbe_for_each(pos, n, index, max, head) \ + for(pos = head, index = 0,\ + n = pos ? (struct pbe *)pos->dummy.val : NULL; \ + (pos != NULL) && (index < max); \ + pos = (PBE_IS_PAGE_END((unsigned long)pos)) ? n : \ + ((struct pbe *)((unsigned long)pos + sizeof(struct pbe))), \ + index ++, \ + n = pos ? (struct pbe*)pos->dummy.val : NULL) + +static inline struct pbe *find_pbe_by_index(struct pbe *pgdir, int index) +{ + unsigned long p = 0; + struct pbe *pbe, *next; + + pr_debug("find_pbe_by_index: %p, 0x%03x", pgdir, index); + pgdir_for_each(pbe, next, pgdir) { + if (p == index / ONE_PAGE_PBE_NUM) { + pbe = (struct pbe *)((unsigned long)pbe + + (index % ONE_PAGE_PBE_NUM) * sizeof(struct pbe)); + pr_debug(" %p, o{%p} c{%p}\n", + pbe, (void*)pbe->orig_address, (void*)pbe->address); + return pbe; + } + p ++; + } + return (NULL); +} + +static inline void pagedir_free(suspend_pagedir_t *head) +{ + suspend_pagedir_t *next, *cur; + pgdir_for_each(cur, next, head) + free_page((unsigned long)cur); +} + +/** + * write_one_pbe - + * @p: + * @data: + * @cur: + * + */ +static int inline write_one_pbe(struct pbe *p, void *data, int cur) +{ + int error = 0; + + mod_printk_progress(cur); + + pr_debug("write_one_pbe: %p, o{%p} c{%p} %d ", + p, (void *)p->orig_address, (void *)p->address, cur); + error = write_page((unsigned long)data, &p->swap_address); + if (error) return error; + pr_debug("%lu\n", swp_offset(p->swap_address)); + + return 0; +} + +static int bio_read_page(pgoff_t page_off, void * page); + +/** + * read_one_pbe - + * @p: + * @data: + * @cur + * + */ +static int inline read_one_pbe(struct pbe *p, void *data, int cur) +{ + int error = 0; + + mod_printk_progress(cur); + + pr_debug("read_one_pbe: %p, o{%p} c{%p} %lu\n", + p, (void *)p->orig_address, data, + swp_offset(p->swap_address)); + + error = bio_read_page(swp_offset(p->swap_address), data); + if (error) return error; + + return 0; +} + +/* + * Returns true if given address/order collides with any orig_address + */ +static int does_collide_order(unsigned long addr, int order) +{ + int i; + + for (i=0; i < (1<<order); i++) + if(!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE))) + return 1; + return 0; +} + +static void **eaten_memory = NULL; + +static void *swsusp_get_safe_free_page(int collide) +{ + void *addr = NULL; + void **c = eaten_memory; + + do { + if (addr) { + eaten_memory = (void**)addr; + *eaten_memory = c; + c = eaten_memory; + } + addr = (void*)__get_free_pages(GFP_ATOMIC | __GFP_COLD, 0); + if (!addr) + return NULL; + } while (collide && does_collide_order((unsigned long)addr, 0)); + + if (collide) + ClearPageNosaveFree(virt_to_page(addr)); + + return addr; +} + +/** + * alloc_one_pagedir - + * @prev: + * @collide: + * + */ +static suspend_pagedir_t * alloc_one_pagedir(suspend_pagedir_t *prev, + int collide) +{ + suspend_pagedir_t *pgdir = NULL; + int i; + + pgdir = (suspend_pagedir_t *)swsusp_get_safe_free_page(collide); + + /*pr_debug("pgdir: %p, %p, %d\n", + pgdir, prev, sizeof(suspend_pagedir_t)); */ + for (i = 0; i < ONE_PAGE_PBE_NUM; i++) { + pgdir[i].dummy.val = 0; + pgdir[i].address = 0; + pgdir[i].orig_address = 0; + if (prev) + prev[i].dummy.val= (unsigned long)pgdir; + } + + return (pgdir); +} + +/* calc_nums - Determine the nums of allocation needed for pagedir_save. */ +static int calc_nums(int nr_copy) +{ + int diff = 0, ret = 0; + do { + diff = (nr_copy / ONE_PAGE_PBE_NUM) - ret + 1; + if (diff) { + ret += diff; + nr_copy += diff; + } + } while (diff); + return nr_copy; +} + +/** + * alloc_pagedir - Allocate the page directory. + * @pbe: + * @pbe_nums: + * @collide: + * @page_nums: + */ +static int alloc_pagedir(struct pbe **pbe, int pbe_nums, + int collide, int page_nums) +{ + unsigned int nums = 0; + unsigned int after_alloc = pbe_nums; + suspend_pagedir_t *prev = NULL, *cur = NULL; + + if (page_nums) + after_alloc = ONE_PAGE_PBE_NUM * page_nums; + else + after_alloc = calc_nums(after_alloc); + pr_debug("alloc_pagedir: %d, %d\n", pbe_nums, after_alloc); + for (nums = 0 ; nums < after_alloc ; nums += ONE_PAGE_PBE_NUM) { + cur = alloc_one_pagedir(prev, collide); + pr_debug("alloc_one_pagedir: %p\n", cur); + if (!cur) { /* get page failed */ + goto no_mem; + } + if (nums == 0) { /* setup the head */ + *pbe = cur; + } + prev = cur; + } + return after_alloc - pbe_nums; + +no_mem: + pagedir_free(*pbe); + *pbe = NULL; + + return (-ENOMEM); +} + +static void __init eat_progress(void) +{ + char *eaten_progess = "-\\|/"; + static int eaten_i = 0; + + printk("\b%c", eaten_progess[eaten_i]); + eaten_i ++; + if (eaten_i > 3) eaten_i = 0; +} + +static int __init check_one_pbe(struct pbe *p, int cur) +{ + unsigned long addr = 0; + + pr_debug("check_one_pbe: %p %lu o{%p} ", + p, p->swap_address.val, (void*)p->orig_address); + addr = (unsigned long)swsusp_get_safe_free_page(1); + if(!addr) + return -ENOMEM; + pr_debug("c{%p} done\n", (void*)addr); + p->address = addr; + + return 0; +} + +static void __init swsusp_copy_pagedir(suspend_pagedir_t *d_pgdir, + suspend_pagedir_t *s_pgdir) +{ + int i = 0; + + while (s_pgdir != NULL) { + suspend_pagedir_t *s_next = (suspend_pagedir_t *)s_pgdir->dummy.val; + suspend_pagedir_t *d_next = (suspend_pagedir_t *)d_pgdir->dummy.val; + for (i = 0; i < ONE_PAGE_PBE_NUM; i++) { + d_pgdir->address = s_pgdir->address; + d_pgdir->orig_address = s_pgdir->orig_address; + d_pgdir->swap_address = s_pgdir->swap_address; + s_pgdir ++; d_pgdir ++; + } + d_pgdir = d_next; + s_pgdir = s_next; + }; +} + +/** + * 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 + */ +static int __init check_pagedir(void) +{ + void **c, *f; + struct pbe *next, *pos; + int error, index; + suspend_pagedir_t *addr = NULL; + struct zone *zone; + unsigned long zone_pfn; + + printk("Relocating pagedir ... "); + /* Set page flags */ + for_each_zone(zone) { + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) + SetPageNosaveFree(pfn_to_page(zone_pfn + zone->zone_start_pfn)); + } + + /* Clear orig address */ + pbe_for_each(pos, next, index, nr_copy_pages, pagedir_nosave) { + pr_debug("clear <%p>, <%p>, %d\n", + pos, (void*)pos->orig_address, index); + ClearPageNosaveFree(virt_to_page(pos->orig_address)); + } + + error = alloc_pagedir(&addr, nr_copy_pages, 1, swsusp_info.pagedir_pages); + if (error < 0) { + return error; + } + swsusp_copy_pagedir(addr, pagedir_nosave); + pagedir_free(pagedir_nosave); + + /* check copy address */ + pbe_for_each(pos, next, index, nr_copy_pages, addr) { + error = check_one_pbe(pos, index); + BUG_ON(error); + } + + /* free eaten memory */ + c = eaten_memory; + while (c) { + eat_progress(); + f = c; + c = *c; + free_pages((unsigned long)f, 0); + } + + printk(" done\n"); + + pagedir_nosave = addr; + + return 0; +} + + +static int __init read_one_pagedir(suspend_pagedir_t *pgdir, int i) +{ + unsigned long offset = swp_offset(swsusp_info.pagedir[i]); + unsigned long next; + int error = 0; + + next = pgdir->dummy.val; + pr_debug("read_one_pagedir: %p, %d, %lu, %p\n", + pgdir, i, offset, (void*)next); + if ((error = bio_read_page(offset, (void *)pgdir))) { + return error; + } + pgdir->dummy.val = next; + + return error; +} + +/** + * for_each_pbe_copy_back - + * + * That usefuly for help us writing the code in assemble code + * + */ +/* #define CREATE_ASM_CODE */ +#ifdef CREATE_ASM_CODE +#if 0 /* if your copy back code is running in real mode, enable it */ +#define GET_ADDRESS(x) __pa(x) +#else +#define GET_ADDRESS(x) (x) +#endif +asmlinkage void for_each_pbe_copy_back(void) +{ + struct pbe *pgdir, *next; + + pgdir = pagedir_nosave; + while (pgdir != NULL) { + unsigned long nums, i; + pgdir = (struct pbe *)GET_ADDRESS(pgdir); + next = (struct pbe*)pgdir->dummy.val; + for (nums = 0; nums < ONE_PAGE_PBE_NUM; nums++) { + register unsigned long *orig, *copy; + orig = (unsigned long *)pgdir->orig_address; + if (orig == 0) goto end; + orig = (unsigned long *)GET_ADDRESS(orig); + copy = (unsigned long *)GET_ADDRESS(pgdir->address); +#if 0 + memcpy(orig, copy, PAGE_SIZE); +#else + for (i = 0; i < PAGE_SIZE / sizeof(unsigned long); i+=4) { + *(orig + i) = *(copy + i); + *(orig + i+1) = *(copy + i+1); + *(orig + i+2) = *(copy + i+2); + *(orig + i+3) = *(copy + i+3); + } +#endif + pgdir ++; + } + pgdir = next; + } +end: + panic("just asm code"); +} +#endif diff -Nurp 2.6.10-mm1-one-pbe/kernel/power/swsusp.c 2.6.10-mm1-one-pbe-hg/kernel/power/swsusp.c --- 2.6.10-mm1-one-pbe/kernel/power/swsusp.c 2005-01-11 22:42:58.000000000 +0800 +++ 2.6.10-mm1-one-pbe-hg/kernel/power/swsusp.c 2005-01-12 22:56:18.000000000 +0800 @@ -71,12 +71,13 @@ #include <asm/io.h> #include "power.h" +#undef pr_debug +#define pr_debug printk /* References to section boundaries */ extern const void __nosave_begin, __nosave_end; /* Variables to be preserved over suspend */ -static int pagedir_order_check; static int nr_copy_pages_check; extern char resume_file[]; @@ -99,7 +100,6 @@ unsigned int nr_copy_pages __nosavedata */ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; static suspend_pagedir_t *pagedir_save; -static int pagedir_order __nosavedata = 0; #define SWSUSP_SIG "S1SUSPEND" @@ -260,6 +260,8 @@ static int write_page(unsigned long addr } +#include "pbe.h" + /** * data_free - Free the swap entries used by the saved image. * @@ -271,14 +273,15 @@ static void data_free(void) { swp_entry_t entry; int i; + struct pbe *next, *pos; - for (i = 0; i < nr_copy_pages; i++) { - entry = (pagedir_nosave + i)->swap_address; + pbe_for_each(pos, next, i, nr_copy_pages, pagedir_nosave) { + entry = pos->swap_address; if (entry.val) swap_free(entry); else break; - (pagedir_nosave + i)->swap_address = (swp_entry_t){0}; + pos->swap_address = (swp_entry_t){0}; } } @@ -293,17 +296,15 @@ static int data_write(void) { int error = 0; int i; - unsigned int mod = nr_copy_pages / 100; + struct pbe *pos, *next; - if (!mod) - mod = 1; + mod_progress = nr_copy_pages / 100; printk( "Writing data to swap (%d pages)... ", nr_copy_pages ); - for (i = 0; i < nr_copy_pages && !error; i++) { - if (!(i%mod)) - printk( "\b\b\b\b%3d%%", i / mod ); - error = write_page((pagedir_nosave+i)->address, - &((pagedir_nosave+i)->swap_address)); + pbe_for_each(pos, next, i, nr_copy_pages, pagedir_nosave) { + BUG_ON(pos->orig_address == 0); + error = write_one_pbe(pos, (void*)pos->address, i); + if (error) break; } printk("\b\b\b\bdone\n"); return error; @@ -373,15 +374,19 @@ static void free_pagedir_entries(void) static int write_pagedir(void) { - unsigned long addr = (unsigned long)pagedir_nosave; int error = 0; - int n = SUSPEND_PD_PAGES(nr_copy_pages); - int i; + int n = 0; + suspend_pagedir_t *pgdir, *next; - swsusp_info.pagedir_pages = n; + pgdir_for_each(pgdir, next, pagedir_nosave) { + error = write_page((unsigned long)pgdir, &swsusp_info.pagedir[n]); + pr_debug("write_pagedir: <%p>, %lu\n", + pgdir, swp_offset(swsusp_info.pagedir[n])); + if (error) break; + n ++; + } printk( "Writing pagedir (%d pages)\n", n); - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = write_page(addr, &swsusp_info.pagedir[i]); + swsusp_info.pagedir_pages = n; return error; } @@ -566,9 +571,9 @@ static void copy_data_pages(void) { struct zone *zone; unsigned long zone_pfn; - struct pbe * pbe = pagedir_nosave; + struct pbe * pbe = NULL; int to_copy = nr_copy_pages; - + for_each_zone(zone) { if (is_highmem(zone)) continue; @@ -576,92 +581,31 @@ static void copy_data_pages(void) for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) { if (saveable(zone, &zone_pfn)) { struct page * page; + pbe = find_pbe_by_index(pagedir_nosave, nr_copy_pages-to_copy); + BUG_ON(pbe == NULL); page = pfn_to_page(zone_pfn + zone->zone_start_pfn); pbe->orig_address = (long) page_address(page); + BUG_ON(pbe->orig_address == 0); + BUG_ON(pbe->address == 0); /* copy_page is not usable for copying task structs. */ memcpy((void *)pbe->address, (void *)pbe->orig_address, PAGE_SIZE); - pbe++; - to_copy--; + to_copy --; } } } BUG_ON(to_copy); } - -/** - * calc_order - Determine the order of allocation needed for pagedir_save. - * - * This looks tricky, but is just subtle. Please fix it some time. - * Since there are %nr_copy_pages worth of pages in the snapshot, we need - * to allocate enough contiguous space to hold - * (%nr_copy_pages * sizeof(struct pbe)), - * which has the saved/orig locations of the page.. - * - * SUSPEND_PD_PAGES() tells us how many pages we need to hold those - * structures, then we call get_bitmask_order(), which will tell us the - * last bit set in the number, starting with 1. (If we need 30 pages, that - * is 0x0000001e in hex. The last bit is the 5th, which is the order we - * would use to allocate 32 contiguous pages). - * - * Since we also need to save those pages, we add the number of pages that - * we need to nr_copy_pages, and in case of an overflow, do the - * calculation again to update the number of pages needed. - * - * With this model, we will tend to waste a lot of memory if we just cross - * an order boundary. Plus, the higher the order of allocation that we try - * to do, the more likely we are to fail in a low-memory situtation - * (though we're unlikely to get this far in such a case, since swsusp - * requires half of memory to be free anyway). - */ - - -static void calc_order(void) -{ - int diff = 0; - int order = 0; - - do { - diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; - if (diff) { - order += diff; - nr_copy_pages += 1 << diff; - } - } while(diff); - pagedir_order = order; -} - - -/** - * alloc_pagedir - Allocate the page directory. - * - * First, determine exactly how many contiguous pages we need and - * allocate them. - */ - -static int alloc_pagedir(void) -{ - calc_order(); - pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, - pagedir_order); - if (!pagedir_save) - return -ENOMEM; - memset(pagedir_save, 0, (1 << pagedir_order) * PAGE_SIZE); - pagedir_nosave = pagedir_save; - return 0; -} - /** * free_image_pages - Free pages allocated for snapshot */ static void free_image_pages(void) { - struct pbe * p; + struct pbe * p, * n; int i; - p = pagedir_save; - for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { + pbe_for_each(p, n, i, nr_copy_pages, pagedir_save) { if (p->address) { ClearPageNosave(virt_to_page(p->address)); free_page(p->address); @@ -677,10 +621,10 @@ static void free_image_pages(void) static int alloc_image_pages(void) { - struct pbe * p; + struct pbe * p, * n; int i; - for (i = 0, p = pagedir_save; i < nr_copy_pages; i++, p++) { + pbe_for_each(p, n, i, nr_copy_pages, pagedir_save) { p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); if (!p->address) return -ENOMEM; @@ -694,7 +638,7 @@ void swsusp_free(void) BUG_ON(PageNosave(virt_to_page(pagedir_save))); BUG_ON(PageNosaveFree(virt_to_page(pagedir_save))); free_image_pages(); - free_pages((unsigned long) pagedir_save, pagedir_order); + pagedir_free(pagedir_save); } @@ -752,18 +696,20 @@ static int swsusp_alloc(void) if (!enough_swap()) return -ENOSPC; - if ((error = alloc_pagedir())) { + if ((error = alloc_pagedir(&pagedir_save, nr_copy_pages, 0, 0)) < 0) { pr_debug("suspend: Allocating pagedir failed.\n"); return error; } + pr_debug("alloc_pagedir: addon %d\n", error); + nr_copy_pages += error; if ((error = alloc_image_pages())) { pr_debug("suspend: Allocating image pages failed.\n"); swsusp_free(); return error; } + pagedir_nosave = pagedir_save; nr_copy_pages_check = nr_copy_pages; - pagedir_order_check = pagedir_order; return 0; } @@ -856,7 +802,6 @@ int swsusp_suspend(void) asmlinkage int swsusp_restore(void) { BUG_ON (nr_copy_pages_check != nr_copy_pages); - BUG_ON (pagedir_order_check != pagedir_order); /* Even mappings of "global" things (vmalloc) need to be fixed */ __flush_tlb_global(); @@ -880,110 +825,6 @@ int swsusp_resume(void) return error; } - -/* More restore stuff */ - -/* - * Returns true if given address/order collides with any orig_address - */ -static int __init does_collide_order(unsigned long addr, int order) -{ - int i; - - for (i=0; i < (1<<order); i++) - if(!PageNosaveFree(virt_to_page(addr + i * PAGE_SIZE))) - return 1; - return 0; -} - -/* - * 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 - */ -static int __init check_pagedir(void) -{ - int i; - - for(i=0; i < nr_copy_pages; i++) { - unsigned long addr; - - do { - addr = get_zeroed_page(GFP_ATOMIC); - if(!addr) - return -ENOMEM; - } while (does_collide_order(addr,0)); - - (pagedir_nosave+i)->address = addr; - } - return 0; -} - -static int __init swsusp_pagedir_relocate(void) -{ - /* - * We have to avoid recursion (not to overflow kernel stack), - * and that's why code looks pretty cryptic - */ - suspend_pagedir_t *old_pagedir = pagedir_nosave; - void **eaten_memory = NULL; - void **c = eaten_memory, *m, *f; - int ret = 0; - struct zone *zone; - int i; - struct pbe *p; - unsigned long zone_pfn; - - printk("Relocating pagedir "); - - /* Set page flags */ - - for_each_zone(zone) { - for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) - SetPageNosaveFree(pfn_to_page(zone_pfn + - zone->zone_start_pfn)); - } - - /* Clear orig address */ - - for(i = 0, p = pagedir_nosave; i < nr_copy_pages; i++, p++) { - ClearPageNosaveFree(virt_to_page(p->orig_address)); - } - - if (!does_collide_order((unsigned long)old_pagedir, pagedir_order)) { - printk("not necessary\n"); - return check_pagedir(); - } - - while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) { - if (!does_collide_order((unsigned long)m, pagedir_order)) - break; - eaten_memory = m; - printk( "." ); - *eaten_memory = c; - c = eaten_memory; - } - - if (!m) { - printk("out of memory\n"); - ret = -ENOMEM; - } else { - pagedir_nosave = - memcpy(m, old_pagedir, PAGE_SIZE << pagedir_order); - } - - c = eaten_memory; - while (c) { - printk(":"); - f = c; - c = *c; - free_pages((unsigned long)f, pagedir_order); - } - if (ret) - return ret; - printk("|\n"); - return check_pagedir(); -} - /** * Using bio to read from swap. * This code requires a bit more work than just using buffer heads @@ -1098,7 +939,6 @@ static int __init check_header(void) return -EPERM; } nr_copy_pages = swsusp_info.image_pages; - pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); return error; } @@ -1134,23 +974,20 @@ static int __init check_sig(void) static int __init data_read(void) { - struct pbe * p; + struct pbe * p, * n; int error; int i; - int mod = nr_copy_pages / 100; - if (!mod) - mod = 1; + if ((error = check_pagedir())) { + return -ENOMEM; + } - if ((error = swsusp_pagedir_relocate())) - return error; + mod_progress = nr_copy_pages / 100; printk( "Reading image data (%d pages): ", nr_copy_pages ); - for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { - if (!(i%mod)) - printk( "\b\b\b\b%3d%%", i / mod ); - error = bio_read_page(swp_offset(p->swap_address), - (void *)p->address); + pbe_for_each(p, n, i, nr_copy_pages, pagedir_nosave) { + error = read_one_pbe(p, (void*)p->address, i); + if (error) break; } printk(" %d done.\n",i); return error; @@ -1161,26 +998,24 @@ extern dev_t __init name_to_dev_t(const static int __init read_pagedir(void) { - unsigned long addr; - int i, n = swsusp_info.pagedir_pages; + int i = 0, n = swsusp_info.pagedir_pages; int error = 0; + suspend_pagedir_t *pgdir, *next; - addr = __get_free_pages(GFP_ATOMIC, pagedir_order); - if (!addr) + error = alloc_pagedir(&pagedir_nosave, nr_copy_pages, 0, n); + if (error < 0) return -ENOMEM; - pagedir_nosave = (struct pbe *)addr; pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); - - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { - unsigned long offset = swp_offset(swsusp_info.pagedir[i]); - if (offset) - error = bio_read_page(offset, (void *)addr); - else - error = -EFAULT; + pgdir_for_each(pgdir, next, pagedir_nosave) { + error = read_one_pagedir(pgdir, i); + if (error) break; + pgdir[ONE_PAGE_PBE_NUM-1].dummy.val = pgdir ? pgdir ->dummy.val : 0; + i++; } + BUG_ON(i != n); if (error) - free_pages((unsigned long)pagedir_nosave, pagedir_order); + pagedir_free(pagedir_nosave); return error; } @@ -1195,7 +1030,7 @@ static int __init read_suspend_image(voi if ((error = read_pagedir())) return error; if ((error = data_read())) - free_pages((unsigned long)pagedir_nosave, pagedir_order); + pagedir_free(pagedir_nosave); return error; } 2.6.10 -> mm1 -> axg's patch -> Hejtmanek's -> core.diff -> ppc.diff diff -Nurp 2.6.10-mm1-axg/arch/ppc/kernel/swsusp.S 2.6.10-mm1-axg-swap_mem/arch/ppc/kernel/swsusp.S --- 2.6.10-mm1-axg/arch/ppc/kernel/swsusp.S 2005-01-06 11:36:19.000000000 +0800 +++ 2.6.10-mm1-axg-swap_mem/arch/ppc/kernel/swsusp.S 2005-01-12 22:34:15.000000000 +0800 @@ -159,43 +159,56 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) sync isync - /* Load ptr the list of pages to copy in r3 */ - lis r11,(pagedir_nosave - KERNELBASE)@h - ori r11,r11,pagedir_nosave@l - lwz r10,0(r11) - tophys(r3,r10) - - /* Load the count of pages to copy in r4 */ - lis r11,(nr_copy_pages - KERNELBASE)@h - ori r11,r11,nr_copy_pages@l - lwz r4,0(r11) + /* Load ptr the list of pages to copy in r9 */ + lis r11,(pagedir_nosave - KERNELBASE)@h + ori r11,r11,pagedir_nosave@l + lwz r9,0(r11) +#if 0 + twi 31,r0,0 /* triger trap */ +#endif + cmpwi r9, 0 + beq copy_loop_end - /* Copy the pages. This is a very basic implementation, to - * be replaced by something more cache efficient */ -1: - li r0,256 - mtctr r0 - lwz r11,0(r3) /* source */ - tophys(r5,r11) - lwz r10,4(r3) /* destination */ - tophys(r6,r10) -2: - lwz r8,0(r5) - lwz r9,4(r5) - lwz r10,8(r5) - lwz r11,12(r5) - addi r5,r5,16 - stw r8,0(r6) - stw r9,4(r6) - stw r10,8(r6) - stw r11,12(r6) - addi r6,r6,16 - bdnz 2b - addi r3,r3,16 - subi r4,r4,1 - cmpwi 0,r4,0 - bne 1b +copy_loop: + tophys(r9,r9) + lwz r6, 12(r9) + li r10, 0 + +copy_one_pgdir: + lwz r11, 4(r9) + addi r8,r10,1 + cmpwi r11, 0 + addi r7,r9,16 + beq copy_loop_end + li r0, 256 + mtctr r0 + lwz r9,0(r9) +#if 0 + twi 31,r0,0 /* triger trap */ +#endif + tophys(r10,r11) + tophys(r11,r9) + +copy_one_page: + lwz r0, 0(r11) + stw r0, 0(r10) + lwz r9, 4(r11) + stw r9, 4(r10) + lwz r0, 8(r11) + stw r0, 8(r10) + lwz r9, 12(r11) + addi r11,r11,16 + stw r9, 12(r10) + addi r10,r10,16 + bdnz copy_one_page + mr r10, r8 + cmplwi r10, 255 + mr r9, r7 + ble copy_one_pgdir + mr r9, r6 + bne copy_loop +copy_loop_end: /* Do a very simple cache flush/inval of the L1 to ensure * coherency of the icache -- Hu Gang .-. /v\ // \\ Linux User /( )\ [204016] GPG Key ID ^^-^^ http://soulinfo.com/~hugang/hugang.asc ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2005-01-12 15:18 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2005-01-11 1:01 PATCH swsusp: page rellocation speed up Lukas Hejtmanek 2005-01-12 12:49 ` hugang 2005-01-12 13:01 ` hugang 2005-01-12 13:46 ` hugang 2005-01-12 15:14 ` hugang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
Powered by JetHome