diff options
Diffstat (limited to 'kernel/power/snapshot.c')
| -rw-r--r-- | kernel/power/snapshot.c | 80 |
1 files changed, 78 insertions, 2 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 33e2e4a819f9..523a451b45d3 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
| @@ -39,6 +39,14 @@ static int swsusp_page_is_free(struct page *); | |||
| 39 | static void swsusp_set_page_forbidden(struct page *); | 39 | static void swsusp_set_page_forbidden(struct page *); |
| 40 | static void swsusp_unset_page_forbidden(struct page *); | 40 | static void swsusp_unset_page_forbidden(struct page *); |
| 41 | 41 | ||
| 42 | /* | ||
| 43 | * Preferred image size in bytes (tunable via /sys/power/image_size). | ||
| 44 | * When it is set to N, swsusp will do its best to ensure the image | ||
| 45 | * size will not exceed N bytes, but if that is impossible, it will | ||
| 46 | * try to create the smallest image possible. | ||
| 47 | */ | ||
| 48 | unsigned long image_size = 500 * 1024 * 1024; | ||
| 49 | |||
| 42 | /* List of PBEs needed for restoring the pages that were allocated before | 50 | /* List of PBEs needed for restoring the pages that were allocated before |
| 43 | * the suspend and included in the suspend image, but have also been | 51 | * the suspend and included in the suspend image, but have also been |
| 44 | * allocated by the "resume" kernel, so their contents cannot be written | 52 | * allocated by the "resume" kernel, so their contents cannot be written |
| @@ -840,7 +848,7 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn) | |||
| 840 | * pages. | 848 | * pages. |
| 841 | */ | 849 | */ |
| 842 | 850 | ||
| 843 | unsigned int count_highmem_pages(void) | 851 | static unsigned int count_highmem_pages(void) |
| 844 | { | 852 | { |
| 845 | struct zone *zone; | 853 | struct zone *zone; |
| 846 | unsigned int n = 0; | 854 | unsigned int n = 0; |
| @@ -902,7 +910,7 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn) | |||
| 902 | * pages. | 910 | * pages. |
| 903 | */ | 911 | */ |
| 904 | 912 | ||
| 905 | unsigned int count_data_pages(void) | 913 | static unsigned int count_data_pages(void) |
| 906 | { | 914 | { |
| 907 | struct zone *zone; | 915 | struct zone *zone; |
| 908 | unsigned long pfn, max_zone_pfn; | 916 | unsigned long pfn, max_zone_pfn; |
| @@ -1058,6 +1066,74 @@ void swsusp_free(void) | |||
| 1058 | buffer = NULL; | 1066 | buffer = NULL; |
| 1059 | } | 1067 | } |
| 1060 | 1068 | ||
| 1069 | /** | ||
| 1070 | * swsusp_shrink_memory - Try to free as much memory as needed | ||
| 1071 | * | ||
| 1072 | * ... but do not OOM-kill anyone | ||
| 1073 | * | ||
| 1074 | * Notice: all userland should be stopped before it is called, or | ||
| 1075 | * livelock is possible. | ||
| 1076 | */ | ||
| 1077 | |||
| 1078 | #define SHRINK_BITE 10000 | ||
| 1079 | static inline unsigned long __shrink_memory(long tmp) | ||
| 1080 | { | ||
| 1081 | if (tmp > SHRINK_BITE) | ||
| 1082 | tmp = SHRINK_BITE; | ||
| 1083 | return shrink_all_memory(tmp); | ||
| 1084 | } | ||
| 1085 | |||
| 1086 | int swsusp_shrink_memory(void) | ||
| 1087 | { | ||
| 1088 | long tmp; | ||
| 1089 | struct zone *zone; | ||
| 1090 | unsigned long pages = 0; | ||
| 1091 | unsigned int i = 0; | ||
| 1092 | char *p = "-\\|/"; | ||
| 1093 | struct timeval start, stop; | ||
| 1094 | |||
| 1095 | printk(KERN_INFO "PM: Shrinking memory... "); | ||
| 1096 | do_gettimeofday(&start); | ||
| 1097 | do { | ||
| 1098 | long size, highmem_size; | ||
| 1099 | |||
| 1100 | highmem_size = count_highmem_pages(); | ||
| 1101 | size = count_data_pages() + PAGES_FOR_IO + SPARE_PAGES; | ||
| 1102 | tmp = size; | ||
| 1103 | size += highmem_size; | ||
| 1104 | for_each_populated_zone(zone) { | ||
| 1105 | tmp += snapshot_additional_pages(zone); | ||
| 1106 | if (is_highmem(zone)) { | ||
| 1107 | highmem_size -= | ||
| 1108 | zone_page_state(zone, NR_FREE_PAGES); | ||
| 1109 | } else { | ||
| 1110 | tmp -= zone_page_state(zone, NR_FREE_PAGES); | ||
| 1111 | tmp += zone->lowmem_reserve[ZONE_NORMAL]; | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | if (highmem_size < 0) | ||
| 1116 | highmem_size = 0; | ||
| 1117 | |||
| 1118 | tmp += highmem_size; | ||
| 1119 | if (tmp > 0) { | ||
| 1120 | tmp = __shrink_memory(tmp); | ||
| 1121 | if (!tmp) | ||
| 1122 | return -ENOMEM; | ||
| 1123 | pages += tmp; | ||
| 1124 | } else if (size > image_size / PAGE_SIZE) { | ||
| 1125 | tmp = __shrink_memory(size - (image_size / PAGE_SIZE)); | ||
| 1126 | pages += tmp; | ||
| 1127 | } | ||
| 1128 | printk("\b%c", p[i++%4]); | ||
| 1129 | } while (tmp > 0); | ||
| 1130 | do_gettimeofday(&stop); | ||
| 1131 | printk("\bdone (%lu pages freed)\n", pages); | ||
| 1132 | swsusp_show_speed(&start, &stop, pages, "Freed"); | ||
| 1133 | |||
| 1134 | return 0; | ||
| 1135 | } | ||
| 1136 | |||
| 1061 | #ifdef CONFIG_HIGHMEM | 1137 | #ifdef CONFIG_HIGHMEM |
| 1062 | /** | 1138 | /** |
| 1063 | * count_pages_for_highmem - compute the number of non-highmem pages | 1139 | * count_pages_for_highmem - compute the number of non-highmem pages |
