aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2006-06-23 05:04:48 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-23 10:43:00 -0400
commit968808b8956e332e556b1eae9b4f7df77518f53b (patch)
treee90598c5640df24a250aa0beb3e526f2571ed6e2
parent7bff24e255ee11ecbc304315a252fcbd84f9ffce (diff)
[PATCH] swsusp: use less memory during resume
Make swsusp allocate only as much memory as needed to store the image data and metadata during resume. Without this patch swsusp additionally allocates many page frames that will conflict with the "original" locations of the image data and are considered as "unsafe", treating them as "eaten" pages (ie. allocated but unusable). The patch makes swsusp allocate as many pages as it'll need to store the data read from the image in one shot, creating a list of allocated "safe" pages, and use the observation that all pages allocated by it are marked with the PG_nosave and PG_nosave_free flags set.  Namely, when it's about to load an image page, swsusp can check whether the page frame corresponding to the "original" location of this page has been allocated (ie. if the page frame has the PG_nosave and PG_nosave_free flags set) and if so, it can load the page directly into this page frame.  Otherwise it uses an allocated "safe" page from the list to store the data that will be copied to their "original" location later on. This allows us to save many page copyings and page allocations during resume and in the future it may allow us to load images greater than 50% of the normal zone. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: "Pavel Machek" <pavel@suse.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--kernel/power/power.h2
-rw-r--r--kernel/power/snapshot.c141
2 files changed, 85 insertions, 58 deletions
diff --git a/kernel/power/power.h b/kernel/power/power.h
index c81f0ed3eeba..98c41423f3b1 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -55,7 +55,7 @@ struct snapshot_handle {
55 unsigned int page; 55 unsigned int page;
56 unsigned int page_offset; 56 unsigned int page_offset;
57 unsigned int prev; 57 unsigned int prev;
58 struct pbe *pbe; 58 struct pbe *pbe, *last_pbe;
59 void *buffer; 59 void *buffer;
60 unsigned int buf_offset; 60 unsigned int buf_offset;
61}; 61};
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index 513eef3391a0..3d9284100b22 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -401,62 +401,29 @@ static inline void create_pbe_list(struct pbe *pblist, unsigned int nr_pages)
401 } 401 }
402} 402}
403 403
404/** 404static unsigned int unsafe_pages;
405 * On resume it is necessary to trace and eventually free the unsafe
406 * pages that have been allocated, because they are needed for I/O
407 * (on x86-64 we likely will "eat" these pages once again while
408 * creating the temporary page translation tables)
409 */
410
411struct eaten_page {
412 struct eaten_page *next;
413 char padding[PAGE_SIZE - sizeof(void *)];
414};
415
416static struct eaten_page *eaten_pages = NULL;
417
418static void release_eaten_pages(void)
419{
420 struct eaten_page *p, *q;
421
422 p = eaten_pages;
423 while (p) {
424 q = p->next;
425 /* We don't want swsusp_free() to free this page again */
426 ClearPageNosave(virt_to_page(p));
427 free_page((unsigned long)p);
428 p = q;
429 }
430 eaten_pages = NULL;
431}
432 405
433/** 406/**
434 * @safe_needed - on resume, for storing the PBE list and the image, 407 * @safe_needed - on resume, for storing the PBE list and the image,
435 * we can only use memory pages that do not conflict with the pages 408 * we can only use memory pages that do not conflict with the pages
436 * which had been used before suspend. 409 * used before suspend.
437 * 410 *
438 * The unsafe pages are marked with the PG_nosave_free flag 411 * The unsafe pages are marked with the PG_nosave_free flag
439 * 412 * and we count them using unsafe_pages
440 * Allocated but unusable (ie eaten) memory pages should be marked
441 * so that swsusp_free() can release them
442 */ 413 */
443 414
444static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) 415static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed)
445{ 416{
446 void *res; 417 void *res;
447 418
419 res = (void *)get_zeroed_page(gfp_mask);
448 if (safe_needed) 420 if (safe_needed)
449 do { 421 while (res && PageNosaveFree(virt_to_page(res))) {
422 /* The page is unsafe, mark it for swsusp_free() */
423 SetPageNosave(virt_to_page(res));
424 unsafe_pages++;
450 res = (void *)get_zeroed_page(gfp_mask); 425 res = (void *)get_zeroed_page(gfp_mask);
451 if (res && PageNosaveFree(virt_to_page(res))) { 426 }
452 /* This is for swsusp_free() */
453 SetPageNosave(virt_to_page(res));
454 ((struct eaten_page *)res)->next = eaten_pages;
455 eaten_pages = res;
456 }
457 } while (res && PageNosaveFree(virt_to_page(res)));
458 else
459 res = (void *)get_zeroed_page(gfp_mask);
460 if (res) { 427 if (res) {
461 SetPageNosave(virt_to_page(res)); 428 SetPageNosave(virt_to_page(res));
462 SetPageNosaveFree(virt_to_page(res)); 429 SetPageNosaveFree(virt_to_page(res));
@@ -751,6 +718,8 @@ static int mark_unsafe_pages(struct pbe *pblist)
751 return -EFAULT; 718 return -EFAULT;
752 } 719 }
753 720
721 unsafe_pages = 0;
722
754 return 0; 723 return 0;
755} 724}
756 725
@@ -828,42 +797,99 @@ static inline struct pbe *unpack_orig_addresses(unsigned long *buf,
828} 797}
829 798
830/** 799/**
831 * create_image - use metadata contained in the PBE list 800 * prepare_image - use metadata contained in the PBE list
832 * pointed to by pagedir_nosave to mark the pages that will 801 * pointed to by pagedir_nosave to mark the pages that will
833 * be overwritten in the process of restoring the system 802 * be overwritten in the process of restoring the system
834 * memory state from the image and allocate memory for 803 * memory state from the image ("unsafe" pages) and allocate
835 * the image avoiding these pages 804 * memory for the image
805 *
806 * The idea is to allocate the PBE list first and then
807 * allocate as many pages as it's needed for the image data,
808 * but not to assign these pages to the PBEs initially.
809 * Instead, we just mark them as allocated and create a list
810 * of "safe" which will be used later
836 */ 811 */
837 812
838static int create_image(struct snapshot_handle *handle) 813struct safe_page {
814 struct safe_page *next;
815 char padding[PAGE_SIZE - sizeof(void *)];
816};
817
818static struct safe_page *safe_pages;
819
820static int prepare_image(struct snapshot_handle *handle)
839{ 821{
840 int error = 0; 822 int error = 0;
841 struct pbe *p, *pblist; 823 unsigned int nr_pages = nr_copy_pages;
824 struct pbe *p, *pblist = NULL;
842 825
843 p = pagedir_nosave; 826 p = pagedir_nosave;
844 error = mark_unsafe_pages(p); 827 error = mark_unsafe_pages(p);
845 if (!error) { 828 if (!error) {
846 pblist = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1); 829 pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1);
847 if (pblist) 830 if (pblist)
848 copy_page_backup_list(pblist, p); 831 copy_page_backup_list(pblist, p);
849 free_pagedir(p, 0); 832 free_pagedir(p, 0);
850 if (!pblist) 833 if (!pblist)
851 error = -ENOMEM; 834 error = -ENOMEM;
852 } 835 }
853 if (!error) 836 safe_pages = NULL;
854 error = alloc_data_pages(pblist, GFP_ATOMIC, 1); 837 if (!error && nr_pages > unsafe_pages) {
838 nr_pages -= unsafe_pages;
839 while (nr_pages--) {
840 struct safe_page *ptr;
841
842 ptr = (struct safe_page *)get_zeroed_page(GFP_ATOMIC);
843 if (!ptr) {
844 error = -ENOMEM;
845 break;
846 }
847 if (!PageNosaveFree(virt_to_page(ptr))) {
848 /* The page is "safe", add it to the list */
849 ptr->next = safe_pages;
850 safe_pages = ptr;
851 }
852 /* Mark the page as allocated */
853 SetPageNosave(virt_to_page(ptr));
854 SetPageNosaveFree(virt_to_page(ptr));
855 }
856 }
855 if (!error) { 857 if (!error) {
856 release_eaten_pages();
857 pagedir_nosave = pblist; 858 pagedir_nosave = pblist;
858 } else { 859 } else {
859 pagedir_nosave = NULL;
860 handle->pbe = NULL; 860 handle->pbe = NULL;
861 nr_copy_pages = 0; 861 swsusp_free();
862 nr_meta_pages = 0;
863 } 862 }
864 return error; 863 return error;
865} 864}
866 865
866static void *get_buffer(struct snapshot_handle *handle)
867{
868 struct pbe *pbe = handle->pbe, *last = handle->last_pbe;
869 struct page *page = virt_to_page(pbe->orig_address);
870
871 if (PageNosave(page) && PageNosaveFree(page)) {
872 /*
873 * We have allocated the "original" page frame and we can
874 * use it directly to store the read page
875 */
876 pbe->address = 0;
877 if (last && last->next)
878 last->next = NULL;
879 return (void *)pbe->orig_address;
880 }
881 /*
882 * The "original" page frame has not been allocated and we have to
883 * use a "safe" page frame to store the read page
884 */
885 pbe->address = (unsigned long)safe_pages;
886 safe_pages = safe_pages->next;
887 if (last)
888 last->next = pbe;
889 handle->last_pbe = pbe;
890 return (void *)pbe->address;
891}
892
867/** 893/**
868 * snapshot_write_next - used for writing the system memory snapshot. 894 * snapshot_write_next - used for writing the system memory snapshot.
869 * 895 *
@@ -908,15 +934,16 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count)
908 } else if (handle->prev <= nr_meta_pages) { 934 } else if (handle->prev <= nr_meta_pages) {
909 handle->pbe = unpack_orig_addresses(buffer, handle->pbe); 935 handle->pbe = unpack_orig_addresses(buffer, handle->pbe);
910 if (!handle->pbe) { 936 if (!handle->pbe) {
911 error = create_image(handle); 937 error = prepare_image(handle);
912 if (error) 938 if (error)
913 return error; 939 return error;
914 handle->pbe = pagedir_nosave; 940 handle->pbe = pagedir_nosave;
915 handle->buffer = (void *)handle->pbe->address; 941 handle->last_pbe = NULL;
942 handle->buffer = get_buffer(handle);
916 } 943 }
917 } else { 944 } else {
918 handle->pbe = handle->pbe->next; 945 handle->pbe = handle->pbe->next;
919 handle->buffer = (void *)handle->pbe->address; 946 handle->buffer = get_buffer(handle);
920 } 947 }
921 handle->prev = handle->page; 948 handle->prev = handle->page;
922 } 949 }