diff options
| -rw-r--r-- | mm/bootmem.c | 92 |
1 files changed, 69 insertions, 23 deletions
diff --git a/mm/bootmem.c b/mm/bootmem.c index 0f30bc873ecc..b6791646143e 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c | |||
| @@ -111,44 +111,74 @@ static unsigned long __init init_bootmem_core(pg_data_t *pgdat, | |||
| 111 | * might be used for boot-time allocations - or it might get added | 111 | * might be used for boot-time allocations - or it might get added |
| 112 | * to the free page pool later on. | 112 | * to the free page pool later on. |
| 113 | */ | 113 | */ |
| 114 | static int __init reserve_bootmem_core(bootmem_data_t *bdata, | 114 | static int __init can_reserve_bootmem_core(bootmem_data_t *bdata, |
| 115 | unsigned long addr, unsigned long size, int flags) | 115 | unsigned long addr, unsigned long size, int flags) |
| 116 | { | 116 | { |
| 117 | unsigned long sidx, eidx; | 117 | unsigned long sidx, eidx; |
| 118 | unsigned long i; | 118 | unsigned long i; |
| 119 | int ret; | 119 | |
| 120 | BUG_ON(!size); | ||
| 121 | |||
| 122 | /* out of range, don't hold other */ | ||
| 123 | if (addr + size < bdata->node_boot_start || | ||
| 124 | PFN_DOWN(addr) > bdata->node_low_pfn) | ||
| 125 | return 0; | ||
| 120 | 126 | ||
| 121 | /* | 127 | /* |
| 122 | * round up, partially reserved pages are considered | 128 | * Round up to index to the range. |
| 123 | * fully reserved. | ||
| 124 | */ | 129 | */ |
| 130 | if (addr > bdata->node_boot_start) | ||
| 131 | sidx= PFN_DOWN(addr - bdata->node_boot_start); | ||
| 132 | else | ||
| 133 | sidx = 0; | ||
| 134 | |||
| 135 | eidx = PFN_UP(addr + size - bdata->node_boot_start); | ||
| 136 | if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) | ||
| 137 | eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); | ||
| 138 | |||
| 139 | for (i = sidx; i < eidx; i++) { | ||
| 140 | if (test_bit(i, bdata->node_bootmem_map)) { | ||
| 141 | if (flags & BOOTMEM_EXCLUSIVE) | ||
| 142 | return -EBUSY; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | return 0; | ||
| 147 | |||
| 148 | } | ||
| 149 | |||
| 150 | static void __init reserve_bootmem_core(bootmem_data_t *bdata, | ||
| 151 | unsigned long addr, unsigned long size, int flags) | ||
| 152 | { | ||
| 153 | unsigned long sidx, eidx; | ||
| 154 | unsigned long i; | ||
| 155 | |||
| 125 | BUG_ON(!size); | 156 | BUG_ON(!size); |
| 126 | BUG_ON(PFN_DOWN(addr) >= bdata->node_low_pfn); | ||
| 127 | BUG_ON(PFN_UP(addr + size) > bdata->node_low_pfn); | ||
| 128 | BUG_ON(addr < bdata->node_boot_start); | ||
| 129 | 157 | ||
| 130 | sidx = PFN_DOWN(addr - bdata->node_boot_start); | 158 | /* out of range */ |
| 159 | if (addr + size < bdata->node_boot_start || | ||
| 160 | PFN_DOWN(addr) > bdata->node_low_pfn) | ||
| 161 | return; | ||
| 162 | |||
| 163 | /* | ||
| 164 | * Round up to index to the range. | ||
| 165 | */ | ||
| 166 | if (addr > bdata->node_boot_start) | ||
| 167 | sidx= PFN_DOWN(addr - bdata->node_boot_start); | ||
| 168 | else | ||
| 169 | sidx = 0; | ||
| 170 | |||
| 131 | eidx = PFN_UP(addr + size - bdata->node_boot_start); | 171 | eidx = PFN_UP(addr + size - bdata->node_boot_start); |
| 172 | if (eidx > bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start)) | ||
| 173 | eidx = bdata->node_low_pfn - PFN_DOWN(bdata->node_boot_start); | ||
| 132 | 174 | ||
| 133 | for (i = sidx; i < eidx; i++) | 175 | for (i = sidx; i < eidx; i++) { |
| 134 | if (test_and_set_bit(i, bdata->node_bootmem_map)) { | 176 | if (test_and_set_bit(i, bdata->node_bootmem_map)) { |
| 135 | #ifdef CONFIG_DEBUG_BOOTMEM | 177 | #ifdef CONFIG_DEBUG_BOOTMEM |
| 136 | printk("hm, page %08lx reserved twice.\n", i*PAGE_SIZE); | 178 | printk("hm, page %08lx reserved twice.\n", i*PAGE_SIZE); |
| 137 | #endif | 179 | #endif |
| 138 | if (flags & BOOTMEM_EXCLUSIVE) { | ||
| 139 | ret = -EBUSY; | ||
| 140 | goto err; | ||
| 141 | } | ||
| 142 | } | 180 | } |
| 143 | 181 | } | |
| 144 | return 0; | ||
| 145 | |||
| 146 | err: | ||
| 147 | /* unreserve memory we accidentally reserved */ | ||
| 148 | for (i--; i >= sidx; i--) | ||
| 149 | clear_bit(i, bdata->node_bootmem_map); | ||
| 150 | |||
| 151 | return ret; | ||
| 152 | } | 182 | } |
| 153 | 183 | ||
| 154 | static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, | 184 | static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, |
| @@ -415,6 +445,11 @@ unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, | |||
| 415 | void __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | 445 | void __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, |
| 416 | unsigned long size, int flags) | 446 | unsigned long size, int flags) |
| 417 | { | 447 | { |
| 448 | int ret; | ||
| 449 | |||
| 450 | ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); | ||
| 451 | if (ret < 0) | ||
| 452 | return; | ||
| 418 | reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); | 453 | reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); |
| 419 | } | 454 | } |
| 420 | 455 | ||
| @@ -440,7 +475,18 @@ unsigned long __init init_bootmem(unsigned long start, unsigned long pages) | |||
| 440 | int __init reserve_bootmem(unsigned long addr, unsigned long size, | 475 | int __init reserve_bootmem(unsigned long addr, unsigned long size, |
| 441 | int flags) | 476 | int flags) |
| 442 | { | 477 | { |
| 443 | return reserve_bootmem_core(NODE_DATA(0)->bdata, addr, size, flags); | 478 | bootmem_data_t *bdata; |
| 479 | int ret; | ||
| 480 | |||
| 481 | list_for_each_entry(bdata, &bdata_list, list) { | ||
| 482 | ret = can_reserve_bootmem_core(bdata, addr, size, flags); | ||
| 483 | if (ret < 0) | ||
| 484 | return ret; | ||
| 485 | } | ||
| 486 | list_for_each_entry(bdata, &bdata_list, list) | ||
| 487 | reserve_bootmem_core(bdata, addr, size, flags); | ||
| 488 | |||
| 489 | return 0; | ||
| 444 | } | 490 | } |
| 445 | #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ | 491 | #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ |
| 446 | 492 | ||
