| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Johannes Weiner | 57cfc29 | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 2 |  *  bootmem - A boot-time physical memory allocator and configurator | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 |  * | 
 | 4 |  *  Copyright (C) 1999 Ingo Molnar | 
| Johannes Weiner | 57cfc29 | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 5 |  *                1999 Kanoj Sarcar, SGI | 
 | 6 |  *                2008 Johannes Weiner | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7 |  * | 
| Johannes Weiner | 57cfc29 | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 8 |  * Access to this subsystem has to be serialized externally (which is true | 
 | 9 |  * for the boot process anyway). | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 10 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 | #include <linux/init.h> | 
| Franck Bui-Huu | bbc7b92 | 2006-09-25 23:31:07 -0700 | [diff] [blame] | 12 | #include <linux/pfn.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | #include <linux/bootmem.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | #include <linux/module.h> | 
| Franck Bui-Huu | e786e86 | 2006-09-25 23:31:06 -0700 | [diff] [blame] | 15 |  | 
 | 16 | #include <asm/bug.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | #include <asm/io.h> | 
| Heiko Carstens | dfd54cb | 2006-09-25 23:31:33 -0700 | [diff] [blame] | 18 | #include <asm/processor.h> | 
| Franck Bui-Huu | e786e86 | 2006-09-25 23:31:06 -0700 | [diff] [blame] | 19 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include "internal.h" | 
 | 21 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 | unsigned long max_low_pfn; | 
 | 23 | unsigned long min_low_pfn; | 
 | 24 | unsigned long max_pfn; | 
 | 25 |  | 
| Vivek Goyal | 92aa63a | 2005-06-25 14:58:18 -0700 | [diff] [blame] | 26 | #ifdef CONFIG_CRASH_DUMP | 
 | 27 | /* | 
 | 28 |  * If we have booted due to a crash, max_pfn will be a very low value. We need | 
 | 29 |  * to know the amount of memory that the previous kernel used. | 
 | 30 |  */ | 
 | 31 | unsigned long saved_max_pfn; | 
 | 32 | #endif | 
 | 33 |  | 
| Johannes Weiner | b61bfa3 | 2008-07-23 21:26:55 -0700 | [diff] [blame] | 34 | bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata; | 
 | 35 |  | 
| Johannes Weiner | 636cc40 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 36 | static struct list_head bdata_list __initdata = LIST_HEAD_INIT(bdata_list); | 
 | 37 |  | 
| Johannes Weiner | 2e5237d | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 38 | static int bootmem_debug; | 
 | 39 |  | 
 | 40 | static int __init bootmem_debug_setup(char *buf) | 
 | 41 | { | 
 | 42 | 	bootmem_debug = 1; | 
 | 43 | 	return 0; | 
 | 44 | } | 
 | 45 | early_param("bootmem_debug", bootmem_debug_setup); | 
 | 46 |  | 
 | 47 | #define bdebug(fmt, args...) ({				\ | 
 | 48 | 	if (unlikely(bootmem_debug))			\ | 
 | 49 | 		printk(KERN_INFO			\ | 
 | 50 | 			"bootmem::%s " fmt,		\ | 
| Harvey Harrison | 80a914d | 2008-10-15 22:01:25 -0700 | [diff] [blame] | 51 | 			__func__, ## args);		\ | 
| Johannes Weiner | 2e5237d | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 52 | }) | 
 | 53 |  | 
| Johannes Weiner | df049a5 | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 54 | static unsigned long __init bootmap_bytes(unsigned long pages) | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 55 | { | 
| Johannes Weiner | df049a5 | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 56 | 	unsigned long bytes = (pages + 7) / 8; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 57 |  | 
| Johannes Weiner | df049a5 | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 58 | 	return ALIGN(bytes, sizeof(long)); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 59 | } | 
 | 60 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 61 | /** | 
 | 62 |  * bootmem_bootmap_pages - calculate bitmap size in pages | 
 | 63 |  * @pages: number of pages the bitmap has to represent | 
 | 64 |  */ | 
| Franck Bui-Huu | f71bf0c | 2006-09-25 23:31:08 -0700 | [diff] [blame] | 65 | unsigned long __init bootmem_bootmap_pages(unsigned long pages) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | { | 
| Johannes Weiner | df049a5 | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 67 | 	unsigned long bytes = bootmap_bytes(pages); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 |  | 
| Johannes Weiner | df049a5 | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 69 | 	return PAGE_ALIGN(bytes) >> PAGE_SHIFT; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | } | 
| Franck Bui-Huu | f71bf0c | 2006-09-25 23:31:08 -0700 | [diff] [blame] | 71 |  | 
| KAMEZAWA Hiroyuki | 679bc9f | 2006-03-27 01:15:58 -0800 | [diff] [blame] | 72 | /* | 
 | 73 |  * link bdata in order | 
 | 74 |  */ | 
| Franck Bui-Huu | 69d49e6 | 2006-09-25 23:31:04 -0700 | [diff] [blame] | 75 | static void __init link_bootmem(bootmem_data_t *bdata) | 
| KAMEZAWA Hiroyuki | 679bc9f | 2006-03-27 01:15:58 -0800 | [diff] [blame] | 76 | { | 
| Johannes Weiner | 636cc40 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 77 | 	struct list_head *iter; | 
| Franck Bui-Huu | f71bf0c | 2006-09-25 23:31:08 -0700 | [diff] [blame] | 78 |  | 
| Johannes Weiner | 636cc40 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 79 | 	list_for_each(iter, &bdata_list) { | 
 | 80 | 		bootmem_data_t *ent; | 
 | 81 |  | 
 | 82 | 		ent = list_entry(iter, bootmem_data_t, list); | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 83 | 		if (bdata->node_min_pfn < ent->node_min_pfn) | 
| Johannes Weiner | 636cc40 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 84 | 			break; | 
| KAMEZAWA Hiroyuki | 679bc9f | 2006-03-27 01:15:58 -0800 | [diff] [blame] | 85 | 	} | 
| Johannes Weiner | 636cc40 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 86 | 	list_add_tail(&bdata->list, iter); | 
| KAMEZAWA Hiroyuki | 679bc9f | 2006-03-27 01:15:58 -0800 | [diff] [blame] | 87 | } | 
 | 88 |  | 
| Franck Bui-Huu | bbc7b92 | 2006-09-25 23:31:07 -0700 | [diff] [blame] | 89 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 |  * Called once to set up the allocator itself. | 
 | 91 |  */ | 
| Johannes Weiner | 8ae0446 | 2008-07-23 21:26:59 -0700 | [diff] [blame] | 92 | static unsigned long __init init_bootmem_core(bootmem_data_t *bdata, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | 	unsigned long mapstart, unsigned long start, unsigned long end) | 
 | 94 | { | 
| Franck Bui-Huu | bbc7b92 | 2006-09-25 23:31:07 -0700 | [diff] [blame] | 95 | 	unsigned long mapsize; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 |  | 
| Mel Gorman | 2dbb51c | 2008-07-23 21:26:52 -0700 | [diff] [blame] | 97 | 	mminit_validate_memmodel_limits(&start, &end); | 
| Franck Bui-Huu | bbc7b92 | 2006-09-25 23:31:07 -0700 | [diff] [blame] | 98 | 	bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart)); | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 99 | 	bdata->node_min_pfn = start; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 100 | 	bdata->node_low_pfn = end; | 
| KAMEZAWA Hiroyuki | 679bc9f | 2006-03-27 01:15:58 -0800 | [diff] [blame] | 101 | 	link_bootmem(bdata); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 102 |  | 
 | 103 | 	/* | 
 | 104 | 	 * Initially all pages are reserved - setup_arch() has to | 
 | 105 | 	 * register free RAM areas explicitly. | 
 | 106 | 	 */ | 
| Johannes Weiner | df049a5 | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 107 | 	mapsize = bootmap_bytes(end - start); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | 	memset(bdata->node_bootmem_map, 0xff, mapsize); | 
 | 109 |  | 
| Johannes Weiner | 2e5237d | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 110 | 	bdebug("nid=%td start=%lx map=%lx end=%lx mapsize=%lx\n", | 
 | 111 | 		bdata - bootmem_node_data, start, mapstart, end, mapsize); | 
 | 112 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 113 | 	return mapsize; | 
 | 114 | } | 
 | 115 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 116 | /** | 
 | 117 |  * init_bootmem_node - register a node as boot memory | 
 | 118 |  * @pgdat: node to register | 
 | 119 |  * @freepfn: pfn where the bitmap for this node is to be placed | 
 | 120 |  * @startpfn: first pfn on the node | 
 | 121 |  * @endpfn: first pfn after the node | 
 | 122 |  * | 
 | 123 |  * Returns the number of bytes needed to hold the bitmap for this node. | 
 | 124 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 125 | unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, | 
 | 126 | 				unsigned long startpfn, unsigned long endpfn) | 
 | 127 | { | 
 | 128 | 	return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn); | 
 | 129 | } | 
 | 130 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 131 | /** | 
 | 132 |  * init_bootmem - register boot memory | 
 | 133 |  * @start: pfn where the bitmap is to be placed | 
 | 134 |  * @pages: number of available physical pages | 
 | 135 |  * | 
 | 136 |  * Returns the number of bytes needed to hold the bitmap. | 
 | 137 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 138 | unsigned long __init init_bootmem(unsigned long start, unsigned long pages) | 
 | 139 | { | 
 | 140 | 	max_low_pfn = pages; | 
 | 141 | 	min_low_pfn = start; | 
 | 142 | 	return init_bootmem_core(NODE_DATA(0)->bdata, start, 0, pages); | 
 | 143 | } | 
 | 144 |  | 
 | 145 | static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) | 
 | 146 | { | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 147 | 	int aligned; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 148 | 	struct page *page; | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 149 | 	unsigned long start, end, pages, count = 0; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 150 |  | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 151 | 	if (!bdata->node_bootmem_map) | 
 | 152 | 		return 0; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 153 |  | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 154 | 	start = bdata->node_min_pfn; | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 155 | 	end = bdata->node_low_pfn; | 
 | 156 |  | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 157 | 	/* | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 158 | 	 * If the start is aligned to the machines wordsize, we might | 
 | 159 | 	 * be able to free pages in bulks of that order. | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 160 | 	 */ | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 161 | 	aligned = !(start & (BITS_PER_LONG - 1)); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 162 |  | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 163 | 	bdebug("nid=%td start=%lx end=%lx aligned=%d\n", | 
 | 164 | 		bdata - bootmem_node_data, start, end, aligned); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 165 |  | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 166 | 	while (start < end) { | 
 | 167 | 		unsigned long *map, idx, vec; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 168 |  | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 169 | 		map = bdata->node_bootmem_map; | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 170 | 		idx = start - bdata->node_min_pfn; | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 171 | 		vec = ~map[idx / BITS_PER_LONG]; | 
 | 172 |  | 
 | 173 | 		if (aligned && vec == ~0UL && start + BITS_PER_LONG < end) { | 
 | 174 | 			int order = ilog2(BITS_PER_LONG); | 
 | 175 |  | 
 | 176 | 			__free_pages_bootmem(pfn_to_page(start), order); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 177 | 			count += BITS_PER_LONG; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 178 | 		} else { | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 179 | 			unsigned long off = 0; | 
 | 180 |  | 
 | 181 | 			while (vec && off < BITS_PER_LONG) { | 
 | 182 | 				if (vec & 1) { | 
 | 183 | 					page = pfn_to_page(start + off); | 
 | 184 | 					__free_pages_bootmem(page, 0); | 
 | 185 | 					count++; | 
 | 186 | 				} | 
 | 187 | 				vec >>= 1; | 
 | 188 | 				off++; | 
 | 189 | 			} | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 190 | 		} | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 191 | 		start += BITS_PER_LONG; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 192 | 	} | 
 | 193 |  | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 194 | 	page = virt_to_page(bdata->node_bootmem_map); | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 195 | 	pages = bdata->node_low_pfn - bdata->node_min_pfn; | 
| Johannes Weiner | 41546c1 | 2008-07-23 21:28:03 -0700 | [diff] [blame] | 196 | 	pages = bootmem_bootmap_pages(pages); | 
 | 197 | 	count += pages; | 
 | 198 | 	while (pages--) | 
 | 199 | 		__free_pages_bootmem(page++, 0); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 200 |  | 
| Johannes Weiner | 2e5237d | 2008-07-23 21:28:02 -0700 | [diff] [blame] | 201 | 	bdebug("nid=%td released=%lx\n", bdata - bootmem_node_data, count); | 
 | 202 |  | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 203 | 	return count; | 
 | 204 | } | 
 | 205 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 206 | /** | 
 | 207 |  * free_all_bootmem_node - release a node's free pages to the buddy allocator | 
 | 208 |  * @pgdat: node to be released | 
 | 209 |  * | 
 | 210 |  * Returns the number of pages actually released. | 
 | 211 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 212 | unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) | 
 | 213 | { | 
 | 214 | 	register_page_bootmem_info_node(pgdat); | 
 | 215 | 	return free_all_bootmem_core(pgdat->bdata); | 
 | 216 | } | 
 | 217 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 218 | /** | 
 | 219 |  * free_all_bootmem - release free pages to the buddy allocator | 
 | 220 |  * | 
 | 221 |  * Returns the number of pages actually released. | 
 | 222 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 223 | unsigned long __init free_all_bootmem(void) | 
 | 224 | { | 
 | 225 | 	return free_all_bootmem_core(NODE_DATA(0)->bdata); | 
 | 226 | } | 
 | 227 |  | 
| Johannes Weiner | d747fa4 | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 228 | static void __init __free(bootmem_data_t *bdata, | 
 | 229 | 			unsigned long sidx, unsigned long eidx) | 
 | 230 | { | 
 | 231 | 	unsigned long idx; | 
 | 232 |  | 
 | 233 | 	bdebug("nid=%td start=%lx end=%lx\n", bdata - bootmem_node_data, | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 234 | 		sidx + bdata->node_min_pfn, | 
 | 235 | 		eidx + bdata->node_min_pfn); | 
| Johannes Weiner | d747fa4 | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 236 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 237 | 	if (bdata->hint_idx > sidx) | 
 | 238 | 		bdata->hint_idx = sidx; | 
 | 239 |  | 
| Johannes Weiner | d747fa4 | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 240 | 	for (idx = sidx; idx < eidx; idx++) | 
 | 241 | 		if (!test_and_clear_bit(idx, bdata->node_bootmem_map)) | 
 | 242 | 			BUG(); | 
 | 243 | } | 
 | 244 |  | 
 | 245 | static int __init __reserve(bootmem_data_t *bdata, unsigned long sidx, | 
 | 246 | 			unsigned long eidx, int flags) | 
 | 247 | { | 
 | 248 | 	unsigned long idx; | 
 | 249 | 	int exclusive = flags & BOOTMEM_EXCLUSIVE; | 
 | 250 |  | 
 | 251 | 	bdebug("nid=%td start=%lx end=%lx flags=%x\n", | 
 | 252 | 		bdata - bootmem_node_data, | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 253 | 		sidx + bdata->node_min_pfn, | 
 | 254 | 		eidx + bdata->node_min_pfn, | 
| Johannes Weiner | d747fa4 | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 255 | 		flags); | 
 | 256 |  | 
 | 257 | 	for (idx = sidx; idx < eidx; idx++) | 
 | 258 | 		if (test_and_set_bit(idx, bdata->node_bootmem_map)) { | 
 | 259 | 			if (exclusive) { | 
 | 260 | 				__free(bdata, sidx, idx); | 
 | 261 | 				return -EBUSY; | 
 | 262 | 			} | 
 | 263 | 			bdebug("silent double reserve of PFN %lx\n", | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 264 | 				idx + bdata->node_min_pfn); | 
| Johannes Weiner | d747fa4 | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 265 | 		} | 
 | 266 | 	return 0; | 
 | 267 | } | 
 | 268 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 269 | static int __init mark_bootmem_node(bootmem_data_t *bdata, | 
 | 270 | 				unsigned long start, unsigned long end, | 
 | 271 | 				int reserve, int flags) | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 272 | { | 
 | 273 | 	unsigned long sidx, eidx; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 274 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 275 | 	bdebug("nid=%td start=%lx end=%lx reserve=%d flags=%x\n", | 
 | 276 | 		bdata - bootmem_node_data, start, end, reserve, flags); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 277 |  | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 278 | 	BUG_ON(start < bdata->node_min_pfn); | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 279 | 	BUG_ON(end > bdata->node_low_pfn); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 280 |  | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 281 | 	sidx = start - bdata->node_min_pfn; | 
 | 282 | 	eidx = end - bdata->node_min_pfn; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 283 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 284 | 	if (reserve) | 
 | 285 | 		return __reserve(bdata, sidx, eidx, flags); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 286 | 	else | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 287 | 		__free(bdata, sidx, eidx); | 
 | 288 | 	return 0; | 
 | 289 | } | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 290 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 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; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 296 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 297 | 	pos = start; | 
 | 298 | 	list_for_each_entry(bdata, &bdata_list, list) { | 
 | 299 | 		int err; | 
 | 300 | 		unsigned long max; | 
 | 301 |  | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 302 | 		if (pos < bdata->node_min_pfn || | 
 | 303 | 		    pos >= bdata->node_low_pfn) { | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 304 | 			BUG_ON(pos != start); | 
 | 305 | 			continue; | 
 | 306 | 		} | 
 | 307 |  | 
 | 308 | 		max = min(bdata->node_low_pfn, end); | 
 | 309 |  | 
 | 310 | 		err = mark_bootmem_node(bdata, pos, max, reserve, flags); | 
 | 311 | 		if (reserve && err) { | 
 | 312 | 			mark_bootmem(start, pos, 0, 0); | 
 | 313 | 			return err; | 
 | 314 | 		} | 
 | 315 |  | 
 | 316 | 		if (max == end) | 
 | 317 | 			return 0; | 
 | 318 | 		pos = bdata->node_low_pfn; | 
 | 319 | 	} | 
 | 320 | 	BUG(); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 321 | } | 
 | 322 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 323 | /** | 
 | 324 |  * free_bootmem_node - mark a page range as usable | 
 | 325 |  * @pgdat: node the range resides on | 
 | 326 |  * @physaddr: starting address of the range | 
 | 327 |  * @size: size of the range in bytes | 
 | 328 |  * | 
 | 329 |  * Partial pages will be considered reserved and left as they are. | 
 | 330 |  * | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 331 |  * The range must reside completely on the specified node. | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 332 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 333 | void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | 
 | 334 | 			      unsigned long size) | 
 | 335 | { | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 336 | 	unsigned long start, end; | 
 | 337 |  | 
 | 338 | 	start = PFN_UP(physaddr); | 
 | 339 | 	end = PFN_DOWN(physaddr + size); | 
 | 340 |  | 
 | 341 | 	mark_bootmem_node(pgdat->bdata, start, end, 0, 0); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 342 | } | 
 | 343 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 344 | /** | 
 | 345 |  * free_bootmem - mark a page range as usable | 
 | 346 |  * @addr: starting address of the range | 
 | 347 |  * @size: size of the range in bytes | 
 | 348 |  * | 
 | 349 |  * Partial pages will be considered reserved and left as they are. | 
 | 350 |  * | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 351 |  * The range must be contiguous but may span node boundaries. | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 352 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 353 | void __init free_bootmem(unsigned long addr, unsigned long size) | 
 | 354 | { | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 355 | 	unsigned long start, end; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 356 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 357 | 	start = PFN_UP(addr); | 
 | 358 | 	end = PFN_DOWN(addr + size); | 
| Yinghai Lu | a5645a6 | 2008-03-18 12:49:12 -0700 | [diff] [blame] | 359 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 360 | 	mark_bootmem(start, end, 0, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 361 | } | 
 | 362 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 363 | /** | 
 | 364 |  * reserve_bootmem_node - mark a page range as reserved | 
 | 365 |  * @pgdat: node the range resides on | 
 | 366 |  * @physaddr: starting address of the range | 
 | 367 |  * @size: size of the range in bytes | 
 | 368 |  * @flags: reservation flags (see linux/bootmem.h) | 
 | 369 |  * | 
 | 370 |  * Partial pages will be reserved. | 
 | 371 |  * | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 372 |  * The range must reside completely on the specified node. | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 373 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 374 | int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | 
 | 375 | 				 unsigned long size, int flags) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 376 | { | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 377 | 	unsigned long start, end; | 
| Franck Bui-Huu | bbc7b92 | 2006-09-25 23:31:07 -0700 | [diff] [blame] | 378 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 379 | 	start = PFN_DOWN(physaddr); | 
 | 380 | 	end = PFN_UP(physaddr + size); | 
 | 381 |  | 
 | 382 | 	return mark_bootmem_node(pgdat->bdata, start, end, 1, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 383 | } | 
 | 384 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 385 | /** | 
 | 386 |  * reserve_bootmem - mark a page range as usable | 
 | 387 |  * @addr: starting address of the range | 
 | 388 |  * @size: size of the range in bytes | 
 | 389 |  * @flags: reservation flags (see linux/bootmem.h) | 
 | 390 |  * | 
 | 391 |  * Partial pages will be reserved. | 
 | 392 |  * | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 393 |  * The range must be contiguous but may span node boundaries. | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 394 |  */ | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 395 | int __init reserve_bootmem(unsigned long addr, unsigned long size, | 
 | 396 | 			    int flags) | 
 | 397 | { | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 398 | 	unsigned long start, end; | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 399 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 400 | 	start = PFN_DOWN(addr); | 
 | 401 | 	end = PFN_UP(addr + size); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 402 |  | 
| Johannes Weiner | e2bf3ca | 2008-07-23 21:28:06 -0700 | [diff] [blame] | 403 | 	return mark_bootmem(start, end, 1, flags); | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 404 | } | 
| Johannes Weiner | 223e8dc | 2008-07-23 21:28:00 -0700 | [diff] [blame] | 405 |  | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 406 | static unsigned long align_idx(struct bootmem_data *bdata, unsigned long idx, | 
 | 407 | 			unsigned long step) | 
 | 408 | { | 
 | 409 | 	unsigned long base = bdata->node_min_pfn; | 
 | 410 |  | 
 | 411 | 	/* | 
 | 412 | 	 * Align the index with respect to the node start so that the | 
 | 413 | 	 * combination of both satisfies the requested alignment. | 
 | 414 | 	 */ | 
 | 415 |  | 
 | 416 | 	return ALIGN(base + idx, step) - base; | 
 | 417 | } | 
 | 418 |  | 
 | 419 | static unsigned long align_off(struct bootmem_data *bdata, unsigned long off, | 
 | 420 | 			unsigned long align) | 
 | 421 | { | 
 | 422 | 	unsigned long base = PFN_PHYS(bdata->node_min_pfn); | 
 | 423 |  | 
 | 424 | 	/* Same as align_idx for byte offsets */ | 
 | 425 |  | 
 | 426 | 	return ALIGN(base + off, align) - base; | 
 | 427 | } | 
 | 428 |  | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 429 | static void * __init alloc_bootmem_core(struct bootmem_data *bdata, | 
 | 430 | 					unsigned long size, unsigned long align, | 
 | 431 | 					unsigned long goal, unsigned long limit) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 432 | { | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 433 | 	unsigned long fallback = 0; | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 434 | 	unsigned long min, max, start, sidx, midx, step; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 435 |  | 
| Johannes Weiner | 594fe1a | 2009-01-06 14:40:32 -0800 | [diff] [blame] | 436 | 	bdebug("nid=%td size=%lx [%lu pages] align=%lx goal=%lx limit=%lx\n", | 
 | 437 | 		bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT, | 
 | 438 | 		align, goal, limit); | 
 | 439 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 440 | 	BUG_ON(!size); | 
 | 441 | 	BUG_ON(align & (align - 1)); | 
 | 442 | 	BUG_ON(limit && goal + size > limit); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 443 |  | 
| Christian Krafft | 7c309a6 | 2006-12-06 20:32:41 -0800 | [diff] [blame] | 444 | 	if (!bdata->node_bootmem_map) | 
 | 445 | 		return NULL; | 
 | 446 |  | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 447 | 	min = bdata->node_min_pfn; | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 448 | 	max = bdata->node_low_pfn; | 
| Yinghai Lu | 9a2dc04 | 2008-03-18 12:44:48 -0700 | [diff] [blame] | 449 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 450 | 	goal >>= PAGE_SHIFT; | 
 | 451 | 	limit >>= PAGE_SHIFT; | 
 | 452 |  | 
 | 453 | 	if (limit && max > limit) | 
 | 454 | 		max = limit; | 
 | 455 | 	if (max <= min) | 
| Yinghai Lu | 9a2dc04 | 2008-03-18 12:44:48 -0700 | [diff] [blame] | 456 | 		return NULL; | 
 | 457 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 458 | 	step = max(align >> PAGE_SHIFT, 1UL); | 
| Yasunori Goto | 281dd25 | 2005-10-19 15:52:18 -0700 | [diff] [blame] | 459 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 460 | 	if (goal && min < goal && goal < max) | 
 | 461 | 		start = ALIGN(goal, step); | 
 | 462 | 	else | 
 | 463 | 		start = ALIGN(min, step); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 464 |  | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 465 | 	sidx = start - bdata->node_min_pfn; | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 466 | 	midx = max - bdata->node_min_pfn; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 467 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 468 | 	if (bdata->hint_idx > sidx) { | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 469 | 		/* | 
 | 470 | 		 * Handle the valid case of sidx being zero and still | 
 | 471 | 		 * catch the fallback below. | 
 | 472 | 		 */ | 
 | 473 | 		fallback = sidx + 1; | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 474 | 		sidx = align_idx(bdata, bdata->hint_idx, step); | 
| Yinghai Lu | ad09315 | 2008-03-10 23:23:42 -0700 | [diff] [blame] | 475 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 477 | 	while (1) { | 
 | 478 | 		int merge; | 
 | 479 | 		void *region; | 
 | 480 | 		unsigned long eidx, i, start_off, end_off; | 
 | 481 | find_block: | 
 | 482 | 		sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx); | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 483 | 		sidx = align_idx(bdata, sidx, step); | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 484 | 		eidx = sidx + PFN_UP(size); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 485 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 486 | 		if (sidx >= midx || eidx > midx) | 
| Haren Myneni | 66d43e9 | 2005-12-12 00:37:39 -0800 | [diff] [blame] | 487 | 			break; | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 488 |  | 
 | 489 | 		for (i = sidx; i < eidx; i++) | 
 | 490 | 			if (test_bit(i, bdata->node_bootmem_map)) { | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 491 | 				sidx = align_idx(bdata, i, step); | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 492 | 				if (sidx == i) | 
 | 493 | 					sidx += step; | 
 | 494 | 				goto find_block; | 
 | 495 | 			} | 
 | 496 |  | 
| Mikulas Patocka | 627240a | 2008-08-15 00:40:17 -0700 | [diff] [blame] | 497 | 		if (bdata->last_end_off & (PAGE_SIZE - 1) && | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 498 | 				PFN_DOWN(bdata->last_end_off) + 1 == sidx) | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 499 | 			start_off = align_off(bdata, bdata->last_end_off, align); | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 500 | 		else | 
 | 501 | 			start_off = PFN_PHYS(sidx); | 
 | 502 |  | 
 | 503 | 		merge = PFN_DOWN(start_off) < sidx; | 
 | 504 | 		end_off = start_off + size; | 
 | 505 |  | 
 | 506 | 		bdata->last_end_off = end_off; | 
 | 507 | 		bdata->hint_idx = PFN_UP(end_off); | 
 | 508 |  | 
 | 509 | 		/* | 
 | 510 | 		 * Reserve the area now: | 
 | 511 | 		 */ | 
| Johannes Weiner | d747fa4 | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 512 | 		if (__reserve(bdata, PFN_DOWN(start_off) + merge, | 
 | 513 | 				PFN_UP(end_off), BOOTMEM_EXCLUSIVE)) | 
 | 514 | 			BUG(); | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 515 |  | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 516 | 		region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) + | 
 | 517 | 				start_off); | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 518 | 		memset(region, 0, size); | 
 | 519 | 		return region; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 520 | 	} | 
 | 521 |  | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 522 | 	if (fallback) { | 
| Johannes Weiner | 481ebd0 | 2008-08-20 14:09:15 -0700 | [diff] [blame] | 523 | 		sidx = align_idx(bdata, fallback - 1, step); | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 524 | 		fallback = 0; | 
 | 525 | 		goto find_block; | 
 | 526 | 	} | 
 | 527 |  | 
 | 528 | 	return NULL; | 
 | 529 | } | 
 | 530 |  | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 531 | static void * __init alloc_arch_preferred_bootmem(bootmem_data_t *bdata, | 
 | 532 | 					unsigned long size, unsigned long align, | 
 | 533 | 					unsigned long goal, unsigned long limit) | 
 | 534 | { | 
 | 535 | #ifdef CONFIG_HAVE_ARCH_BOOTMEM | 
 | 536 | 	bootmem_data_t *p_bdata; | 
 | 537 |  | 
 | 538 | 	p_bdata = bootmem_arch_preferred_node(bdata, size, align, goal, limit); | 
 | 539 | 	if (p_bdata) | 
 | 540 | 		return alloc_bootmem_core(p_bdata, size, align, goal, limit); | 
 | 541 | #endif | 
 | 542 | 	return NULL; | 
 | 543 | } | 
 | 544 |  | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 545 | static void * __init ___alloc_bootmem_nopanic(unsigned long size, | 
 | 546 | 					unsigned long align, | 
 | 547 | 					unsigned long goal, | 
 | 548 | 					unsigned long limit) | 
 | 549 | { | 
 | 550 | 	bootmem_data_t *bdata; | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 551 | 	void *region; | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 552 |  | 
 | 553 | restart: | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 554 | 	region = alloc_arch_preferred_bootmem(NULL, size, align, goal, limit); | 
 | 555 | 	if (region) | 
 | 556 | 		return region; | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 557 |  | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 558 | 	list_for_each_entry(bdata, &bdata_list, list) { | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 559 | 		if (goal && bdata->node_low_pfn <= PFN_DOWN(goal)) | 
 | 560 | 			continue; | 
| Johannes Weiner | 3560e24 | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 561 | 		if (limit && bdata->node_min_pfn >= PFN_DOWN(limit)) | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 562 | 			break; | 
 | 563 |  | 
 | 564 | 		region = alloc_bootmem_core(bdata, size, align, goal, limit); | 
 | 565 | 		if (region) | 
 | 566 | 			return region; | 
 | 567 | 	} | 
 | 568 |  | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 569 | 	if (goal) { | 
 | 570 | 		goal = 0; | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 571 | 		goto restart; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 572 | 	} | 
| Johannes Weiner | 5f2809e | 2008-07-23 21:28:05 -0700 | [diff] [blame] | 573 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 574 | 	return NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 575 | } | 
 | 576 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 577 | /** | 
 | 578 |  * __alloc_bootmem_nopanic - allocate boot memory without panicking | 
 | 579 |  * @size: size of the request in bytes | 
 | 580 |  * @align: alignment of the region | 
 | 581 |  * @goal: preferred starting address of the region | 
 | 582 |  * | 
 | 583 |  * The goal is dropped if it can not be satisfied and the allocation will | 
 | 584 |  * fall back to memory below @goal. | 
 | 585 |  * | 
 | 586 |  * Allocation may happen on any node in the system. | 
 | 587 |  * | 
 | 588 |  * Returns NULL on failure. | 
 | 589 |  */ | 
| Franck Bui-Huu | bb0923a | 2006-09-25 23:31:05 -0700 | [diff] [blame] | 590 | void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align, | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 591 | 					unsigned long goal) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 592 | { | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 593 | 	return ___alloc_bootmem_nopanic(size, align, goal, 0); | 
 | 594 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 |  | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 596 | static void * __init ___alloc_bootmem(unsigned long size, unsigned long align, | 
 | 597 | 					unsigned long goal, unsigned long limit) | 
 | 598 | { | 
 | 599 | 	void *mem = ___alloc_bootmem_nopanic(size, align, goal, limit); | 
 | 600 |  | 
 | 601 | 	if (mem) | 
 | 602 | 		return mem; | 
 | 603 | 	/* | 
 | 604 | 	 * Whoops, we cannot satisfy the allocation request. | 
 | 605 | 	 */ | 
 | 606 | 	printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size); | 
 | 607 | 	panic("Out of memory"); | 
| Andi Kleen | a806223 | 2006-04-07 19:49:21 +0200 | [diff] [blame] | 608 | 	return NULL; | 
 | 609 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 610 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 611 | /** | 
 | 612 |  * __alloc_bootmem - allocate boot memory | 
 | 613 |  * @size: size of the request in bytes | 
 | 614 |  * @align: alignment of the region | 
 | 615 |  * @goal: preferred starting address of the region | 
 | 616 |  * | 
 | 617 |  * The goal is dropped if it can not be satisfied and the allocation will | 
 | 618 |  * fall back to memory below @goal. | 
 | 619 |  * | 
 | 620 |  * Allocation may happen on any node in the system. | 
 | 621 |  * | 
 | 622 |  * The function panics if the request can not be satisfied. | 
 | 623 |  */ | 
| Franck Bui-Huu | bb0923a | 2006-09-25 23:31:05 -0700 | [diff] [blame] | 624 | void * __init __alloc_bootmem(unsigned long size, unsigned long align, | 
 | 625 | 			      unsigned long goal) | 
| Andi Kleen | a806223 | 2006-04-07 19:49:21 +0200 | [diff] [blame] | 626 | { | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 627 | 	return ___alloc_bootmem(size, align, goal, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 628 | } | 
 | 629 |  | 
| Johannes Weiner | 4cc278b | 2008-07-23 21:28:08 -0700 | [diff] [blame] | 630 | static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata, | 
 | 631 | 				unsigned long size, unsigned long align, | 
 | 632 | 				unsigned long goal, unsigned long limit) | 
 | 633 | { | 
 | 634 | 	void *ptr; | 
 | 635 |  | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 636 | 	ptr = alloc_arch_preferred_bootmem(bdata, size, align, goal, limit); | 
 | 637 | 	if (ptr) | 
 | 638 | 		return ptr; | 
 | 639 |  | 
| Johannes Weiner | 4cc278b | 2008-07-23 21:28:08 -0700 | [diff] [blame] | 640 | 	ptr = alloc_bootmem_core(bdata, size, align, goal, limit); | 
 | 641 | 	if (ptr) | 
 | 642 | 		return ptr; | 
 | 643 |  | 
 | 644 | 	return ___alloc_bootmem(size, align, goal, limit); | 
 | 645 | } | 
 | 646 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 647 | /** | 
 | 648 |  * __alloc_bootmem_node - allocate boot memory from a specific node | 
 | 649 |  * @pgdat: node to allocate from | 
 | 650 |  * @size: size of the request in bytes | 
 | 651 |  * @align: alignment of the region | 
 | 652 |  * @goal: preferred starting address of the region | 
 | 653 |  * | 
 | 654 |  * The goal is dropped if it can not be satisfied and the allocation will | 
 | 655 |  * fall back to memory below @goal. | 
 | 656 |  * | 
 | 657 |  * Allocation may fall back to any node in the system if the specified node | 
 | 658 |  * can not hold the requested memory. | 
 | 659 |  * | 
 | 660 |  * The function panics if the request can not be satisfied. | 
 | 661 |  */ | 
| Franck Bui-Huu | bb0923a | 2006-09-25 23:31:05 -0700 | [diff] [blame] | 662 | void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, | 
 | 663 | 				   unsigned long align, unsigned long goal) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 664 | { | 
| Johannes Weiner | 4cc278b | 2008-07-23 21:28:08 -0700 | [diff] [blame] | 665 | 	return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 666 | } | 
 | 667 |  | 
| Yasunori Goto | e70260a | 2008-04-28 02:13:32 -0700 | [diff] [blame] | 668 | #ifdef CONFIG_SPARSEMEM | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 669 | /** | 
 | 670 |  * alloc_bootmem_section - allocate boot memory from a specific section | 
 | 671 |  * @size: size of the request in bytes | 
 | 672 |  * @section_nr: sparse map section to allocate from | 
 | 673 |  * | 
 | 674 |  * Return NULL on failure. | 
 | 675 |  */ | 
| Yasunori Goto | e70260a | 2008-04-28 02:13:32 -0700 | [diff] [blame] | 676 | void * __init alloc_bootmem_section(unsigned long size, | 
 | 677 | 				    unsigned long section_nr) | 
 | 678 | { | 
| Johannes Weiner | 75a56cf | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 679 | 	bootmem_data_t *bdata; | 
 | 680 | 	unsigned long pfn, goal, limit; | 
| Yasunori Goto | e70260a | 2008-04-28 02:13:32 -0700 | [diff] [blame] | 681 |  | 
 | 682 | 	pfn = section_nr_to_pfn(section_nr); | 
| Johannes Weiner | 75a56cf | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 683 | 	goal = pfn << PAGE_SHIFT; | 
 | 684 | 	limit = section_nr_to_pfn(section_nr + 1) << PAGE_SHIFT; | 
 | 685 | 	bdata = &bootmem_node_data[early_pfn_to_nid(pfn)]; | 
| Yasunori Goto | e70260a | 2008-04-28 02:13:32 -0700 | [diff] [blame] | 686 |  | 
| Johannes Weiner | 75a56cf | 2008-07-23 21:28:09 -0700 | [diff] [blame] | 687 | 	return alloc_bootmem_core(bdata, size, SMP_CACHE_BYTES, goal, limit); | 
| Yasunori Goto | e70260a | 2008-04-28 02:13:32 -0700 | [diff] [blame] | 688 | } | 
 | 689 | #endif | 
 | 690 |  | 
| Andi Kleen | b54bbf7 | 2008-07-23 21:27:45 -0700 | [diff] [blame] | 691 | void * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, | 
 | 692 | 				   unsigned long align, unsigned long goal) | 
 | 693 | { | 
 | 694 | 	void *ptr; | 
 | 695 |  | 
| Tejun Heo | d0c4f57 | 2009-03-01 16:06:56 +0900 | [diff] [blame] | 696 | 	ptr = alloc_arch_preferred_bootmem(pgdat->bdata, size, align, goal, 0); | 
 | 697 | 	if (ptr) | 
 | 698 | 		return ptr; | 
 | 699 |  | 
| Andi Kleen | b54bbf7 | 2008-07-23 21:27:45 -0700 | [diff] [blame] | 700 | 	ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); | 
 | 701 | 	if (ptr) | 
 | 702 | 		return ptr; | 
 | 703 |  | 
 | 704 | 	return __alloc_bootmem_nopanic(size, align, goal); | 
 | 705 | } | 
 | 706 |  | 
| Heiko Carstens | dfd54cb | 2006-09-25 23:31:33 -0700 | [diff] [blame] | 707 | #ifndef ARCH_LOW_ADDRESS_LIMIT | 
 | 708 | #define ARCH_LOW_ADDRESS_LIMIT	0xffffffffUL | 
 | 709 | #endif | 
