aboutsummaryrefslogtreecommitdiffstats
path: root/mm/sparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/sparse.c')
-rw-r--r--mm/sparse.c99
1 files changed, 94 insertions, 5 deletions
diff --git a/mm/sparse.c b/mm/sparse.c
index 347249a4917a..72079b538e2d 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -5,8 +5,10 @@
5#include <linux/mm.h> 5#include <linux/mm.h>
6#include <linux/mmzone.h> 6#include <linux/mmzone.h>
7#include <linux/bootmem.h> 7#include <linux/bootmem.h>
8#include <linux/highmem.h>
8#include <linux/module.h> 9#include <linux/module.h>
9#include <linux/spinlock.h> 10#include <linux/spinlock.h>
11#include <linux/vmalloc.h>
10#include <asm/dma.h> 12#include <asm/dma.h>
11 13
12/* 14/*
@@ -72,6 +74,31 @@ static inline int sparse_index_init(unsigned long section_nr, int nid)
72} 74}
73#endif 75#endif
74 76
77/*
78 * Although written for the SPARSEMEM_EXTREME case, this happens
79 * to also work for the flat array case becase
80 * NR_SECTION_ROOTS==NR_MEM_SECTIONS.
81 */
82int __section_nr(struct mem_section* ms)
83{
84 unsigned long root_nr;
85 struct mem_section* root;
86
87 for (root_nr = 0;
88 root_nr < NR_MEM_SECTIONS;
89 root_nr += SECTIONS_PER_ROOT) {
90 root = __nr_to_section(root_nr);
91
92 if (!root)
93 continue;
94
95 if ((ms >= root) && (ms < (root + SECTIONS_PER_ROOT)))
96 break;
97 }
98
99 return (root_nr * SECTIONS_PER_ROOT) + (ms - root);
100}
101
75/* Record a memory area against a node. */ 102/* Record a memory area against a node. */
76void memory_present(int nid, unsigned long start, unsigned long end) 103void memory_present(int nid, unsigned long start, unsigned long end)
77{ 104{
@@ -162,6 +189,45 @@ static struct page *sparse_early_mem_map_alloc(unsigned long pnum)
162 return NULL; 189 return NULL;
163} 190}
164 191
192static struct page *__kmalloc_section_memmap(unsigned long nr_pages)
193{
194 struct page *page, *ret;
195 unsigned long memmap_size = sizeof(struct page) * nr_pages;
196
197 page = alloc_pages(GFP_KERNEL, get_order(memmap_size));
198 if (page)
199 goto got_map_page;
200
201 ret = vmalloc(memmap_size);
202 if (ret)
203 goto got_map_ptr;
204
205 return NULL;
206got_map_page:
207 ret = (struct page *)pfn_to_kaddr(page_to_pfn(page));
208got_map_ptr:
209 memset(ret, 0, memmap_size);
210
211 return ret;
212}
213
214static int vaddr_in_vmalloc_area(void *addr)
215{
216 if (addr >= (void *)VMALLOC_START &&
217 addr < (void *)VMALLOC_END)
218 return 1;
219 return 0;
220}
221
222static void __kfree_section_memmap(struct page *memmap, unsigned long nr_pages)
223{
224 if (vaddr_in_vmalloc_area(memmap))
225 vfree(memmap);
226 else
227 free_pages((unsigned long)memmap,
228 get_order(sizeof(struct page) * nr_pages));
229}
230
165/* 231/*
166 * Allocate the accumulated non-linear sections, allocate a mem_map 232 * Allocate the accumulated non-linear sections, allocate a mem_map
167 * for each and record the physical to section mapping. 233 * for each and record the physical to section mapping.
@@ -187,14 +253,37 @@ void sparse_init(void)
187 * set. If this is <=0, then that means that the passed-in 253 * set. If this is <=0, then that means that the passed-in
188 * map was not consumed and must be freed. 254 * map was not consumed and must be freed.
189 */ 255 */
190int sparse_add_one_section(unsigned long start_pfn, int nr_pages, struct page *map) 256int sparse_add_one_section(struct zone *zone, unsigned long start_pfn,
257 int nr_pages)
191{ 258{
192 struct mem_section *ms = __pfn_to_section(start_pfn); 259 unsigned long section_nr = pfn_to_section_nr(start_pfn);
260 struct pglist_data *pgdat = zone->zone_pgdat;
261 struct mem_section *ms;
262 struct page *memmap;
263 unsigned long flags;
264 int ret;
193 265
194 if (ms->section_mem_map & SECTION_MARKED_PRESENT) 266 /*
195 return -EEXIST; 267 * no locking for this, because it does its own
268 * plus, it does a kmalloc
269 */
270 sparse_index_init(section_nr, pgdat->node_id);
271 memmap = __kmalloc_section_memmap(nr_pages);
272
273 pgdat_resize_lock(pgdat, &flags);
196 274
275 ms = __pfn_to_section(start_pfn);
276 if (ms->section_mem_map & SECTION_MARKED_PRESENT) {
277 ret = -EEXIST;
278 goto out;
279 }
197 ms->section_mem_map |= SECTION_MARKED_PRESENT; 280 ms->section_mem_map |= SECTION_MARKED_PRESENT;
198 281
199 return sparse_init_one_section(ms, pfn_to_section_nr(start_pfn), map); 282 ret = sparse_init_one_section(ms, section_nr, memmap);
283
284 if (ret <= 0)
285 __kfree_section_memmap(memmap, nr_pages);
286out:
287 pgdat_resize_unlock(pgdat, &flags);
288 return ret;
200} 289}