📄 bootmem.c
字号:
* @pgdat: node the range resides on * @physaddr: starting address of the range * @size: size of the range in bytes * @flags: reservation flags (see linux/bootmem.h) * * Partial pages will be reserved. * * The range must reside completely on the specified node. */int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, unsigned long size, int flags){ unsigned long start, end; start = PFN_DOWN(physaddr); end = PFN_UP(physaddr + size); return mark_bootmem_node(pgdat->bdata, start, end, 1, flags);}#ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE/** * reserve_bootmem - mark a page range as usable * @addr: starting address of the range * @size: size of the range in bytes * @flags: reservation flags (see linux/bootmem.h) * * Partial pages will be reserved. * * The range must be contiguous but may span node boundaries. */int __init reserve_bootmem(unsigned long addr, unsigned long size, int flags){ unsigned long start, end; start = PFN_DOWN(addr); end = PFN_UP(addr + size); return mark_bootmem(start, end, 1, flags);}#endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */static unsigned long align_idx(struct bootmem_data *bdata, unsigned long idx, unsigned long step){ unsigned long base = bdata->node_min_pfn; /* * Align the index with respect to the node start so that the * combination of both satisfies the requested alignment. */ return ALIGN(base + idx, step) - base;}static unsigned long align_off(struct bootmem_data *bdata, unsigned long off, unsigned long align){ unsigned long base = PFN_PHYS(bdata->node_min_pfn); /* Same as align_idx for byte offsets */ return ALIGN(base + off, align) - base;}static void * __init alloc_bootmem_core(struct bootmem_data *bdata, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit){ unsigned long fallback = 0; unsigned long min, max, start, sidx, midx, step; bdebug("nid=%td size=%lx [%lu pages] align=%lx goal=%lx limit=%lx\n", bdata - bootmem_node_data, size, PAGE_ALIGN(size) >> PAGE_SHIFT, align, goal, limit); BUG_ON(!size); BUG_ON(align & (align - 1)); BUG_ON(limit && goal + size > limit); if (!bdata->node_bootmem_map) return NULL; min = bdata->node_min_pfn; max = bdata->node_low_pfn; goal >>= PAGE_SHIFT; limit >>= PAGE_SHIFT; if (limit && max > limit) max = limit; if (max <= min) return NULL; step = max(align >> PAGE_SHIFT, 1UL); if (goal && min < goal && goal < max) start = ALIGN(goal, step); else start = ALIGN(min, step); sidx = start - bdata->node_min_pfn; midx = max - bdata->node_min_pfn; if (bdata->hint_idx > sidx) { /* * Handle the valid case of sidx being zero and still * catch the fallback below. */ fallback = sidx + 1; sidx = align_idx(bdata, bdata->hint_idx, step); } while (1) { int merge; void *region; unsigned long eidx, i, start_off, end_off;find_block: sidx = find_next_zero_bit(bdata->node_bootmem_map, midx, sidx); sidx = align_idx(bdata, sidx, step); eidx = sidx + PFN_UP(size); if (sidx >= midx || eidx > midx) break; for (i = sidx; i < eidx; i++) if (test_bit(i, bdata->node_bootmem_map)) { sidx = align_idx(bdata, i, step); if (sidx == i) sidx += step; goto find_block; } if (bdata->last_end_off & (PAGE_SIZE - 1) && PFN_DOWN(bdata->last_end_off) + 1 == sidx) start_off = align_off(bdata, bdata->last_end_off, align); else start_off = PFN_PHYS(sidx); merge = PFN_DOWN(start_off) < sidx; end_off = start_off + size; bdata->last_end_off = end_off; bdata->hint_idx = PFN_UP(end_off); /* * Reserve the area now: */ if (__reserve(bdata, PFN_DOWN(start_off) + merge, PFN_UP(end_off), BOOTMEM_EXCLUSIVE)) BUG(); region = phys_to_virt(PFN_PHYS(bdata->node_min_pfn) + start_off); memset(region, 0, size); return region; } if (fallback) { sidx = align_idx(bdata, fallback - 1, step); fallback = 0; goto find_block; } return NULL;}static void * __init ___alloc_bootmem_nopanic(unsigned long size, unsigned long align, unsigned long goal, unsigned long limit){ bootmem_data_t *bdata;restart: list_for_each_entry(bdata, &bdata_list, list) { void *region; if (goal && bdata->node_low_pfn <= PFN_DOWN(goal)) continue; if (limit && bdata->node_min_pfn >= PFN_DOWN(limit)) break; region = alloc_bootmem_core(bdata, size, align, goal, limit); if (region) return region; } if (goal) { goal = 0; goto restart; } return NULL;}/** * __alloc_bootmem_nopanic - allocate boot memory without panicking * @size: size of the request in bytes * @align: alignment of the region * @goal: preferred starting address of the region * * The goal is dropped if it can not be satisfied and the allocation will * fall back to memory below @goal. * * Allocation may happen on any node in the system. * * Returns NULL on failure. */void * __init __alloc_bootmem_nopanic(unsigned long size, unsigned long align, unsigned long goal){ return ___alloc_bootmem_nopanic(size, align, goal, 0);}static void * __init ___alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal, unsigned long limit){ void *mem = ___alloc_bootmem_nopanic(size, align, goal, limit); if (mem) return mem; /* * Whoops, we cannot satisfy the allocation request. */ printk(KERN_ALERT "bootmem alloc of %lu bytes failed!\n", size); panic("Out of memory"); return NULL;}/** * __alloc_bootmem - allocate boot memory * @size: size of the request in bytes * @align: alignment of the region * @goal: preferred starting address of the region * * The goal is dropped if it can not be satisfied and the allocation will * fall back to memory below @goal. * * Allocation may happen on any node in the system. * * The function panics if the request can not be satisfied. */void * __init __alloc_bootmem(unsigned long size, unsigned long align, unsigned long goal){ return ___alloc_bootmem(size, align, goal, 0);}static void * __init ___alloc_bootmem_node(bootmem_data_t *bdata, unsigned long size, unsigned long align, unsigned long goal, unsigned long limit){ void *ptr; ptr = alloc_bootmem_core(bdata, size, align, goal, limit); if (ptr) return ptr; return ___alloc_bootmem(size, align, goal, limit);}/** * __alloc_bootmem_node - allocate boot memory from a specific node * @pgdat: node to allocate from * @size: size of the request in bytes * @align: alignment of the region * @goal: preferred starting address of the region * * The goal is dropped if it can not be satisfied and the allocation will * fall back to memory below @goal. * * Allocation may fall back to any node in the system if the specified node * can not hold the requested memory. * * The function panics if the request can not be satisfied. */void * __init __alloc_bootmem_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal){ return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, 0);}#ifdef CONFIG_SPARSEMEM/** * alloc_bootmem_section - allocate boot memory from a specific section * @size: size of the request in bytes * @section_nr: sparse map section to allocate from * * Return NULL on failure. */void * __init alloc_bootmem_section(unsigned long size, unsigned long section_nr){ bootmem_data_t *bdata; unsigned long pfn, goal, limit; pfn = section_nr_to_pfn(section_nr); goal = pfn << PAGE_SHIFT; limit = section_nr_to_pfn(section_nr + 1) << PAGE_SHIFT; bdata = &bootmem_node_data[early_pfn_to_nid(pfn)]; return alloc_bootmem_core(bdata, size, SMP_CACHE_BYTES, goal, limit);}#endifvoid * __init __alloc_bootmem_node_nopanic(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal){ void *ptr; ptr = alloc_bootmem_core(pgdat->bdata, size, align, goal, 0); if (ptr) return ptr; return __alloc_bootmem_nopanic(size, align, goal);}#ifndef ARCH_LOW_ADDRESS_LIMIT#define ARCH_LOW_ADDRESS_LIMIT 0xffffffffUL#endif/** * __alloc_bootmem_low - allocate low boot memory * @size: size of the request in bytes * @align: alignment of the region * @goal: preferred starting address of the region * * The goal is dropped if it can not be satisfied and the allocation will * fall back to memory below @goal. * * Allocation may happen on any node in the system. * * The function panics if the request can not be satisfied. */void * __init __alloc_bootmem_low(unsigned long size, unsigned long align, unsigned long goal){ return ___alloc_bootmem(size, align, goal, ARCH_LOW_ADDRESS_LIMIT);}/** * __alloc_bootmem_low_node - allocate low boot memory from a specific node * @pgdat: node to allocate from * @size: size of the request in bytes * @align: alignment of the region * @goal: preferred starting address of the region * * The goal is dropped if it can not be satisfied and the allocation will * fall back to memory below @goal. * * Allocation may fall back to any node in the system if the specified node * can not hold the requested memory. * * The function panics if the request can not be satisfied. */void * __init __alloc_bootmem_low_node(pg_data_t *pgdat, unsigned long size, unsigned long align, unsigned long goal){ return ___alloc_bootmem_node(pgdat->bdata, size, align, goal, ARCH_LOW_ADDRESS_LIMIT);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -