aboutsummaryrefslogtreecommitdiffstats
path: root/mm/bootmem.c
diff options
context:
space:
mode:
authorJohannes Weiner <hannes@saeurebad.de>2008-07-24 00:28:06 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-07-24 13:47:20 -0400
commite2bf3cae515090fefe28329e71230dfe7ab873b1 (patch)
tree459543aefbeef71a3479ab15ccfe2423bbce6602 /mm/bootmem.c
parentd747fa4bcebcf3696607b86a6b0dafa644be0676 (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>
Diffstat (limited to 'mm/bootmem.c')
-rw-r--r--mm/bootmem.c188
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
266static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, 269static 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
291static 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 */
312void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, 332void __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 */
328void __init free_bootmem(unsigned long addr, unsigned long size) 352void __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 */
340static 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
376static 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 */
415int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, 373int __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 */
439int __init reserve_bootmem(unsigned long addr, unsigned long size, 395int __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