⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 discontig.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -