📄 discontig.c
字号:
/* * Copyright (c) 2000, 2003 Silicon Graphics, Inc. All rights reserved. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Tony Luck <tony.luck@intel.com> * Copyright (c) 2002 NEC Corp. * Copyright (c) 2002 Kimio Suganuma <k-suganuma@da.jp.nec.com> * Copyright (c) 2004 Silicon Graphics, Inc * Russ Anderson <rja@sgi.com> * Jesse Barnes <jbarnes@sgi.com> * Jack Steiner <steiner@sgi.com> *//* * Platform initialization for Discontig Memory */#include <linux/kernel.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/bootmem.h>#include <linux/acpi.h>#include <linux/efi.h>#include <linux/nodemask.h>#include <asm/pgalloc.h>#include <asm/tlb.h>#include <asm/meminit.h>#include <asm/numa.h>#include <asm/sections.h>/* * Track per-node information needed to setup the boot memory allocator, the * per-node areas, and the real VM. */struct early_node_data { struct ia64_node_data *node_data; pg_data_t *pgdat; unsigned long pernode_addr; unsigned long pernode_size; struct bootmem_data bootmem_data; unsigned long num_physpages; unsigned long num_dma_physpages; unsigned long min_pfn; unsigned long max_pfn;};static struct early_node_data mem_data[MAX_NUMNODES] __initdata;static nodemask_t memory_less_mask __initdata;/* * To prevent cache aliasing effects, align per-node structures so that they * start at addresses that are strided by node number. */#define MAX_NODE_ALIGN_OFFSET (32 * 1024 * 1024)#define NODEDATA_ALIGN(addr, node) \ ((((addr) + 1024*1024-1) & ~(1024*1024-1)) + \ (((node)*PERCPU_PAGE_SIZE) & (MAX_NODE_ALIGN_OFFSET - 1)))/** * build_node_maps - callback to setup bootmem structs for each node * @start: physical start of range * @len: length of range * @node: node where this range resides * * We allocate a struct bootmem_data for each piece of memory that we wish to * treat as a virtually contiguous block (i.e. each node). Each such block * must start on an %IA64_GRANULE_SIZE boundary, so we round the address down * if necessary. Any non-existent pages will simply be part of the virtual * memmap. We also update min_low_pfn and max_low_pfn here as we receive * memory ranges from the caller. */static int __init build_node_maps(unsigned long start, unsigned long len, int node){ unsigned long cstart, epfn, end = start + len; struct bootmem_data *bdp = &mem_data[node].bootmem_data; epfn = GRANULEROUNDUP(end) >> PAGE_SHIFT; cstart = GRANULEROUNDDOWN(start); if (!bdp->node_low_pfn) { bdp->node_boot_start = cstart; bdp->node_low_pfn = epfn; } else { bdp->node_boot_start = min(cstart, bdp->node_boot_start); bdp->node_low_pfn = max(epfn, bdp->node_low_pfn); } min_low_pfn = min(min_low_pfn, bdp->node_boot_start>>PAGE_SHIFT); max_low_pfn = max(max_low_pfn, bdp->node_low_pfn); return 0;}/** * early_nr_cpus_node - return number of cpus on a given node * @node: node to check * * Count the number of cpus on @node. We can't use nr_cpus_node() yet because * acpi_boot_init() (which builds the node_to_cpu_mask array) hasn't been * called yet. Note that node 0 will also count all non-existent cpus. */static int __init early_nr_cpus_node(int node){ int cpu, n = 0; for (cpu = 0; cpu < NR_CPUS; cpu++) if (node == node_cpuid[cpu].nid) n++; return n;}/** * compute_pernodesize - compute size of pernode data * @node: the node id. */static unsigned long __init compute_pernodesize(int node){ unsigned long pernodesize = 0, cpus; cpus = early_nr_cpus_node(node); pernodesize += PERCPU_PAGE_SIZE * cpus; pernodesize += node * L1_CACHE_BYTES; pernodesize += L1_CACHE_ALIGN(sizeof(pg_data_t)); pernodesize += L1_CACHE_ALIGN(sizeof(struct ia64_node_data)); pernodesize = PAGE_ALIGN(pernodesize); return pernodesize;}/** * per_cpu_node_setup - setup per-cpu areas on each node * @cpu_data: per-cpu area on this node * @node: node to setup * * Copy the static per-cpu data into the region we just set aside and then * setup __per_cpu_offset for each CPU on this node. Return a pointer to * the end of the area. */static void *per_cpu_node_setup(void *cpu_data, int node){#ifdef CONFIG_SMP int cpu; for (cpu = 0; cpu < NR_CPUS; cpu++) { if (node == node_cpuid[cpu].nid) { memcpy(__va(cpu_data), __phys_per_cpu_start, __per_cpu_end - __per_cpu_start); __per_cpu_offset[cpu] = (char*)__va(cpu_data) - __per_cpu_start; cpu_data += PERCPU_PAGE_SIZE; } }#endif return cpu_data;}/** * fill_pernode - initialize pernode data. * @node: the node id. * @pernode: physical address of pernode data * @pernodesize: size of the pernode data */static void __init fill_pernode(int node, unsigned long pernode, unsigned long pernodesize){ void *cpu_data; int cpus = early_nr_cpus_node(node); struct bootmem_data *bdp = &mem_data[node].bootmem_data; mem_data[node].pernode_addr = pernode; mem_data[node].pernode_size = pernodesize; memset(__va(pernode), 0, pernodesize); cpu_data = (void *)pernode; pernode += PERCPU_PAGE_SIZE * cpus; pernode += node * L1_CACHE_BYTES; mem_data[node].pgdat = __va(pernode); pernode += L1_CACHE_ALIGN(sizeof(pg_data_t)); mem_data[node].node_data = __va(pernode); pernode += L1_CACHE_ALIGN(sizeof(struct ia64_node_data)); mem_data[node].pgdat->bdata = bdp; pernode += L1_CACHE_ALIGN(sizeof(pg_data_t)); cpu_data = per_cpu_node_setup(cpu_data, node); return;}/** * find_pernode_space - allocate memory for memory map and per-node structures * @start: physical start of range * @len: length of range * @node: node where this range resides * * This routine reserves space for the per-cpu data struct, the list of * pg_data_ts and the per-node data struct. Each node will have something like * the following in the first chunk of addr. space large enough to hold it. * * ________________________ * | | * |~~~~~~~~~~~~~~~~~~~~~~~~| <-- NODEDATA_ALIGN(start, node) for the first * | PERCPU_PAGE_SIZE * | start and length big enough * | cpus_on_this_node | Node 0 will also have entries for all non-existent cpus. * |------------------------| * | local pg_data_t * | * |------------------------| * | local ia64_node_data | * |------------------------| * | ??? | * |________________________| * * Once this space has been set aside, the bootmem maps are initialized. We * could probably move the allocation of the per-cpu and ia64_node_data space * outside of this function and use alloc_bootmem_node(), but doing it here * is straightforward and we get the alignments we want so... */static int __init find_pernode_space(unsigned long start, unsigned long len, int node){ unsigned long epfn; unsigned long pernodesize = 0, pernode, pages, mapsize; struct bootmem_data *bdp = &mem_data[node].bootmem_data; epfn = (start + len) >> PAGE_SHIFT; pages = bdp->node_low_pfn - (bdp->node_boot_start >> PAGE_SHIFT); mapsize = bootmem_bootmap_pages(pages) << PAGE_SHIFT; /* * Make sure this memory falls within this node's usable memory * since we may have thrown some away in build_maps(). */ if (start < bdp->node_boot_start || epfn > bdp->node_low_pfn) return 0; /* Don't setup this node's local space twice... */ if (mem_data[node].pernode_addr) return 0; /* * Calculate total size needed, incl. what's necessary * for good alignment and alias prevention. */ pernodesize = compute_pernodesize(node); pernode = NODEDATA_ALIGN(start, node); /* Is this range big enough for what we want to store here? */ if (start + len > (pernode + pernodesize + mapsize)) fill_pernode(node, pernode, pernodesize); return 0;}/** * free_node_bootmem - free bootmem allocator memory for use * @start: physical start of range * @len: length of range * @node: node where this range resides * * Simply calls the bootmem allocator to free the specified ranged from * the given pg_data_t's bdata struct. After this function has been called * for all the entries in the EFI memory map, the bootmem allocator will * be ready to service allocation requests. */static int __init free_node_bootmem(unsigned long start, unsigned long len, int node){ free_bootmem_node(mem_data[node].pgdat, start, len); return 0;}/** * reserve_pernode_space - reserve memory for per-node space * * Reserve the space used by the bootmem maps & per-node space in the boot * allocator so that when we actually create the real mem maps we don't * use their memory. */static void __init reserve_pernode_space(void){ unsigned long base, size, pages; struct bootmem_data *bdp; int node; for_each_online_node(node) { pg_data_t *pdp = mem_data[node].pgdat; if (node_isset(node, memory_less_mask)) continue; bdp = pdp->bdata; /* First the bootmem_map itself */ pages = bdp->node_low_pfn - (bdp->node_boot_start>>PAGE_SHIFT); size = bootmem_bootmap_pages(pages) << PAGE_SHIFT; base = __pa(bdp->node_bootmem_map); reserve_bootmem_node(pdp, base, size); /* Now the per-node space */ size = mem_data[node].pernode_size; base = __pa(mem_data[node].pernode_addr); reserve_bootmem_node(pdp, base, size); }}/** * initialize_pernode_data - fixup per-cpu & per-node pointers * * Each node's per-node area has a copy of the global pg_data_t list, so * we copy that to each node here, as well as setting the per-cpu pointer * to the local node data structure. The active_cpus field of the per-node * structure gets setup by the platform_cpu_init() function later. */static void __init initialize_pernode_data(void){ pg_data_t *pgdat_list[MAX_NUMNODES]; int cpu, node; for_each_online_node(node) pgdat_list[node] = mem_data[node].pgdat; /* Copy the pg_data_t list to each node and init the node field */ for_each_online_node(node) { memcpy(mem_data[node].node_data->pg_data_ptrs, pgdat_list, sizeof(pgdat_list)); }#ifdef CONFIG_SMP /* Set the node_data pointer for each per-cpu struct */ for (cpu = 0; cpu < NR_CPUS; cpu++) { node = node_cpuid[cpu].nid; per_cpu(cpu_info, cpu).node_data = mem_data[node].node_data; }#else { struct cpuinfo_ia64 *cpu0_cpu_info; cpu = 0; node = node_cpuid[cpu].nid; cpu0_cpu_info = (struct cpuinfo_ia64 *)(__phys_per_cpu_start + ((char *)&per_cpu__cpu_info - __per_cpu_start)); cpu0_cpu_info->node_data = mem_data[node].node_data; }#endif /* CONFIG_SMP */}/** * memory_less_node_alloc - * attempt to allocate memory on the best NUMA slit * node but fall back to any other node when __alloc_bootmem_node fails * for best. * @nid: node id * @pernodesize: size of this node's pernode data */static void __init *memory_less_node_alloc(int nid, unsigned long pernodesize){ void *ptr = NULL; u8 best = 0xff; int bestnode = -1, node, anynode = 0; for_each_online_node(node) { if (node_isset(node, memory_less_mask)) continue; else if (node_distance(nid, node) < best) { best = node_distance(nid, node); bestnode = node; } anynode = node; } if (bestnode == -1) bestnode = anynode; ptr = __alloc_bootmem_node(mem_data[bestnode].pgdat, pernodesize, PERCPU_PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -