2005-09-26 14:04:21 +08:00
|
|
|
/*
|
|
|
|
* PowerPC version
|
|
|
|
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
|
|
|
|
*
|
|
|
|
* Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
|
|
|
|
* and Cort Dougan (PReP) (cort@cs.nmt.edu)
|
|
|
|
* Copyright (C) 1996 Paul Mackerras
|
|
|
|
*
|
|
|
|
* Derived from "arch/i386/mm/init.c"
|
|
|
|
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
|
|
|
|
*
|
|
|
|
* Dave Engebretsen <engebret@us.ibm.com>
|
|
|
|
* Rework for PPC64 port.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
[POWERPC] vmemmap fixes to use smaller pages
This changes vmemmap to use a different region (region 0xf) of the
address space, and to configure the page size of that region
dynamically at boot.
The problem with the current approach of always using 16M pages is that
it's not well suited to machines that have small amounts of memory such
as small partitions on pseries, or PS3's.
In fact, on the PS3, failure to allocate the 16M page backing vmmemmap
tends to prevent hotplugging the HV's "additional" memory, thus limiting
the available memory even more, from my experience down to something
like 80M total, which makes it really not very useable.
The logic used by my match to choose the vmemmap page size is:
- If 16M pages are available and there's 1G or more RAM at boot,
use that size.
- Else if 64K pages are available, use that
- Else use 4K pages
I've tested on a POWER6 (16M pages) and on an iSeries POWER3 (4K pages)
and it seems to work fine.
Note that I intend to change the way we organize the kernel regions &
SLBs so the actual region will change from 0xf back to something else at
one point, as I simplify the SLB miss handler, but that will be for a
later patch.
Signed-off-by: Paul Mackerras <paulus@samba.org>
2008-04-30 13:41:48 +08:00
|
|
|
#undef DEBUG
|
|
|
|
|
2005-09-26 14:04:21 +08:00
|
|
|
#include <linux/signal.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/mman.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/swap.h>
|
|
|
|
#include <linux/stddef.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/highmem.h>
|
|
|
|
#include <linux/idr.h>
|
|
|
|
#include <linux/nodemask.h>
|
|
|
|
#include <linux/module.h>
|
2006-06-27 17:53:52 +08:00
|
|
|
#include <linux/poison.h>
|
2010-07-12 12:36:09 +08:00
|
|
|
#include <linux/memblock.h>
|
powerpc/mm: Allow more flexible layouts for hugepage pagetables
Currently each available hugepage size uses a slightly different
pagetable layout: that is, the bottem level table of pointers to
hugepages is a different size, and may branch off from the normal page
tables at a different level. Every hugepage aware path that needs to
walk the pagetables must therefore look up the hugepage size from the
slice info first, and work out the correct way to walk the pagetables
accordingly. Future hardware is likely to add more possible hugepage
sizes, more layout options and more mess.
This patch, therefore reworks the handling of hugepage pagetables to
reduce this complexity. In the new scheme, instead of having to
consult the slice mask, pagetable walking code can check a flag in the
PGD/PUD/PMD entries to see where to branch off to hugepage pagetables,
and the entry also contains the information (eseentially hugepage
shift) necessary to then interpret that table without recourse to the
slice mask. This scheme can be extended neatly to handle multiple
levels of self-describing "special" hugepage pagetables, although for
now we assume only one level exists.
This approach means that only the pagetable allocation path needs to
know how the pagetables should be set out. All other (hugepage)
pagetable walking paths can just interpret the structure as they go.
There already was a flag bit in PGD/PUD/PMD entries for hugepage
directory pointers, but it was only used for debug. We alter that
flag bit to instead be a 0 in the MSB to indicate a hugepage pagetable
pointer (normally it would be 1 since the pointer lies in the linear
mapping). This means that asm pagetable walking can test for (and
punt on) hugepage pointers with the same test that checks for
unpopulated page directory entries (beq becomes bge), since hugepage
pointers will always be positive, and normal pointers always negative.
While we're at it, we get rid of the confusing (and grep defeating)
#defining of hugepte_shift to be the same thing as mmu_huge_psizes.
Signed-off-by: David Gibson <dwg@au1.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2009-10-27 03:24:31 +08:00
|
|
|
#include <linux/hugetlb.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-09-26 14:04:21 +08:00
|
|
|
|
|
|
|
#include <asm/pgalloc.h>
|
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/prom.h>
|
|
|
|
#include <asm/rtas.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/mmu_context.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/mmu.h>
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2005-09-26 14:04:21 +08:00
|
|
|
#include <asm/smp.h>
|
|
|
|
#include <asm/machdep.h>
|
|
|
|
#include <asm/tlb.h>
|
|
|
|
#include <asm/eeh.h>
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/mmzone.h>
|
|
|
|
#include <asm/cputable.h>
|
|
|
|
#include <asm/sections.h>
|
|
|
|
#include <asm/iommu.h>
|
|
|
|
#include <asm/vdso.h>
|
2005-11-16 12:43:48 +08:00
|
|
|
|
|
|
|
#include "mmu_decl.h"
|
2005-09-26 14:04:21 +08:00
|
|
|
|
2009-06-03 05:17:45 +08:00
|
|
|
#ifdef CONFIG_PPC_STD_MMU_64
|
2016-04-29 21:25:49 +08:00
|
|
|
#if H_PGTABLE_RANGE > USER_VSID_RANGE
|
2005-09-26 14:04:21 +08:00
|
|
|
#warning Limited user VSID range means pagetable space is wasted
|
|
|
|
#endif
|
|
|
|
|
2016-04-29 21:25:49 +08:00
|
|
|
#if (TASK_SIZE_USER64 < H_PGTABLE_RANGE) && (TASK_SIZE_USER64 < USER_VSID_RANGE)
|
2005-09-26 14:04:21 +08:00
|
|
|
#warning TASK_SIZE is smaller than it needs to be.
|
|
|
|
#endif
|
2009-06-03 05:17:45 +08:00
|
|
|
#endif /* CONFIG_PPC_STD_MMU_64 */
|
2005-09-26 14:04:21 +08:00
|
|
|
|
2008-04-22 02:22:34 +08:00
|
|
|
phys_addr_t memstart_addr = ~0;
|
2010-08-20 02:08:09 +08:00
|
|
|
EXPORT_SYMBOL_GPL(memstart_addr);
|
2008-04-22 02:22:34 +08:00
|
|
|
phys_addr_t kernstart_addr;
|
2010-08-20 02:08:09 +08:00
|
|
|
EXPORT_SYMBOL_GPL(kernstart_addr);
|
2008-04-16 03:52:22 +08:00
|
|
|
|
2007-10-16 16:24:17 +08:00
|
|
|
#ifdef CONFIG_SPARSEMEM_VMEMMAP
|
|
|
|
/*
|
|
|
|
* Given an address within the vmemmap, determine the pfn of the page that
|
|
|
|
* represents the start of the section it is within. Note that we have to
|
|
|
|
* do this by hand as the proffered address may not be correctly aligned.
|
|
|
|
* Subtraction of non-aligned pointers produces undefined results.
|
|
|
|
*/
|
2008-05-08 12:27:07 +08:00
|
|
|
static unsigned long __meminit vmemmap_section_start(unsigned long page)
|
2007-10-16 16:24:17 +08:00
|
|
|
{
|
|
|
|
unsigned long offset = page - ((unsigned long)(vmemmap));
|
|
|
|
|
|
|
|
/* Return the pfn of the start of the section. */
|
|
|
|
return (offset / sizeof(struct page)) & PAGE_SECTION_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if this vmemmap page is already initialised. If any section
|
|
|
|
* which overlaps this vmemmap page is initialised then this page is
|
|
|
|
* initialised already.
|
|
|
|
*/
|
2008-05-08 12:27:07 +08:00
|
|
|
static int __meminit vmemmap_populated(unsigned long start, int page_size)
|
2007-10-16 16:24:17 +08:00
|
|
|
{
|
|
|
|
unsigned long end = start + page_size;
|
powerpc: start loop at section start of start in vmemmap_populated()
vmemmap_populated() checks whether the [start, start + page_size) has valid
pfn numbers, to know whether a vmemmap mapping has been created that includes
this range.
Some range before end might not be checked by this loop:
sec11start......start11..sec11end/sec12start..end....start12..sec12end
as the above, for start11(section 11), it checks [sec11start, sec11end), and
loop ends as the next start(start12) is bigger than end. However,
[sec11end/sec12start, end) is not checked here.
So before the loop, adjust the start to be the start of the section, so we don't miss ranges like the above.
After we adjust start to be the start of the section, it also means it's
aligned with vmemmap as of the sizeof struct page, so we could use
page_to_pfn directly in the loop.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:39 +08:00
|
|
|
start = (unsigned long)(pfn_to_page(vmemmap_section_start(start)));
|
2007-10-16 16:24:17 +08:00
|
|
|
|
|
|
|
for (; start < end; start += (PAGES_PER_SECTION * sizeof(struct page)))
|
powerpc: start loop at section start of start in vmemmap_populated()
vmemmap_populated() checks whether the [start, start + page_size) has valid
pfn numbers, to know whether a vmemmap mapping has been created that includes
this range.
Some range before end might not be checked by this loop:
sec11start......start11..sec11end/sec12start..end....start12..sec12end
as the above, for start11(section 11), it checks [sec11start, sec11end), and
loop ends as the next start(start12) is bigger than end. However,
[sec11end/sec12start, end) is not checked here.
So before the loop, adjust the start to be the start of the section, so we don't miss ranges like the above.
After we adjust start to be the start of the section, it also means it's
aligned with vmemmap as of the sizeof struct page, so we could use
page_to_pfn directly in the loop.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:39 +08:00
|
|
|
if (pfn_valid(page_to_pfn((struct page *)start)))
|
2007-10-16 16:24:17 +08:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-22 00:21:03 +08:00
|
|
|
struct vmemmap_backing *vmemmap_list;
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
static struct vmemmap_backing *next;
|
|
|
|
static int num_left;
|
|
|
|
static int num_freed;
|
2010-04-22 00:21:03 +08:00
|
|
|
|
|
|
|
static __meminit struct vmemmap_backing * vmemmap_list_alloc(int node)
|
|
|
|
{
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
struct vmemmap_backing *vmem_back;
|
|
|
|
/* get from freed entries first */
|
|
|
|
if (num_freed) {
|
|
|
|
num_freed--;
|
|
|
|
vmem_back = next;
|
|
|
|
next = next->list;
|
|
|
|
|
|
|
|
return vmem_back;
|
|
|
|
}
|
2010-04-22 00:21:03 +08:00
|
|
|
|
|
|
|
/* allocate a page when required and hand out chunks */
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
if (!num_left) {
|
2010-04-22 00:21:03 +08:00
|
|
|
next = vmemmap_alloc_block(PAGE_SIZE, node);
|
|
|
|
if (unlikely(!next)) {
|
|
|
|
WARN_ON(1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
num_left = PAGE_SIZE / sizeof(struct vmemmap_backing);
|
|
|
|
}
|
|
|
|
|
|
|
|
num_left--;
|
|
|
|
|
|
|
|
return next++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __meminit void vmemmap_list_populate(unsigned long phys,
|
|
|
|
unsigned long start,
|
|
|
|
int node)
|
|
|
|
{
|
|
|
|
struct vmemmap_backing *vmem_back;
|
|
|
|
|
|
|
|
vmem_back = vmemmap_list_alloc(node);
|
|
|
|
if (unlikely(!vmem_back)) {
|
|
|
|
WARN_ON(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vmem_back->phys = phys;
|
|
|
|
vmem_back->virt_addr = start;
|
|
|
|
vmem_back->list = vmemmap_list;
|
|
|
|
|
|
|
|
vmemmap_list = vmem_back;
|
|
|
|
}
|
|
|
|
|
2014-06-11 16:23:38 +08:00
|
|
|
int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
|
|
|
|
{
|
|
|
|
unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
|
|
|
|
|
|
|
|
/* Align to the page size of the linear mapping. */
|
|
|
|
start = _ALIGN_DOWN(start, page_size);
|
|
|
|
|
|
|
|
pr_debug("vmemmap_populate %lx..%lx, node %d\n", start, end, node);
|
|
|
|
|
|
|
|
for (; start < end; start += page_size) {
|
|
|
|
void *p;
|
2016-02-09 11:32:42 +08:00
|
|
|
int rc;
|
2014-06-11 16:23:38 +08:00
|
|
|
|
|
|
|
if (vmemmap_populated(start, page_size))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = vmemmap_alloc_block(page_size, node);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
vmemmap_list_populate(__pa(p), start, node);
|
|
|
|
|
|
|
|
pr_debug(" * %016lx..%016lx allocated at %p\n",
|
|
|
|
start, start + page_size, p);
|
|
|
|
|
2016-02-09 11:32:42 +08:00
|
|
|
rc = vmemmap_create_mapping(start, page_size, __pa(p));
|
|
|
|
if (rc < 0) {
|
|
|
|
pr_warning(
|
|
|
|
"vmemmap_populate: Unable to create vmemmap mapping: %d\n",
|
|
|
|
rc);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
2014-06-11 16:23:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_MEMORY_HOTPLUG
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
static unsigned long vmemmap_list_free(unsigned long start)
|
|
|
|
{
|
|
|
|
struct vmemmap_backing *vmem_back, *vmem_back_prev;
|
|
|
|
|
|
|
|
vmem_back_prev = vmem_back = vmemmap_list;
|
|
|
|
|
|
|
|
/* look for it with prev pointer recorded */
|
|
|
|
for (; vmem_back; vmem_back = vmem_back->list) {
|
|
|
|
if (vmem_back->virt_addr == start)
|
|
|
|
break;
|
|
|
|
vmem_back_prev = vmem_back;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlikely(!vmem_back)) {
|
|
|
|
WARN_ON(1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove it from vmemmap_list */
|
|
|
|
if (vmem_back == vmemmap_list) /* remove head */
|
|
|
|
vmemmap_list = vmem_back->list;
|
|
|
|
else
|
|
|
|
vmem_back_prev->list = vmem_back->list;
|
|
|
|
|
|
|
|
/* next point to this freed entry */
|
|
|
|
vmem_back->list = next;
|
|
|
|
next = vmem_back;
|
|
|
|
num_freed++;
|
|
|
|
|
|
|
|
return vmem_back->phys;
|
|
|
|
}
|
|
|
|
|
2014-06-11 16:23:38 +08:00
|
|
|
void __ref vmemmap_free(unsigned long start, unsigned long end)
|
2007-10-16 16:24:17 +08:00
|
|
|
{
|
[POWERPC] vmemmap fixes to use smaller pages
This changes vmemmap to use a different region (region 0xf) of the
address space, and to configure the page size of that region
dynamically at boot.
The problem with the current approach of always using 16M pages is that
it's not well suited to machines that have small amounts of memory such
as small partitions on pseries, or PS3's.
In fact, on the PS3, failure to allocate the 16M page backing vmmemmap
tends to prevent hotplugging the HV's "additional" memory, thus limiting
the available memory even more, from my experience down to something
like 80M total, which makes it really not very useable.
The logic used by my match to choose the vmemmap page size is:
- If 16M pages are available and there's 1G or more RAM at boot,
use that size.
- Else if 64K pages are available, use that
- Else use 4K pages
I've tested on a POWER6 (16M pages) and on an iSeries POWER3 (4K pages)
and it seems to work fine.
Note that I intend to change the way we organize the kernel regions &
SLBs so the actual region will change from 0xf back to something else at
one point, as I simplify the SLB miss handler, but that will be for a
later patch.
Signed-off-by: Paul Mackerras <paulus@samba.org>
2008-04-30 13:41:48 +08:00
|
|
|
unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
|
2007-10-16 16:24:17 +08:00
|
|
|
|
|
|
|
start = _ALIGN_DOWN(start, page_size);
|
|
|
|
|
2014-06-11 16:23:38 +08:00
|
|
|
pr_debug("vmemmap_free %lx...%lx\n", start, end);
|
2009-07-24 07:15:58 +08:00
|
|
|
|
2007-10-16 16:24:17 +08:00
|
|
|
for (; start < end; start += page_size) {
|
2014-06-11 16:23:38 +08:00
|
|
|
unsigned long addr;
|
2007-10-16 16:24:17 +08:00
|
|
|
|
2014-06-11 16:23:38 +08:00
|
|
|
/*
|
|
|
|
* the section has already be marked as invalid, so
|
|
|
|
* vmemmap_populated() true means some other sections still
|
|
|
|
* in this page, so skip it.
|
|
|
|
*/
|
2007-10-16 16:24:17 +08:00
|
|
|
if (vmemmap_populated(start, page_size))
|
|
|
|
continue;
|
|
|
|
|
2014-06-11 16:23:38 +08:00
|
|
|
addr = vmemmap_list_free(start);
|
|
|
|
if (addr) {
|
|
|
|
struct page *page = pfn_to_page(addr >> PAGE_SHIFT);
|
|
|
|
|
|
|
|
if (PageReserved(page)) {
|
|
|
|
/* allocated from bootmem */
|
|
|
|
if (page_size < PAGE_SIZE) {
|
|
|
|
/*
|
|
|
|
* this shouldn't happen, but if it is
|
|
|
|
* the case, leave the memory there
|
|
|
|
*/
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
} else {
|
|
|
|
unsigned int nr_pages =
|
|
|
|
1 << get_order(page_size);
|
|
|
|
while (nr_pages--)
|
|
|
|
free_reserved_page(page++);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
free_pages((unsigned long)(__va(addr)),
|
|
|
|
get_order(page_size));
|
|
|
|
|
|
|
|
vmemmap_remove_mapping(start, page_size);
|
|
|
|
}
|
2007-10-16 16:24:17 +08:00
|
|
|
}
|
2013-02-23 08:33:08 +08:00
|
|
|
}
|
2014-06-11 16:23:38 +08:00
|
|
|
#endif
|
2013-09-27 23:18:09 +08:00
|
|
|
void register_page_bootmem_memmap(unsigned long section_nr,
|
|
|
|
struct page *start_page, unsigned long size)
|
|
|
|
{
|
|
|
|
}
|
2010-07-07 06:39:02 +08:00
|
|
|
|
2013-08-28 16:37:42 +08:00
|
|
|
/*
|
|
|
|
* We do not have access to the sparsemem vmemmap, so we fallback to
|
|
|
|
* walking the list of sparsemem blocks which we already maintain for
|
|
|
|
* the sake of crashdump. In the long run, we might want to maintain
|
|
|
|
* a tree if performance of that linear walk becomes a problem.
|
|
|
|
*
|
|
|
|
* realmode_pfn_to_page functions can fail due to:
|
|
|
|
* 1) As real sparsemem blocks do not lay in RAM continously (they
|
|
|
|
* are in virtual address space which is not available in the real mode),
|
|
|
|
* the requested page struct can be split between blocks so get_page/put_page
|
|
|
|
* may fail.
|
|
|
|
* 2) When huge pages are used, the get_page/put_page API will fail
|
|
|
|
* in real mode as the linked addresses in the page struct are virtual
|
|
|
|
* too.
|
|
|
|
*/
|
|
|
|
struct page *realmode_pfn_to_page(unsigned long pfn)
|
|
|
|
{
|
|
|
|
struct vmemmap_backing *vmem_back;
|
|
|
|
struct page *page;
|
|
|
|
unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
|
|
|
|
unsigned long pg_va = (unsigned long) pfn_to_page(pfn);
|
|
|
|
|
|
|
|
for (vmem_back = vmemmap_list; vmem_back; vmem_back = vmem_back->list) {
|
|
|
|
if (pg_va < vmem_back->virt_addr)
|
|
|
|
continue;
|
|
|
|
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
/* After vmemmap_list entry free is possible, need check all */
|
|
|
|
if ((pg_va + sizeof(struct page)) <=
|
|
|
|
(vmem_back->virt_addr + page_size)) {
|
|
|
|
page = (struct page *) (vmem_back->phys + pg_va -
|
2013-08-28 16:37:42 +08:00
|
|
|
vmem_back->virt_addr);
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
return page;
|
|
|
|
}
|
2013-08-28 16:37:42 +08:00
|
|
|
}
|
|
|
|
|
powerpc: implement vmemmap_list_free()
This patch implements vmemmap_list_free() for vmemmap_free().
The freed entries will be removed from vmemmap_list, and form a freed list,
with next as the header. The next position in the last allocated page is kept
at the list tail.
When allocation, if there are freed entries left, get it from the freed list;
if no freed entries left, get it like before from the last allocated pages.
With this change, realmode_pfn_to_page() also needs to be changed to walk
all the entries in the vmemmap_list, as the virt_addr of the entries might not
be stored in order anymore.
It helps to reuse the memory when continuous doing memory hot-plug/remove
operations, but didn't reclaim the pages already allocated, so the memory usage
will only increase, but won't exceed the value for the largest memory
configuration.
Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2014-06-11 16:23:36 +08:00
|
|
|
/* Probably that page struct is split between real pages */
|
2013-08-28 16:37:42 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
|
|
|
|
|
|
|
|
#elif defined(CONFIG_FLATMEM)
|
|
|
|
|
|
|
|
struct page *realmode_pfn_to_page(unsigned long pfn)
|
|
|
|
{
|
|
|
|
struct page *page = pfn_to_page(pfn);
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
|
|
|
|
|
|
|
|
#endif /* CONFIG_SPARSEMEM_VMEMMAP/CONFIG_FLATMEM */
|
2016-07-26 18:09:30 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_PPC_STD_MMU_64
|
2016-07-26 19:29:30 +08:00
|
|
|
static bool disable_radix;
|
|
|
|
static int __init parse_disable_radix(char *p)
|
|
|
|
{
|
|
|
|
disable_radix = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
early_param("disable_radix", parse_disable_radix);
|
|
|
|
|
2016-07-26 18:09:30 +08:00
|
|
|
void __init mmu_early_init_devtree(void)
|
|
|
|
{
|
2016-07-26 19:29:30 +08:00
|
|
|
/* Disable radix mode based on kernel command line. */
|
|
|
|
if (disable_radix)
|
2016-07-27 11:19:01 +08:00
|
|
|
cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX;
|
2016-07-26 19:31:59 +08:00
|
|
|
|
2016-07-23 17:12:35 +08:00
|
|
|
if (early_radix_enabled())
|
2016-07-26 19:55:27 +08:00
|
|
|
radix__early_init_devtree();
|
|
|
|
else
|
2016-07-26 19:31:59 +08:00
|
|
|
hash__early_init_devtree();
|
2016-07-26 18:09:30 +08:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_PPC_STD_MMU_64 */
|