diff options
author | Johannes Weiner <hannes@saeurebad.de> | 2008-07-24 00:28:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:20 -0400 |
commit | e2bf3cae515090fefe28329e71230dfe7ab873b1 (patch) | |
tree | 459543aefbeef71a3479ab15ccfe2423bbce6602 | |
parent | d747fa4bcebcf3696607b86a6b0dafa644be0676 (diff) |
bootmem: factor out the marking of a PFN range
Introduce new helpers that mark a range that resides completely on a node
or node-agnostic ranges that might also span node boundaries.
The free/reserve API functions will then directly use these helpers.
Note that the free/reserve semantics become more strict: while the prior
code took basically arbitrary range arguments and marked the PFNs that
happen to fall into that range, the new code requires node-specific ranges
to be completely on the node. The node-agnostic requests might span node
boundaries as long as the nodes are contiguous.
Passing ranges that do not satisfy these criteria is a bug.
[akpm@linux-foundation.org: fix printk warnings]
Signed-off-by: Johannes Weiner <hannes@saeurebad.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Yinghai Lu <yhlu.kernel@gmail.com>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | mm/bootmem.c | 188 |
1 files changed, 69 insertions, 119 deletions
diff --git a/mm/bootmem.c b/mm/bootmem.c index 9d03ff651359..e5415a5414a5 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c | |||
@@ -234,6 +234,9 @@ static void __init __free(bootmem_data_t *bdata, | |||
234 | sidx + PFN_DOWN(bdata->node_boot_start), | 234 | sidx + PFN_DOWN(bdata->node_boot_start), |
235 | eidx + PFN_DOWN(bdata->node_boot_start)); | 235 | eidx + PFN_DOWN(bdata->node_boot_start)); |
236 | 236 | ||
237 | if (bdata->hint_idx > sidx) | ||
238 | bdata->hint_idx = sidx; | ||
239 | |||
237 | for (idx = sidx; idx < eidx; idx++) | 240 | for (idx = sidx; idx < eidx; idx++) |
238 | if (!test_and_clear_bit(idx, bdata->node_bootmem_map)) | 241 | if (!test_and_clear_bit(idx, bdata->node_bootmem_map)) |
239 | BUG(); | 242 | BUG(); |
@@ -263,40 +266,57 @@ static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx, | |||
263 | return 0; | 266 | return 0; |
264 | } | 267 | } |
265 | 268 | ||
266 | static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, | 269 | static int __init mark_bootmem_node(bootmem_data_t *bdata, |
267 | unsigned long size) | 270 | unsigned long start, unsigned long end, |
271 | int reserve, int flags) | ||
268 | { | 272 | { |
269 | unsigned long sidx, eidx; | 273 | unsigned long sidx, eidx; |
270 | unsigned long i; | ||
271 | 274 | ||
272 | BUG_ON(!size); | 275 | bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n", |
276 | bdata - bootmem_node_data, start, end, reserve, flags); | ||
273 | 277 | ||
274 | /* out range */ | 278 | BUG_ON(start < PFN_DOWN(bdata->node_boot_start)); |
275 | if (addr + size < bdata->node_boot_start || | 279 | BUG_ON(end > bdata->node_low_pfn); |
276 | PFN_DOWN(addr) > bdata->node_low_pfn) | ||
277 | return; | ||
278 | /* | ||
279 | * round down end of usable mem, partially free pages are | ||
280 | * considered reserved. | ||
281 | */ | ||
282 | 280 | ||
283 | if (addr >= bdata->node_boot_start && | 281 | sidx = start - PFN_DOWN(bdata->node_boot_start); |
284 | PFN_DOWN(addr - bdata->node_boot_start) < bdata->hint_idx) | 282 | eidx = end - PFN_DOWN(bdata->node_boot_start); |
285 | bdata->hint_idx = PFN_DOWN(addr - bdata->node_boot_start); | ||
286 | 283 | ||
287 | /* | 284 | if (reserve) |
288 | * Round up to index to the range. | 285 | return __reserve(bdata, sidx, eidx, flags); |
289 | */ | ||
290 | if (PFN_UP(addr) > PFN_DOWN(bdata->node_boot_start)) | ||
291 | sidx = PFN_UP(addr) - PFN_DOWN(bdata->node_boot_start); | ||
292 | else | 286 | else |
293 | sidx = 0; | 287 | __free(bdata, sidx, eidx); |
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int __init mark_bootmem(unsigned long start, unsigned long end, | ||
292 | int reserve, int flags) | ||
293 | { | ||
294 | unsigned long pos; | ||
295 | bootmem_data_t *bdata; | ||
296 | |||
297 | pos = start; | ||
298 | list_for_each_entry(bdata, &bdata_list, list) { | ||
299 | int err; | ||
300 | unsigned long max; | ||
301 | |||
302 | if (pos < PFN_DOWN(bdata->node_boot_start)) { | ||
303 | BUG_ON(pos != start); | ||
304 | continue; | ||
305 | } | ||
306 | |||
307 | max = min(bdata->node_low_pfn, end); | ||
294 | 308 | ||
295 | eidx = PFN_DOWN(addr + size - bdata->node_boot_start); | 309 | err = mark_bootmem_node(bdata, pos, max, reserve, flags); |
296 | if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) | 310 | if (reserve && err) { |
297 | eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); | 311 | mark_bootmem(start, pos, 0, 0); |
312 | return err; | ||
313 | } | ||
298 | 314 | ||
299 | __free(bdata, sidx, eidx); | 315 | if (max == end) |
316 | return 0; | ||
317 | pos = bdata->node_low_pfn; | ||
318 | } | ||
319 | BUG(); | ||
300 | } | 320 | } |
301 | 321 | ||
302 | /** | 322 | /** |
@@ -307,12 +327,17 @@ static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, | |||
307 | * | 327 | * |
308 | * Partial pages will be considered reserved and left as they are. | 328 | * Partial pages will be considered reserved and left as they are. |
309 | * | 329 | * |
310 | * Only physical pages that actually reside on @pgdat are marked. | 330 | * The range must reside completely on the specified node. |
311 | */ | 331 | */ |
312 | void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | 332 | void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, |
313 | unsigned long size) | 333 | unsigned long size) |
314 | { | 334 | { |
315 | free_bootmem_core(pgdat->bdata, physaddr, size); | 335 | unsigned long start, end; |
336 | |||
337 | start = PFN_UP(physaddr); | ||
338 | end = PFN_DOWN(physaddr + size); | ||
339 | |||
340 | mark_bootmem_node(pgdat->bdata, start, end, 0, 0); | ||
316 | } | 341 | } |
317 | 342 | ||
318 | /** | 343 | /** |
@@ -322,83 +347,16 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | |||
322 | * | 347 | * |
323 | * Partial pages will be considered reserved and left as they are. | 348 | * Partial pages will be considered reserved and left as they are. |
324 | * | 349 | * |
325 | * All physical pages within the range are marked, no matter what | 350 | * The range must be contiguous but may span node boundaries. |
326 | * node they reside on. | ||
327 | */ | 351 | */ |
328 | void __init free_bootmem(unsigned long addr, unsigned long size) | 352 | void __init free_bootmem(unsigned long addr, unsigned long size) |
329 | { | 353 | { |
330 | bootmem_data_t *bdata; | 354 | unsigned long start, end; |
331 | list_for_each_entry(bdata, &bdata_list, list) | ||
332 | free_bootmem_core(bdata, addr, size); | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Marks a particular physical memory range as unallocatable. Usable RAM | ||
337 | * might be used for boot-time allocations - or it might get added | ||
338 | * to the free page pool later on. | ||
339 | */ | ||
340 | static int __init can_reserve_bootmem_core(bootmem_data_t *bdata, | ||
341 | unsigned long addr, unsigned long size, int flags) | ||
342 | { | ||
343 | unsigned long sidx, eidx; | ||
344 | unsigned long i; | ||
345 | |||
346 | BUG_ON(!size); | ||
347 | |||
348 | /* out of range, don't hold other */ | ||
349 | if (addr + size < bdata->node_boot_start || | ||
350 | PFN_DOWN(addr) > bdata->node_low_pfn) | ||
351 | return 0; | ||
352 | |||
353 | /* | ||
354 | * Round up to index to the range. | ||
355 | */ | ||
356 | if (addr > bdata->node_boot_start) | ||
357 | sidx= PFN_DOWN(addr - bdata->node_boot_start); | ||
358 | else | ||
359 | sidx = 0; | ||
360 | |||
361 | eidx = PFN_UP(addr + size - bdata->node_boot_start); | ||
362 | if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) | ||
363 | eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); | ||
364 | |||
365 | for (i = sidx; i < eidx; i++) { | ||
366 | if (test_bit(i, bdata->node_bootmem_map)) { | ||
367 | if (flags & BOOTMEM_EXCLUSIVE) | ||
368 | return -EBUSY; | ||
369 | } | ||
370 | } | ||
371 | |||
372 | return 0; | ||
373 | |||
374 | } | ||
375 | |||
376 | static void __init reserve_bootmem_core(bootmem_data_t *bdata, | ||
377 | unsigned long addr, unsigned long size, int flags) | ||
378 | { | ||
379 | unsigned long sidx, eidx; | ||
380 | unsigned long i; | ||
381 | |||
382 | BUG_ON(!size); | ||
383 | |||
384 | /* out of range */ | ||
385 | if (addr + size < bdata->node_boot_start || | ||
386 | PFN_DOWN(addr) > bdata->node_low_pfn) | ||
387 | return; | ||
388 | |||
389 | /* | ||
390 | * Round up to index to the range. | ||
391 | */ | ||
392 | if (addr > bdata->node_boot_start) | ||
393 | sidx= PFN_DOWN(addr - bdata->node_boot_start); | ||
394 | else | ||
395 | sidx = 0; | ||
396 | 355 | ||
397 | eidx = PFN_UP(addr + size - bdata->node_boot_start); | 356 | start = PFN_UP(addr); |
398 | if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) | 357 | end = PFN_DOWN(addr + size); |
399 | eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); | ||
400 | 358 | ||
401 | return __reserve(bdata, sidx, eidx, flags); | 359 | mark_bootmem(start, end, 0, 0); |
402 | } | 360 | } |
403 | 361 | ||
404 | /** | 362 | /** |
@@ -410,18 +368,17 @@ static void __init reserve_bootmem_core(bootmem_data_t *bdata, | |||
410 | * | 368 | * |
411 | * Partial pages will be reserved. | 369 | * Partial pages will be reserved. |
412 | * | 370 | * |
413 | * Only physical pages that actually reside on @pgdat are marked. | 371 | * The range must reside completely on the specified node. |
414 | */ | 372 | */ |
415 | int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | 373 | int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, |
416 | unsigned long size, int flags) | 374 | unsigned long size, int flags) |
417 | { | 375 | { |
418 | int ret; | 376 | unsigned long start, end; |
419 | 377 | ||
420 | ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); | 378 | start = PFN_DOWN(physaddr); |
421 | if (ret < 0) | 379 | end = PFN_UP(physaddr + size); |
422 | return -ENOMEM; | 380 | |
423 | reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); | 381 | return mark_bootmem_node(pgdat->bdata, start, end, 1, flags); |
424 | return 0; | ||
425 | } | 382 | } |
426 | 383 | ||
427 | #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE | 384 | #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE |
@@ -433,24 +390,17 @@ int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | |||
433 | * | 390 | * |
434 | * Partial pages will be reserved. | 391 | * Partial pages will be reserved. |
435 | * | 392 | * |
436 | * All physical pages within the range are marked, no matter what | 393 | * The range must be contiguous but may span node boundaries. |
437 | * node they reside on. | ||
438 | */ | 394 | */ |
439 | int __init reserve_bootmem(unsigned long addr, unsigned long size, | 395 | int __init reserve_bootmem(unsigned long addr, unsigned long size, |
440 | int flags) | 396 | int flags) |
441 | { | 397 | { |
442 | bootmem_data_t *bdata; | 398 | unsigned long start, end; |
443 | int ret; | ||
444 | 399 | ||
445 | list_for_each_entry(bdata, &bdata_list, list) { | 400 | start = PFN_DOWN(addr); |
446 | ret = can_reserve_bootmem_core(bdata, addr, size, flags); | 401 | end = PFN_UP(addr + size); |
447 | if (ret < 0) | ||
448 | return ret; | ||
449 | } | ||
450 | list_for_each_entry(bdata, &bdata_list, list) | ||
451 | reserve_bootmem_core(bdata, addr, size, flags); | ||
452 | 402 | ||
453 | return 0; | 403 | return mark_bootmem(start, end, 1, flags); |
454 | } | 404 | } |
455 | #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ | 405 | #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ |
456 | 406 | ||
@@ -663,7 +613,7 @@ void * __init alloc_bootmem_section(unsigned long size, | |||
663 | if (start_nr != section_nr || end_nr != section_nr) { | 613 | if (start_nr != section_nr || end_nr != section_nr) { |
664 | printk(KERN_WARNING "alloc_bootmem failed on section %ld.\n", | 614 | printk(KERN_WARNING "alloc_bootmem failed on section %ld.\n", |
665 | section_nr); | 615 | section_nr); |
666 | free_bootmem_core(pgdat->bdata, __pa(ptr), size); | 616 | free_bootmem_node(pgdat, __pa(ptr), size); |
667 | ptr = NULL; | 617 | ptr = NULL; |
668 | } | 618 | } |
669 | 619 | ||