aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/early_res.c
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2010-02-16 21:40:35 -0500
committerH. Peter Anvin <hpa@zytor.com>2010-02-17 00:43:39 -0500
commit580e0ad21d6d6f932461d24b47041e3dd499c23f (patch)
tree3476c71849c0f9aa54c160b9a994bf4d211bd697 /kernel/early_res.c
parentdd645cee7b50b61cb2d05b59eb6027679c437af6 (diff)
core: Move early_res from arch/x86 to kernel/
This makes the range reservation feature available to other architectures. -v2: add get_max_mapped, max_pfn_mapped only defined in x86... to fix PPC compiling -v3: according to hpa, add CONFIG_HAVE_EARLY_RES -v4: fix typo about EARLY_RES in config Signed-off-by: Yinghai Lu <yinghai@kernel.org> LKML-Reference: <4B7B5723.4070009@kernel.org> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'kernel/early_res.c')
-rw-r--r--kernel/early_res.c513
1 files changed, 513 insertions, 0 deletions
diff --git a/kernel/early_res.c b/kernel/early_res.c
new file mode 100644
index 000000000000..aa5494ac4462
--- /dev/null
+++ b/kernel/early_res.c
@@ -0,0 +1,513 @@
1/*
2 * early_res, could be used to replace bootmem
3 */
4#include <linux/kernel.h>
5#include <linux/types.h>
6#include <linux/init.h>
7#include <linux/bootmem.h>
8#include <linux/mm.h>
9#include <linux/early_res.h>
10
11/*
12 * Early reserved memory areas.
13 */
14/*
15 * need to make sure this one is bigger enough before
16 * find_fw_memmap_area could be used
17 */
18#define MAX_EARLY_RES_X 32
19
20struct early_res {
21 u64 start, end;
22 char name[15];
23 char overlap_ok;
24};
25static struct early_res early_res_x[MAX_EARLY_RES_X] __initdata;
26
27static int max_early_res __initdata = MAX_EARLY_RES_X;
28static struct early_res *early_res __initdata = &early_res_x[0];
29static int early_res_count __initdata;
30
31static int __init find_overlapped_early(u64 start, u64 end)
32{
33 int i;
34 struct early_res *r;
35
36 for (i = 0; i < max_early_res && early_res[i].end; i++) {
37 r = &early_res[i];
38 if (end > r->start && start < r->end)
39 break;
40 }
41
42 return i;
43}
44
45/*
46 * Drop the i-th range from the early reservation map,
47 * by copying any higher ranges down one over it, and
48 * clearing what had been the last slot.
49 */
50static void __init drop_range(int i)
51{
52 int j;
53
54 for (j = i + 1; j < max_early_res && early_res[j].end; j++)
55 ;
56
57 memmove(&early_res[i], &early_res[i + 1],
58 (j - 1 - i) * sizeof(struct early_res));
59
60 early_res[j - 1].end = 0;
61 early_res_count--;
62}
63
64/*
65 * Split any existing ranges that:
66 * 1) are marked 'overlap_ok', and
67 * 2) overlap with the stated range [start, end)
68 * into whatever portion (if any) of the existing range is entirely
69 * below or entirely above the stated range. Drop the portion
70 * of the existing range that overlaps with the stated range,
71 * which will allow the caller of this routine to then add that
72 * stated range without conflicting with any existing range.
73 */
74static void __init drop_overlaps_that_are_ok(u64 start, u64 end)
75{
76 int i;
77 struct early_res *r;
78 u64 lower_start, lower_end;
79 u64 upper_start, upper_end;
80 char name[15];
81
82 for (i = 0; i < max_early_res && early_res[i].end; i++) {
83 r = &early_res[i];
84
85 /* Continue past non-overlapping ranges */
86 if (end <= r->start || start >= r->end)
87 continue;
88
89 /*
90 * Leave non-ok overlaps as is; let caller
91 * panic "Overlapping early reservations"
92 * when it hits this overlap.
93 */
94 if (!r->overlap_ok)
95 return;
96
97 /*
98 * We have an ok overlap. We will drop it from the early
99 * reservation map, and add back in any non-overlapping
100 * portions (lower or upper) as separate, overlap_ok,
101 * non-overlapping ranges.
102 */
103
104 /* 1. Note any non-overlapping (lower or upper) ranges. */
105 strncpy(name, r->name, sizeof(name) - 1);
106
107 lower_start = lower_end = 0;
108 upper_start = upper_end = 0;
109 if (r->start < start) {
110 lower_start = r->start;
111 lower_end = start;
112 }
113 if (r->end > end) {
114 upper_start = end;
115 upper_end = r->end;
116 }
117
118 /* 2. Drop the original ok overlapping range */
119 drop_range(i);
120
121 i--; /* resume for-loop on copied down entry */
122
123 /* 3. Add back in any non-overlapping ranges. */
124 if (lower_end)
125 reserve_early_overlap_ok(lower_start, lower_end, name);
126 if (upper_end)
127 reserve_early_overlap_ok(upper_start, upper_end, name);
128 }
129}
130
131static void __init __reserve_early(u64 start, u64 end, char *name,
132 int overlap_ok)
133{
134 int i;
135 struct early_res *r;
136
137 i = find_overlapped_early(start, end);
138 if (i >= max_early_res)
139 panic("Too many early reservations");
140 r = &early_res[i];
141 if (r->end)
142 panic("Overlapping early reservations "
143 "%llx-%llx %s to %llx-%llx %s\n",
144 start, end - 1, name ? name : "", r->start,
145 r->end - 1, r->name);
146 r->start = start;
147 r->end = end;
148 r->overlap_ok = overlap_ok;
149 if (name)
150 strncpy(r->name, name, sizeof(r->name) - 1);
151 early_res_count++;
152}
153
154/*
155 * A few early reservtations come here.
156 *
157 * The 'overlap_ok' in the name of this routine does -not- mean it
158 * is ok for these reservations to overlap an earlier reservation.
159 * Rather it means that it is ok for subsequent reservations to
160 * overlap this one.
161 *
162 * Use this entry point to reserve early ranges when you are doing
163 * so out of "Paranoia", reserving perhaps more memory than you need,
164 * just in case, and don't mind a subsequent overlapping reservation
165 * that is known to be needed.
166 *
167 * The drop_overlaps_that_are_ok() call here isn't really needed.
168 * It would be needed if we had two colliding 'overlap_ok'
169 * reservations, so that the second such would not panic on the
170 * overlap with the first. We don't have any such as of this
171 * writing, but might as well tolerate such if it happens in
172 * the future.
173 */
174void __init reserve_early_overlap_ok(u64 start, u64 end, char *name)
175{
176 drop_overlaps_that_are_ok(start, end);
177 __reserve_early(start, end, name, 1);
178}
179
180static void __init __check_and_double_early_res(u64 ex_start, u64 ex_end)
181{
182 u64 start, end, size, mem;
183 struct early_res *new;
184
185 /* do we have enough slots left ? */
186 if ((max_early_res - early_res_count) > max(max_early_res/8, 2))
187 return;
188
189 /* double it */
190 mem = -1ULL;
191 size = sizeof(struct early_res) * max_early_res * 2;
192 if (early_res == early_res_x)
193 start = 0;
194 else
195 start = early_res[0].end;
196 end = ex_start;
197 if (start + size < end)
198 mem = find_fw_memmap_area(start, end, size,
199 sizeof(struct early_res));
200 if (mem == -1ULL) {
201 start = ex_end;
202 end = get_max_mapped();
203 if (start + size < end)
204 mem = find_fw_memmap_area(start, end, size,
205 sizeof(struct early_res));
206 }
207 if (mem == -1ULL)
208 panic("can not find more space for early_res array");
209
210 new = __va(mem);
211 /* save the first one for own */
212 new[0].start = mem;
213 new[0].end = mem + size;
214 new[0].overlap_ok = 0;
215 /* copy old to new */
216 if (early_res == early_res_x) {
217 memcpy(&new[1], &early_res[0],
218 sizeof(struct early_res) * max_early_res);
219 memset(&new[max_early_res+1], 0,
220 sizeof(struct early_res) * (max_early_res - 1));
221 early_res_count++;
222 } else {
223 memcpy(&new[1], &early_res[1],
224 sizeof(struct early_res) * (max_early_res - 1));
225 memset(&new[max_early_res], 0,
226 sizeof(struct early_res) * max_early_res);
227 }
228 memset(&early_res[0], 0, sizeof(struct early_res) * max_early_res);
229 early_res = new;
230 max_early_res *= 2;
231 printk(KERN_DEBUG "early_res array is doubled to %d at [%llx - %llx]\n",
232 max_early_res, mem, mem + size - 1);
233}
234
235/*
236 * Most early reservations come here.
237 *
238 * We first have drop_overlaps_that_are_ok() drop any pre-existing
239 * 'overlap_ok' ranges, so that we can then reserve this memory
240 * range without risk of panic'ing on an overlapping overlap_ok
241 * early reservation.
242 */
243void __init reserve_early(u64 start, u64 end, char *name)
244{
245 if (start >= end)
246 return;
247
248 __check_and_double_early_res(start, end);
249
250 drop_overlaps_that_are_ok(start, end);
251 __reserve_early(start, end, name, 0);
252}
253
254void __init reserve_early_without_check(u64 start, u64 end, char *name)
255{
256 struct early_res *r;
257
258 if (start >= end)
259 return;
260
261 __check_and_double_early_res(start, end);
262
263 r = &early_res[early_res_count];
264
265 r->start = start;
266 r->end = end;
267 r->overlap_ok = 0;
268 if (name)
269 strncpy(r->name, name, sizeof(r->name) - 1);
270 early_res_count++;
271}
272
273void __init free_early(u64 start, u64 end)
274{
275 struct early_res *r;
276 int i;
277
278 i = find_overlapped_early(start, end);
279 r = &early_res[i];
280 if (i >= max_early_res || r->end != end || r->start != start)
281 panic("free_early on not reserved area: %llx-%llx!",
282 start, end - 1);
283
284 drop_range(i);
285}
286
287#ifdef CONFIG_NO_BOOTMEM
288static void __init subtract_early_res(struct range *range, int az)
289{
290 int i, count;
291 u64 final_start, final_end;
292 int idx = 0;
293
294 count = 0;
295 for (i = 0; i < max_early_res && early_res[i].end; i++)
296 count++;
297
298 /* need to skip first one ?*/
299 if (early_res != early_res_x)
300 idx = 1;
301
302#define DEBUG_PRINT_EARLY_RES 1
303
304#if DEBUG_PRINT_EARLY_RES
305 printk(KERN_INFO "Subtract (%d early reservations)\n", count);
306#endif
307 for (i = idx; i < count; i++) {
308 struct early_res *r = &early_res[i];
309#if DEBUG_PRINT_EARLY_RES
310 printk(KERN_INFO " #%d [%010llx - %010llx] %15s\n", i,
311 r->start, r->end, r->name);
312#endif
313 final_start = PFN_DOWN(r->start);
314 final_end = PFN_UP(r->end);
315 if (final_start >= final_end)
316 continue;
317 subtract_range(range, az, final_start, final_end);
318 }
319
320}
321
322int __init get_free_all_memory_range(struct range **rangep, int nodeid)
323{
324 int i, count;
325 u64 start = 0, end;
326 u64 size;
327 u64 mem;
328 struct range *range;
329 int nr_range;
330
331 count = 0;
332 for (i = 0; i < max_early_res && early_res[i].end; i++)
333 count++;
334
335 count *= 2;
336
337 size = sizeof(struct range) * count;
338 end = get_max_mapped();
339#ifdef MAX_DMA32_PFN
340 if (end > (MAX_DMA32_PFN << PAGE_SHIFT))
341 start = MAX_DMA32_PFN << PAGE_SHIFT;
342#endif
343 mem = find_fw_memmap_area(start, end, size, sizeof(struct range));
344 if (mem == -1ULL)
345 panic("can not find more space for range free");
346
347 range = __va(mem);
348 /* use early_node_map[] and early_res to get range array at first */
349 memset(range, 0, size);
350 nr_range = 0;
351
352 /* need to go over early_node_map to find out good range for node */
353 nr_range = add_from_early_node_map(range, count, nr_range, nodeid);
354#ifdef CONFIG_X86_32
355 subtract_range(range, count, max_low_pfn, -1ULL);
356#endif
357 subtract_early_res(range, count);
358 nr_range = clean_sort_range(range, count);
359
360 /* need to clear it ? */
361 if (nodeid == MAX_NUMNODES) {
362 memset(&early_res[0], 0,
363 sizeof(struct early_res) * max_early_res);
364 early_res = NULL;
365 max_early_res = 0;
366 }
367
368 *rangep = range;
369 return nr_range;
370}
371#else
372void __init early_res_to_bootmem(u64 start, u64 end)
373{
374 int i, count;
375 u64 final_start, final_end;
376 int idx = 0;
377
378 count = 0;
379 for (i = 0; i < max_early_res && early_res[i].end; i++)
380 count++;
381
382 /* need to skip first one ?*/
383 if (early_res != early_res_x)
384 idx = 1;
385
386 printk(KERN_INFO "(%d/%d early reservations) ==> bootmem [%010llx - %010llx]\n",
387 count - idx, max_early_res, start, end);
388 for (i = idx; i < count; i++) {
389 struct early_res *r = &early_res[i];
390 printk(KERN_INFO " #%d [%010llx - %010llx] %16s", i,
391 r->start, r->end, r->name);
392 final_start = max(start, r->start);
393 final_end = min(end, r->end);
394 if (final_start >= final_end) {
395 printk(KERN_CONT "\n");
396 continue;
397 }
398 printk(KERN_CONT " ==> [%010llx - %010llx]\n",
399 final_start, final_end);
400 reserve_bootmem_generic(final_start, final_end - final_start,
401 BOOTMEM_DEFAULT);
402 }
403 /* clear them */
404 memset(&early_res[0], 0, sizeof(struct early_res) * max_early_res);
405 early_res = NULL;
406 max_early_res = 0;
407 early_res_count = 0;
408}
409#endif
410
411/* Check for already reserved areas */
412static inline int __init bad_addr(u64 *addrp, u64 size, u64 align)
413{
414 int i;
415 u64 addr = *addrp;
416 int changed = 0;
417 struct early_res *r;
418again:
419 i = find_overlapped_early(addr, addr + size);
420 r = &early_res[i];
421 if (i < max_early_res && r->end) {
422 *addrp = addr = round_up(r->end, align);
423 changed = 1;
424 goto again;
425 }
426 return changed;
427}
428
429/* Check for already reserved areas */
430static inline int __init bad_addr_size(u64 *addrp, u64 *sizep, u64 align)
431{
432 int i;
433 u64 addr = *addrp, last;
434 u64 size = *sizep;
435 int changed = 0;
436again:
437 last = addr + size;
438 for (i = 0; i < max_early_res && early_res[i].end; i++) {
439 struct early_res *r = &early_res[i];
440 if (last > r->start && addr < r->start) {
441 size = r->start - addr;
442 changed = 1;
443 goto again;
444 }
445 if (last > r->end && addr < r->end) {
446 addr = round_up(r->end, align);
447 size = last - addr;
448 changed = 1;
449 goto again;
450 }
451 if (last <= r->end && addr >= r->start) {
452 (*sizep)++;
453 return 0;
454 }
455 }
456 if (changed) {
457 *addrp = addr;
458 *sizep = size;
459 }
460 return changed;
461}
462
463/*
464 * Find a free area with specified alignment in a specific range.
465 * only with the area.between start to end is active range from early_node_map
466 * so they are good as RAM
467 */
468u64 __init find_early_area(u64 ei_start, u64 ei_last, u64 start, u64 end,
469 u64 size, u64 align)
470{
471 u64 addr, last;
472
473 addr = round_up(ei_start, align);
474 if (addr < start)
475 addr = round_up(start, align);
476 if (addr >= ei_last)
477 goto out;
478 while (bad_addr(&addr, size, align) && addr+size <= ei_last)
479 ;
480 last = addr + size;
481 if (last > ei_last)
482 goto out;
483 if (last > end)
484 goto out;
485
486 return addr;
487
488out:
489 return -1ULL;
490}
491
492u64 __init find_early_area_size(u64 ei_start, u64 ei_last, u64 start,
493 u64 *sizep, u64 align)
494{
495 u64 addr, last;
496
497 addr = round_up(ei_start, align);
498 if (addr < start)
499 addr = round_up(start, align);
500 if (addr >= ei_last)
501 goto out;
502 *sizep = ei_last - addr;
503 while (bad_addr_size(&addr, sizep, align) && addr + *sizep <= ei_last)
504 ;
505 last = addr + *sizep;
506 if (last > ei_last)
507 goto out;
508
509 return addr;
510
511out:
512 return -1ULL;
513}