| Ravikiran G Thirumalai | 008857c | 2006-01-06 00:11:01 -0800 | [diff] [blame] | 710 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 711 | /** | 
 | 712 |  * __alloc_bootmem_low - allocate low boot memory | 
 | 713 |  * @size: size of the request in bytes | 
 | 714 |  * @align: alignment of the region | 
 | 715 |  * @goal: preferred starting address of the region | 
 | 716 |  * | 
 | 717 |  * The goal is dropped if it can not be satisfied and the allocation will | 
 | 718 |  * fall back to memory below @goal. | 
 | 719 |  * | 
 | 720 |  * Allocation may happen on any node in the system. | 
 | 721 |  * | 
 | 722 |  * The function panics if the request can not be satisfied. | 
 | 723 |  */ | 
| Franck Bui-Huu | bb0923a | 2006-09-25 23:31:05 -0700 | [diff] [blame] | 724 | void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, | 
 | 725 | 				  unsigned long goal) | 
| Ravikiran G Thirumalai | 008857c | 2006-01-06 00:11:01 -0800 | [diff] [blame] | 726 | { | 
| Johannes Weiner | 0f3caba | 2008-07-23 21:28:07 -0700 | [diff] [blame] | 727 | 	return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT); | 
| Ravikiran G Thirumalai | 008857c | 2006-01-06 00:11:01 -0800 | [diff] [blame] | 728 | } | 
 | 729 |  | 
