Commit 89ccb313 authored by David Mosberger's avatar David Mosberger

ia64: Based on patch by Jess Barnes: split up memory-initialization

	from kernel/setup.c into two separate files: mm/{dis,contig}.c
	to handle contiguous vs. discontiguous memory layouts.
parent b4f30940
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <asm/ia32.h> #include <asm/ia32.h>
#include <asm/machvec.h> #include <asm/machvec.h>
#include <asm/mca.h> #include <asm/mca.h>
#include <asm/meminit.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/patch.h> #include <asm/patch.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -83,91 +84,12 @@ unsigned long ia64_max_iommu_merge_mask = ~0UL; ...@@ -83,91 +84,12 @@ unsigned long ia64_max_iommu_merge_mask = ~0UL;
char saved_command_line[COMMAND_LINE_SIZE]; /* used in proc filesystem */ char saved_command_line[COMMAND_LINE_SIZE]; /* used in proc filesystem */
/*
* Entries defined so far:
* - boot param structure itself
* - memory map
* - initrd (optional)
* - command line string
* - kernel code & data
*
* More could be added if necessary
*/
#define IA64_MAX_RSVD_REGIONS 5
struct rsvd_region {
unsigned long start; /* virtual address of beginning of element */
unsigned long end; /* virtual address of end of element + 1 */
};
/* /*
* We use a special marker for the end of memory and it uses the extra (+1) slot * We use a special marker for the end of memory and it uses the extra (+1) slot
*/ */
static struct rsvd_region rsvd_region[IA64_MAX_RSVD_REGIONS + 1]; struct rsvd_region rsvd_region[IA64_MAX_RSVD_REGIONS + 1];
static int num_rsvd_regions; int num_rsvd_regions;
#define IGNORE_PFN0 1 /* XXX fix me: ignore pfn 0 until TLB miss handler is updated... */
#ifndef CONFIG_DISCONTIGMEM
static unsigned long bootmap_start; /* physical address where the bootmem map is located */
static int
find_max_pfn (unsigned long start, unsigned long end, void *arg)
{
unsigned long *max_pfnp = arg, pfn;
pfn = (PAGE_ALIGN(end - 1) - PAGE_OFFSET) >> PAGE_SHIFT;
if (pfn > *max_pfnp)
*max_pfnp = pfn;
return 0;
}
#else /* CONFIG_DISCONTIGMEM */
/*
* efi_memmap_walk() knows nothing about layout of memory across nodes. Find
* out to which node a block of memory belongs. Ignore memory that we cannot
* identify, and split blocks that run across multiple nodes.
*
* Take this opportunity to round the start address up and the end address
* down to page boundaries.
*/
void
call_pernode_memory (unsigned long start, unsigned long end, void *arg)
{
unsigned long rs, re;
void (*func)(unsigned long, unsigned long, int, int);
int i;
start = PAGE_ALIGN(start);
end &= PAGE_MASK;
if (start >= end)
return;
func = arg;
if (!num_memblks) {
/*
* This machine doesn't have SRAT, so call func with
* nid=0, bank=0.
*/
if (start < end)
(*func)(start, end - start, 0, 0);
return;
}
for (i = 0; i < num_memblks; i++) {
rs = max(start, node_memblk[i].start_paddr);
re = min(end, node_memblk[i].start_paddr+node_memblk[i].size);
if (rs < re)
(*func)(rs, re-rs, node_memblk[i].nid,
node_memblk[i].bank);
}
}
#endif /* CONFIG_DISCONTIGMEM */
/* /*
* Filter incoming memory segments based on the primitive map created from the boot * Filter incoming memory segments based on the primitive map created from the boot
...@@ -215,48 +137,6 @@ filter_rsvd_memory (unsigned long start, unsigned long end, void *arg) ...@@ -215,48 +137,6 @@ filter_rsvd_memory (unsigned long start, unsigned long end, void *arg)
return 0; return 0;
} }
#ifndef CONFIG_DISCONTIGMEM
/*
* Find a place to put the bootmap and return its starting address in bootmap_start.
* This address must be page-aligned.
*/
static int
find_bootmap_location (unsigned long start, unsigned long end, void *arg)
{
unsigned long needed = *(unsigned long *)arg;
unsigned long range_start, range_end, free_start;
int i;
#if IGNORE_PFN0
if (start == PAGE_OFFSET) {
start += PAGE_SIZE;
if (start >= end) return 0;
}
#endif
free_start = PAGE_OFFSET;
for (i = 0; i < num_rsvd_regions; i++) {
range_start = max(start, free_start);
range_end = min(end, rsvd_region[i].start & PAGE_MASK);
if (range_end <= range_start) continue; /* skip over empty range */
if (range_end - range_start >= needed) {
bootmap_start = __pa(range_start);
return 1; /* done */
}
/* nothing more available in this segment */
if (range_end == end) return 0;
free_start = PAGE_ALIGN(rsvd_region[i].end);
}
return 0;
}
#endif /* !CONFIG_DISCONTIGMEM */
static void static void
sort_regions (struct rsvd_region *rsvd_region, int max) sort_regions (struct rsvd_region *rsvd_region, int max)
{ {
...@@ -275,10 +155,15 @@ sort_regions (struct rsvd_region *rsvd_region, int max) ...@@ -275,10 +155,15 @@ sort_regions (struct rsvd_region *rsvd_region, int max)
} }
} }
static void /**
find_memory (void) * reserve_memory - setup reserved memory areas
*
* Setup the reserved memory areas set aside for the boot parameters, initrd,
* etc.
*/
void
reserve_memory (void)
{ {
unsigned long bootmap_size;
int n = 0; int n = 0;
/* /*
...@@ -317,36 +202,17 @@ find_memory (void) ...@@ -317,36 +202,17 @@ find_memory (void)
num_rsvd_regions = n; num_rsvd_regions = n;
sort_regions(rsvd_region, num_rsvd_regions); sort_regions(rsvd_region, num_rsvd_regions);
}
#ifdef CONFIG_DISCONTIGMEM /**
{ * find_initrd - get initrd parameters from the boot parameter structure
extern void discontig_mem_init (void); *
* Grab the initrd start and end from the boot parameter struct given us by
bootmap_size = max_pfn = 0; /* stop gcc warnings */ * the boot loader.
discontig_mem_init(); */
} void
#else /* !CONFIG_DISCONTIGMEM */ find_initrd (void)
{
/* first find highest page frame number */
max_pfn = 0;
efi_memmap_walk(find_max_pfn, &max_pfn);
/* how many bytes to cover all the pages */
bootmap_size = bootmem_bootmap_pages(max_pfn) << PAGE_SHIFT;
/* look for a location to hold the bootmap */
bootmap_start = ~0UL;
efi_memmap_walk(find_bootmap_location, &bootmap_size);
if (bootmap_start == ~0UL)
panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
bootmap_size = init_bootmem(bootmap_start >> PAGE_SHIFT, max_pfn);
/* Free all available memory, then mark bootmem-map as being in use. */
efi_memmap_walk(filter_rsvd_memory, free_bootmem);
reserve_bootmem(bootmap_start, bootmap_size);
#endif /* !CONFIG_DISCONTIGMEM */
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
if (ia64_boot_param->initrd_start) { if (ia64_boot_param->initrd_start) {
initrd_start = (unsigned long)__va(ia64_boot_param->initrd_start); initrd_start = (unsigned long)__va(ia64_boot_param->initrd_start);
......
...@@ -7,3 +7,6 @@ obj-y := init.o fault.o tlb.o extable.o ...@@ -7,3 +7,6 @@ obj-y := init.o fault.o tlb.o extable.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_NUMA) += numa.o obj-$(CONFIG_NUMA) += numa.o
obj-$(CONFIG_DISCONTIGMEM) += discontig.o obj-$(CONFIG_DISCONTIGMEM) += discontig.o
ifndef CONFIG_DISCONTIGMEM
obj-y += contig.o
endif
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1998-2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com>
* Stephane Eranian <eranian@hpl.hp.com>
* Copyright (C) 2000, Rohit Seth <rohit.seth@intel.com>
* Copyright (C) 1999 VA Linux Systems
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
* Copyright (C) 2003 Silicon Graphics, Inc. All rights reserved.
*
* Routines used by ia64 machines with contiguous (or virtually contiguous)
* memory.
*/
#include <linux/config.h>
#include <linux/bootmem.h>
#include <linux/efi.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <asm/meminit.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/sections.h>
/**
* show_mem - display a memory statistics summary
*
* Just walks the pages in the system and describes where they're allocated.
*/
void
show_mem (void)
{
int i, total = 0, reserved = 0;
int shared = 0, cached = 0;
printk("Mem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
i = max_mapnr;
while (i-- > 0) {
total++;
if (PageReserved(mem_map+i))
reserved++;
else if (PageSwapCache(mem_map+i))
cached++;
else if (page_count(mem_map + i))
shared += page_count(mem_map + i) - 1;
}
printk("%d pages of RAM\n", total);
printk("%d reserved pages\n", reserved);
printk("%d pages shared\n", shared);
printk("%d pages swap cached\n", cached);
printk("%ld pages in page table cache\n", pgtable_cache_size);
}
/* physical address where the bootmem map is located */
unsigned long bootmap_start;
/**
* find_max_pfn - adjust the maximum page number callback
* @start: start of range
* @end: end of range
* @arg: address of pointer to global max_pfn variable
*
* Passed as a callback function to efi_memmap_walk() to determine the highest
* available page frame number in the system.
*/
int
find_max_pfn (unsigned long start, unsigned long end, void *arg)
{
unsigned long *max_pfnp = arg, pfn;
pfn = (PAGE_ALIGN(end - 1) - PAGE_OFFSET) >> PAGE_SHIFT;
if (pfn > *max_pfnp)
*max_pfnp = pfn;
return 0;
}
/**
* find_bootmap_location - callback to find a memory area for the bootmap
* @start: start of region
* @end: end of region
* @arg: unused callback data
*
* Find a place to put the bootmap and return its starting address in
* bootmap_start. This address must be page-aligned.
*/
int
find_bootmap_location (unsigned long start, unsigned long end, void *arg)
{
unsigned long needed = *(unsigned long *)arg;
unsigned long range_start, range_end, free_start;
int i;
#if IGNORE_PFN0
if (start == PAGE_OFFSET) {
start += PAGE_SIZE;
if (start >= end)
return 0;
}
#endif
free_start = PAGE_OFFSET;
for (i = 0; i < num_rsvd_regions; i++) {
range_start = max(start, free_start);
range_end = min(end, rsvd_region[i].start & PAGE_MASK);
if (range_end <= range_start)
continue; /* skip over empty range */
if (range_end - range_start >= needed) {
bootmap_start = __pa(range_start);
return 1; /* done */
}
/* nothing more available in this segment */
if (range_end == end)
return 0;
free_start = PAGE_ALIGN(rsvd_region[i].end);
}
return 0;
}
/**
* find_memory - setup memory map
*
* Walk the EFI memory map and find usable memory for the system, taking
* into account reserved areas.
*/
void
find_memory (void)
{
unsigned long bootmap_size;
reserve_memory();
/* first find highest page frame number */
max_pfn = 0;
efi_memmap_walk(find_max_pfn, &max_pfn);
/* how many bytes to cover all the pages */
bootmap_size = bootmem_bootmap_pages(max_pfn) << PAGE_SHIFT;
/* look for a location to hold the bootmap */
bootmap_start = ~0UL;
efi_memmap_walk(find_bootmap_location, &bootmap_size);
if (bootmap_start == ~0UL)
panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
bootmap_size = init_bootmem(bootmap_start >> PAGE_SHIFT, max_pfn);
/* Free all available memory, then mark bootmem-map as being in use. */
efi_memmap_walk(filter_rsvd_memory, free_bootmem);
reserve_bootmem(bootmap_start, bootmap_size);
find_initrd();
}
/* /*
* Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved. * Copyright (c) 2000, 2003 Silicon Graphics, Inc. All rights reserved.
* Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Tony Luck <tony.luck@intel.com> * Copyright (c) 2001 Tony Luck <tony.luck@intel.com>
* Copyright (c) 2002 NEC Corp. * Copyright (c) 2002 NEC Corp.
...@@ -12,10 +12,13 @@ ...@@ -12,10 +12,13 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/swap.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/mmzone.h> #include <linux/mmzone.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <asm/pgalloc.h>
#include <asm/meminit.h>
/* /*
...@@ -27,9 +30,6 @@ static struct ia64_node_data *node_data[NR_NODES]; ...@@ -27,9 +30,6 @@ static struct ia64_node_data *node_data[NR_NODES];
static long boot_pg_data[8*NR_NODES+sizeof(pg_data_t)] __initdata; static long boot_pg_data[8*NR_NODES+sizeof(pg_data_t)] __initdata;
static pg_data_t *pg_data_ptr[NR_NODES] __initdata; static pg_data_t *pg_data_ptr[NR_NODES] __initdata;
static bootmem_data_t bdata[NR_NODES][NR_BANKS_PER_NODE+1] __initdata; static bootmem_data_t bdata[NR_NODES][NR_BANKS_PER_NODE+1] __initdata;
extern int filter_rsvd_memory (unsigned long start, unsigned long end, void *arg);
/* /*
* Return the compact node number of this cpu. Used prior to * Return the compact node number of this cpu. Used prior to
* setting up the cpu_data area. * setting up the cpu_data area.
...@@ -198,7 +198,7 @@ allocate_pernode_structures(void) ...@@ -198,7 +198,7 @@ allocate_pernode_structures(void)
pgdat->pgdat_next = new_pgdat_list; pgdat->pgdat_next = new_pgdat_list;
new_pgdat_list = pgdat; new_pgdat_list = pgdat;
} }
memcpy(node_data[mynode]->pg_data_ptrs, pg_data_ptr, sizeof(pg_data_ptr)); memcpy(node_data[mynode]->pg_data_ptrs, pg_data_ptr, sizeof(pg_data_ptr));
memcpy(node_data[mynode]->node_data_ptrs, node_data, sizeof(node_data)); memcpy(node_data[mynode]->node_data_ptrs, node_data, sizeof(node_data));
...@@ -209,11 +209,12 @@ allocate_pernode_structures(void) ...@@ -209,11 +209,12 @@ allocate_pernode_structures(void)
* Called early in boot to setup the boot memory allocator, and to * Called early in boot to setup the boot memory allocator, and to
* allocate the node-local pg_data & node-directory data structures.. * allocate the node-local pg_data & node-directory data structures..
*/ */
void __init void __init find_memory(void)
discontig_mem_init(void)
{ {
int node; int node;
reserve_memory();
if (numnodes == 0) { if (numnodes == 0) {
printk(KERN_ERR "node info missing!\n"); printk(KERN_ERR "node info missing!\n");
numnodes = 1; numnodes = 1;
...@@ -232,6 +233,8 @@ discontig_mem_init(void) ...@@ -232,6 +233,8 @@ discontig_mem_init(void)
efi_memmap_walk(filter_rsvd_memory, discontig_free_bootmem_node); efi_memmap_walk(filter_rsvd_memory, discontig_free_bootmem_node);
discontig_reserve_bootmem(); discontig_reserve_bootmem();
allocate_pernode_structures(); allocate_pernode_structures();
find_initrd();
} }
/* /*
...@@ -242,8 +245,8 @@ discontig_mem_init(void) ...@@ -242,8 +245,8 @@ discontig_mem_init(void)
* the per-bank mem_map entries. * the per-bank mem_map entries.
* - fix the page struct "virtual" pointers. These are bank specific * - fix the page struct "virtual" pointers. These are bank specific
* values that the paging system doesn't understand. * values that the paging system doesn't understand.
* - replicate the nodedir structure to other nodes * - replicate the nodedir structure to other nodes
*/ */
void __init void __init
discontig_paging_init(void) discontig_paging_init(void)
...@@ -305,3 +308,71 @@ discontig_paging_init(void) ...@@ -305,3 +308,71 @@ discontig_paging_init(void)
} }
} }
void show_mem(void)
{
int i, reserved = 0;
int shared = 0, cached = 0;
pg_data_t *pgdat;
printk("Mem-info:\n");
show_free_areas();
printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
for_each_pgdat(pgdat) {
printk("Node ID: %d\n", pgdat->node_id);
for(i = 0; i < pgdat->node_spanned_pages; i++) {
if (PageReserved(pgdat->node_mem_map+i))
reserved++;
else if (PageSwapCache(pgdat->node_mem_map+i))
cached++;
else if (page_count(pgdat->node_mem_map+i))
shared += page_count(pgdat->node_mem_map+i)-1;
}
printk("\t%ld pages of RAM\n", pgdat->node_present_pages);
printk("\t%d reserved pages\n", reserved);
printk("\t%d pages shared\n", shared);
printk("\t%d pages swap cached\n", cached);
}
printk("Total of %ld pages in page table cache\n", pgtable_cache_size);
printk("%d free buffer pages\n", nr_free_buffer_pages());
}
/*
* efi_memmap_walk() knows nothing about layout of memory across nodes. Find
* out to which node a block of memory belongs. Ignore memory that we cannot
* identify, and split blocks that run across multiple nodes.
*
* Take this opportunity to round the start address up and the end address
* down to page boundaries.
*/
void call_pernode_memory(unsigned long start, unsigned long end, void *arg)
{
unsigned long rs, re;
void (*func)(unsigned long, unsigned long, int, int);
int i;
start = PAGE_ALIGN(start);
end &= PAGE_MASK;
if (start >= end)
return;
func = arg;
if (!num_memblks) {
/*
* This machine doesn't have SRAT, so call func with
* nid=0, bank=0.
*/
if (start < end)
(*func)(start, end - start, 0, 0);
return;
}
for (i = 0; i < num_memblks; i++) {
rs = max(start, node_memblk[i].start_paddr);
re = min(end, node_memblk[i].start_paddr+node_memblk[i].size);
if (rs < re)
(*func)(rs, re-rs, node_memblk[i].nid,
node_memblk[i].bank);
}
}
...@@ -214,58 +214,6 @@ free_initrd_mem (unsigned long start, unsigned long end) ...@@ -214,58 +214,6 @@ free_initrd_mem (unsigned long start, unsigned long end)
} }
} }
void
show_mem(void)
{
int i, total = 0, reserved = 0;
int shared = 0, cached = 0;
printk("Mem-info:\n");
show_free_areas();
#ifdef CONFIG_DISCONTIGMEM
{
pg_data_t *pgdat;
printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
for_each_pgdat(pgdat) {
printk("Node ID: %d\n", pgdat->node_id);
for(i = 0; i < pgdat->node_spanned_pages; i++) {
if (PageReserved(pgdat->node_mem_map+i))
reserved++;
else if (PageSwapCache(pgdat->node_mem_map+i))
cached++;
else if (page_count(pgdat->node_mem_map + i))
shared += page_count(pgdat->node_mem_map + i) - 1;
}
printk("\t%d pages of RAM\n", pgdat->node_spanned_pages);
printk("\t%d reserved pages\n", reserved);
printk("\t%d pages shared\n", shared);
printk("\t%d pages swap cached\n", cached);
}
printk("Total of %ld pages in page table cache\n", pgtable_cache_size);
printk("%d free buffer pages\n", nr_free_buffer_pages());
}
#else /* !CONFIG_DISCONTIGMEM */
printk("Free swap: %6dkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
i = max_mapnr;
while (i-- > 0) {
total++;
if (PageReserved(mem_map+i))
reserved++;
else if (PageSwapCache(mem_map+i))
cached++;
else if (page_count(mem_map + i))
shared += page_count(mem_map + i) - 1;
}
printk("%d pages of RAM\n", total);
printk("%d reserved pages\n", reserved);
printk("%d pages shared\n", shared);
printk("%d pages swap cached\n", cached);
printk("%ld pages in page table cache\n", pgtable_cache_size);
#endif /* !CONFIG_DISCONTIGMEM */
}
/* /*
* This is like put_dirty_page() but installs a clean page in the kernel's page table. * This is like put_dirty_page() but installs a clean page in the kernel's page table.
*/ */
......
#ifndef meminit_h
#define meminit_h
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
/*
* Entries defined so far:
* - boot param structure itself
* - memory map
* - initrd (optional)
* - command line string
* - kernel code & data
*
* More could be added if necessary
*/
#define IA64_MAX_RSVD_REGIONS 5
struct rsvd_region {
unsigned long start; /* virtual address of beginning of element */
unsigned long end; /* virtual address of end of element + 1 */
};
extern struct rsvd_region rsvd_region[IA64_MAX_RSVD_REGIONS + 1];
extern int num_rsvd_regions;
extern void find_memory (void);
extern void reserve_memory (void);
extern void find_initrd (void);
extern int filter_rsvd_memory (unsigned long start, unsigned long end, void *arg);
#ifdef CONFIG_DISCONTIGMEM
extern void call_pernode_memory (unsigned long start, unsigned long end, void *arg);
#endif
#define IGNORE_PFN0 1 /* XXX fix me: ignore pfn 0 until TLB miss handler is updated... */
#endif /* meminit_h */
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
#ifndef _ASM_IA64_NUMA_H #ifndef _ASM_IA64_NUMA_H
#define _ASM_IA64_NUMA_H #define _ASM_IA64_NUMA_H
#include <linux/config.h>
#include <linux/cpumask.h>
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
#ifdef CONFIG_DISCONTIGMEM #ifdef CONFIG_DISCONTIGMEM
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment