diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2010-09-11 14:58:27 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2010-09-11 15:03:53 -0400 |
commit | 6715045ddc7472a22be5e49d4047d2d89b391f45 (patch) | |
tree | d9a48095e915407f6d54db29cfce44c201ae9cef /kernel | |
parent | 0109c2c48d062a04685638926a35ed20153fedc8 (diff) |
PM / Hibernate: Avoid hitting OOM during preallocation of memory
There is a problem in hibernate_preallocate_memory() that it calls
preallocate_image_memory() with an argument that may be greater than
the total number of available non-highmem memory pages. If that's
the case, the OOM condition is guaranteed to trigger, which in turn
can cause significant slowdown to occur during hibernation.
To avoid that, make preallocate_image_memory() adjust its argument
before calling preallocate_image_pages(), so that the total number of
saveable non-highem pages left is not less than the minimum size of
a hibernation image. Change hibernate_preallocate_memory() to try to
allocate from highmem if the number of pages allocated by
preallocate_image_memory() is too low.
Modify free_unnecessary_pages() to take all possible memory
allocation patterns into account.
Reported-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Tested-by: M. Vefa Bicakci <bicave@superonline.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/power/snapshot.c | 85 |
1 files changed, 65 insertions, 20 deletions
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 5e7edfb05e66..5209b39e6982 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -1122,9 +1122,19 @@ static unsigned long preallocate_image_pages(unsigned long nr_pages, gfp_t mask) | |||
1122 | return nr_alloc; | 1122 | return nr_alloc; |
1123 | } | 1123 | } |
1124 | 1124 | ||
1125 | static unsigned long preallocate_image_memory(unsigned long nr_pages) | 1125 | static unsigned long preallocate_image_memory(unsigned long nr_pages, |
1126 | unsigned long avail_normal) | ||
1126 | { | 1127 | { |
1127 | return preallocate_image_pages(nr_pages, GFP_IMAGE); | 1128 | unsigned long alloc; |
1129 | |||
1130 | if (avail_normal <= alloc_normal) | ||
1131 | return 0; | ||
1132 | |||
1133 | alloc = avail_normal - alloc_normal; | ||
1134 | if (nr_pages < alloc) | ||
1135 | alloc = nr_pages; | ||
1136 | |||
1137 | return preallocate_image_pages(alloc, GFP_IMAGE); | ||
1128 | } | 1138 | } |
1129 | 1139 | ||
1130 | #ifdef CONFIG_HIGHMEM | 1140 | #ifdef CONFIG_HIGHMEM |
@@ -1170,15 +1180,22 @@ static inline unsigned long preallocate_highmem_fraction(unsigned long nr_pages, | |||
1170 | */ | 1180 | */ |
1171 | static void free_unnecessary_pages(void) | 1181 | static void free_unnecessary_pages(void) |
1172 | { | 1182 | { |
1173 | unsigned long save_highmem, to_free_normal, to_free_highmem; | 1183 | unsigned long save, to_free_normal, to_free_highmem; |
1174 | 1184 | ||
1175 | to_free_normal = alloc_normal - count_data_pages(); | 1185 | save = count_data_pages(); |
1176 | save_highmem = count_highmem_pages(); | 1186 | if (alloc_normal >= save) { |
1177 | if (alloc_highmem > save_highmem) { | 1187 | to_free_normal = alloc_normal - save; |
1178 | to_free_highmem = alloc_highmem - save_highmem; | 1188 | save = 0; |
1189 | } else { | ||
1190 | to_free_normal = 0; | ||
1191 | save -= alloc_normal; | ||
1192 | } | ||
1193 | save += count_highmem_pages(); | ||
1194 | if (alloc_highmem >= save) { | ||
1195 | to_free_highmem = alloc_highmem - save; | ||
1179 | } else { | 1196 | } else { |
1180 | to_free_highmem = 0; | 1197 | to_free_highmem = 0; |
1181 | to_free_normal -= save_highmem - alloc_highmem; | 1198 | to_free_normal -= save - alloc_highmem; |
1182 | } | 1199 | } |
1183 | 1200 | ||
1184 | memory_bm_position_reset(©_bm); | 1201 | memory_bm_position_reset(©_bm); |
@@ -1259,7 +1276,7 @@ int hibernate_preallocate_memory(void) | |||
1259 | { | 1276 | { |
1260 | struct zone *zone; | 1277 | struct zone *zone; |
1261 | unsigned long saveable, size, max_size, count, highmem, pages = 0; | 1278 | unsigned long saveable, size, max_size, count, highmem, pages = 0; |
1262 | unsigned long alloc, save_highmem, pages_highmem; | 1279 | unsigned long alloc, save_highmem, pages_highmem, avail_normal; |
1263 | struct timeval start, stop; | 1280 | struct timeval start, stop; |
1264 | int error; | 1281 | int error; |
1265 | 1282 | ||
@@ -1296,6 +1313,7 @@ int hibernate_preallocate_memory(void) | |||
1296 | else | 1313 | else |
1297 | count += zone_page_state(zone, NR_FREE_PAGES); | 1314 | count += zone_page_state(zone, NR_FREE_PAGES); |
1298 | } | 1315 | } |
1316 | avail_normal = count; | ||
1299 | count += highmem; | 1317 | count += highmem; |
1300 | count -= totalreserve_pages; | 1318 | count -= totalreserve_pages; |
1301 | 1319 | ||
@@ -1310,12 +1328,21 @@ int hibernate_preallocate_memory(void) | |||
1310 | */ | 1328 | */ |
1311 | if (size >= saveable) { | 1329 | if (size >= saveable) { |
1312 | pages = preallocate_image_highmem(save_highmem); | 1330 | pages = preallocate_image_highmem(save_highmem); |
1313 | pages += preallocate_image_memory(saveable - pages); | 1331 | pages += preallocate_image_memory(saveable - pages, avail_normal); |
1314 | goto out; | 1332 | goto out; |
1315 | } | 1333 | } |
1316 | 1334 | ||
1317 | /* Estimate the minimum size of the image. */ | 1335 | /* Estimate the minimum size of the image. */ |
1318 | pages = minimum_image_size(saveable); | 1336 | pages = minimum_image_size(saveable); |
1337 | /* | ||
1338 | * To avoid excessive pressure on the normal zone, leave room in it to | ||
1339 | * accommodate an image of the minimum size (unless it's already too | ||
1340 | * small, in which case don't preallocate pages from it at all). | ||
1341 | */ | ||
1342 | if (avail_normal > pages) | ||
1343 | avail_normal -= pages; | ||
1344 | else | ||
1345 | avail_normal = 0; | ||
1319 | if (size < pages) | 1346 | if (size < pages) |
1320 | size = min_t(unsigned long, pages, max_size); | 1347 | size = min_t(unsigned long, pages, max_size); |
1321 | 1348 | ||
@@ -1336,16 +1363,34 @@ int hibernate_preallocate_memory(void) | |||
1336 | */ | 1363 | */ |
1337 | pages_highmem = preallocate_image_highmem(highmem / 2); | 1364 | pages_highmem = preallocate_image_highmem(highmem / 2); |
1338 | alloc = (count - max_size) - pages_highmem; | 1365 | alloc = (count - max_size) - pages_highmem; |
1339 | pages = preallocate_image_memory(alloc); | 1366 | pages = preallocate_image_memory(alloc, avail_normal); |
1340 | if (pages < alloc) | 1367 | if (pages < alloc) { |
1341 | goto err_out; | 1368 | /* We have exhausted non-highmem pages, try highmem. */ |
1342 | size = max_size - size; | 1369 | alloc -= pages; |
1343 | alloc = size; | 1370 | pages += pages_highmem; |
1344 | size = preallocate_highmem_fraction(size, highmem, count); | 1371 | pages_highmem = preallocate_image_highmem(alloc); |
1345 | pages_highmem += size; | 1372 | if (pages_highmem < alloc) |
1346 | alloc -= size; | 1373 | goto err_out; |
1347 | pages += preallocate_image_memory(alloc); | 1374 | pages += pages_highmem; |
1348 | pages += pages_highmem; | 1375 | /* |
1376 | * size is the desired number of saveable pages to leave in | ||
1377 | * memory, so try to preallocate (all memory - size) pages. | ||
1378 | */ | ||
1379 | alloc = (count - pages) - size; | ||
1380 | pages += preallocate_image_highmem(alloc); | ||
1381 | } else { | ||
1382 | /* | ||
1383 | * There are approximately max_size saveable pages at this point | ||
1384 | * and we want to reduce this number down to size. | ||
1385 | */ | ||
1386 | alloc = max_size - size; | ||
1387 | size = preallocate_highmem_fraction(alloc, highmem, count); | ||
1388 | pages_highmem += size; | ||
1389 | alloc -= size; | ||
1390 | size = preallocate_image_memory(alloc, avail_normal); | ||
1391 | pages_highmem += preallocate_image_highmem(alloc - size); | ||
1392 | pages += pages_highmem + size; | ||
1393 | } | ||
1349 | 1394 | ||
1350 | /* | 1395 | /* |
1351 | * We only need as many page frames for the image as there are saveable | 1396 | * We only need as many page frames for the image as there are saveable |