| Johannes Weiner | a66fd7d | 2008-07-23 21:28:01 -0700 | [diff] [blame] | 730 | /** | 
 | 731 |  * __alloc_bootmem_low_node - allocate low boot memory from a specific node | 
 | 732 |  * @pgdat: node to allocate from | 
 | 733 |  * @size: size of the request in bytes | 
 | 734 |  * @align: alignment of the region | 
 | 735 |  * @goal: preferred starting address of the region | 
 | 736 |  * | 
 | 737 |  * The goal is dropped if it can not be satisfied and the allocation will | 
 | 738 |  * fall back to memory below @goal. | 
 | 739 |  * | 
 | 740 |  * Allocation may fall back to any node in the system if the specified node | 
 | 741 |  * can not hold the requested memory. | 
 | 742 |  * | 
 | 743 |  * The function panics if the request can not be satisfied. | 
 | 744 |  */ | 
| Ravikiran G Thirumalai | 008857c | 2006-01-06 00:11:01 -0800 | [diff] [blame] | 745 | void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, | 
 | 746 | 				       unsigned long align, unsigned long goal) | 
 | 747 | { | 
| Johannes Weiner | 4cc278b | 2008-07-23 21:28:08 -0700 | [diff] [blame] | 748 | 	return ___alloc_bootmem_node(pgdat->bdata, size, align, | 
 | 749 | 				goal, ARCH_LOW_ADDRESS_LIMIT); | 
| Ravikiran G Thirumalai | 008857c | 2006-01-06 00:11:01 -0800 | [diff] [blame] | 750 | } |