Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc

* 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc:
  vmlinux.lds: fix .data..init_task output section (fix popwerpc boot)
  powerpc: Fix erroneous lmb->memblock conversions
  powerpc/mm: Add some debug output when hash insertion fails
  powerpc/mm: Fix bugs in huge page hashing
  powerpc/mm: Move around testing of _PAGE_PRESENT in hash code
  powerpc/mm: Handle hypervisor pte insert failure in __hash_page_huge
  powerpc/kexec: Fix boundary case for book-e kexec memory limits
This commit is contained in:
Linus Torvalds 2010-07-23 13:26:16 -07:00
Родитель 20a52d4f59 7ffb65f84b
Коммит 86c65a7857
9 изменённых файлов: 93 добавлений и 69 удалений

Просмотреть файл

@ -8,9 +8,9 @@
* On FSL-BookE we setup a 1:1 mapping which covers the first 2GiB of memory * On FSL-BookE we setup a 1:1 mapping which covers the first 2GiB of memory
* and therefore we can only deal with memory within this range * and therefore we can only deal with memory within this range
*/ */
#define KEXEC_SOURCE_MEMORY_LIMIT (2 * 1024 * 1024 * 1024UL) #define KEXEC_SOURCE_MEMORY_LIMIT (2 * 1024 * 1024 * 1024UL - 1)
#define KEXEC_DESTINATION_MEMORY_LIMIT (2 * 1024 * 1024 * 1024UL) #define KEXEC_DESTINATION_MEMORY_LIMIT (2 * 1024 * 1024 * 1024UL - 1)
#define KEXEC_CONTROL_MEMORY_LIMIT (2 * 1024 * 1024 * 1024UL) #define KEXEC_CONTROL_MEMORY_LIMIT (2 * 1024 * 1024 * 1024UL - 1)
#else #else

Просмотреть файл

@ -250,7 +250,9 @@ extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid, int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
pte_t *ptep, unsigned long trap, int local, int ssize, pte_t *ptep, unsigned long trap, int local, int ssize,
unsigned int shift, unsigned int mmu_psize); unsigned int shift, unsigned int mmu_psize);
extern void hash_failure_debug(unsigned long ea, unsigned long access,
unsigned long vsid, unsigned long trap,
int ssize, int psize, unsigned long pte);
extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend, extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
unsigned long pstart, unsigned long prot, unsigned long pstart, unsigned long prot,
int psize, int ssize); int psize, int ssize);

Просмотреть файл

@ -414,7 +414,7 @@ static int __init early_init_dt_scan_drconf_memory(unsigned long node)
u64 base, size, memblock_size; u64 base, size, memblock_size;
unsigned int is_kexec_kdump = 0, rngs; unsigned int is_kexec_kdump = 0, rngs;
ls = of_get_flat_dt_prop(node, "ibm,memblock-size", &l); ls = of_get_flat_dt_prop(node, "ibm,lmb-size", &l);
if (ls == NULL || l < dt_root_size_cells * sizeof(__be32)) if (ls == NULL || l < dt_root_size_cells * sizeof(__be32))
return 0; return 0;
memblock_size = dt_mem_next_cell(dt_root_size_cells, &ls); memblock_size = dt_mem_next_cell(dt_root_size_cells, &ls);

Просмотреть файл

@ -68,9 +68,6 @@ _GLOBAL(__hash_page_4K)
std r8,STK_PARM(r8)(r1) std r8,STK_PARM(r8)(r1)
std r9,STK_PARM(r9)(r1) std r9,STK_PARM(r9)(r1)
/* Add _PAGE_PRESENT to access */
ori r4,r4,_PAGE_PRESENT
/* Save non-volatile registers. /* Save non-volatile registers.
* r31 will hold "old PTE" * r31 will hold "old PTE"
* r30 is "new PTE" * r30 is "new PTE"
@ -347,9 +344,6 @@ _GLOBAL(__hash_page_4K)
std r8,STK_PARM(r8)(r1) std r8,STK_PARM(r8)(r1)
std r9,STK_PARM(r9)(r1) std r9,STK_PARM(r9)(r1)
/* Add _PAGE_PRESENT to access */
ori r4,r4,_PAGE_PRESENT
/* Save non-volatile registers. /* Save non-volatile registers.
* r31 will hold "old PTE" * r31 will hold "old PTE"
* r30 is "new PTE" * r30 is "new PTE"
@ -687,9 +681,6 @@ _GLOBAL(__hash_page_64K)
std r8,STK_PARM(r8)(r1) std r8,STK_PARM(r8)(r1)
std r9,STK_PARM(r9)(r1) std r9,STK_PARM(r9)(r1)
/* Add _PAGE_PRESENT to access */
ori r4,r4,_PAGE_PRESENT
/* Save non-volatile registers. /* Save non-volatile registers.
* r31 will hold "old PTE" * r31 will hold "old PTE"
* r30 is "new PTE" * r30 is "new PTE"

Просмотреть файл

@ -871,6 +871,18 @@ static inline int subpage_protection(struct mm_struct *mm, unsigned long ea)
} }
#endif #endif
void hash_failure_debug(unsigned long ea, unsigned long access,
unsigned long vsid, unsigned long trap,
int ssize, int psize, unsigned long pte)
{
if (!printk_ratelimit())
return;
pr_info("mm: Hashing failure ! EA=0x%lx access=0x%lx current=%s\n",
ea, access, current->comm);
pr_info(" trap=0x%lx vsid=0x%lx ssize=%d psize=%d pte=0x%lx\n",
trap, vsid, ssize, psize, pte);
}
/* Result code is: /* Result code is:
* 0 - handled * 0 - handled
* 1 - normal page fault * 1 - normal page fault
@ -955,6 +967,17 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
return 1; return 1;
} }
/* Add _PAGE_PRESENT to the required access perm */
access |= _PAGE_PRESENT;
/* Pre-check access permissions (will be re-checked atomically
* in __hash_page_XX but this pre-check is a fast path
*/
if (access & ~pte_val(*ptep)) {
DBG_LOW(" no access !\n");
return 1;
}
#ifdef CONFIG_HUGETLB_PAGE #ifdef CONFIG_HUGETLB_PAGE
if (hugeshift) if (hugeshift)
return __hash_page_huge(ea, access, vsid, ptep, trap, local, return __hash_page_huge(ea, access, vsid, ptep, trap, local,
@ -967,14 +990,6 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
DBG_LOW(" i-pte: %016lx %016lx\n", pte_val(*ptep), DBG_LOW(" i-pte: %016lx %016lx\n", pte_val(*ptep),
pte_val(*(ptep + PTRS_PER_PTE))); pte_val(*(ptep + PTRS_PER_PTE)));
#endif #endif
/* Pre-check access permissions (will be re-checked atomically
* in __hash_page_XX but this pre-check is a fast path
*/
if (access & ~pte_val(*ptep)) {
DBG_LOW(" no access !\n");
return 1;
}
/* Do actual hashing */ /* Do actual hashing */
#ifdef CONFIG_PPC_64K_PAGES #ifdef CONFIG_PPC_64K_PAGES
/* If _PAGE_4K_PFN is set, make sure this is a 4k segment */ /* If _PAGE_4K_PFN is set, make sure this is a 4k segment */
@ -1033,6 +1048,12 @@ int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
local, ssize, spp); local, ssize, spp);
} }
/* Dump some info in case of hash insertion failure, they should
* never happen so it is really useful to know if/when they do
*/
if (rc == -1)
hash_failure_debug(ea, access, vsid, trap, ssize, psize,
pte_val(*ptep));
#ifndef CONFIG_PPC_64K_PAGES #ifndef CONFIG_PPC_64K_PAGES
DBG_LOW(" o-pte: %016lx\n", pte_val(*ptep)); DBG_LOW(" o-pte: %016lx\n", pte_val(*ptep));
#else #else
@ -1051,8 +1072,7 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,
void *pgdir; void *pgdir;
pte_t *ptep; pte_t *ptep;
unsigned long flags; unsigned long flags;
int local = 0; int rc, ssize, local = 0;
int ssize;
BUG_ON(REGION_ID(ea) != USER_REGION_ID); BUG_ON(REGION_ID(ea) != USER_REGION_ID);
@ -1098,11 +1118,18 @@ void hash_preload(struct mm_struct *mm, unsigned long ea,
/* Hash it in */ /* Hash it in */
#ifdef CONFIG_PPC_HAS_HASH_64K #ifdef CONFIG_PPC_HAS_HASH_64K
if (mm->context.user_psize == MMU_PAGE_64K) if (mm->context.user_psize == MMU_PAGE_64K)
__hash_page_64K(ea, access, vsid, ptep, trap, local, ssize); rc = __hash_page_64K(ea, access, vsid, ptep, trap, local, ssize);
else else
#endif /* CONFIG_PPC_HAS_HASH_64K */ #endif /* CONFIG_PPC_HAS_HASH_64K */
__hash_page_4K(ea, access, vsid, ptep, trap, local, ssize, rc = __hash_page_4K(ea, access, vsid, ptep, trap, local, ssize,
subpage_protection(pgdir, ea)); subpage_protection(pgdir, ea));
/* Dump some info in case of hash insertion failure, they should
* never happen so it is really useful to know if/when they do
*/
if (rc == -1)
hash_failure_debug(ea, access, vsid, trap, ssize,
mm->context.user_psize, pte_val(*ptep));
local_irq_restore(flags); local_irq_restore(flags);
} }

Просмотреть файл

@ -21,21 +21,13 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
unsigned long old_pte, new_pte; unsigned long old_pte, new_pte;
unsigned long va, rflags, pa, sz; unsigned long va, rflags, pa, sz;
long slot; long slot;
int err = 1;
BUG_ON(shift != mmu_psize_defs[mmu_psize].shift); BUG_ON(shift != mmu_psize_defs[mmu_psize].shift);
/* Search the Linux page table for a match with va */ /* Search the Linux page table for a match with va */
va = hpt_va(ea, vsid, ssize); va = hpt_va(ea, vsid, ssize);
/* /* At this point, we have a pte (old_pte) which can be used to build
* Check the user's access rights to the page. If access should be
* prevented then send the problem up to do_page_fault.
*/
if (unlikely(access & ~pte_val(*ptep)))
goto out;
/*
* At this point, we have a pte (old_pte) which can be used to build
* or update an HPTE. There are 2 cases: * or update an HPTE. There are 2 cases:
* *
* 1. There is a valid (present) pte with no associated HPTE (this is * 1. There is a valid (present) pte with no associated HPTE (this is
@ -49,9 +41,17 @@ int __hash_page_huge(unsigned long ea, unsigned long access, unsigned long vsid,
do { do {
old_pte = pte_val(*ptep); old_pte = pte_val(*ptep);
if (old_pte & _PAGE_BUSY) /* If PTE busy, retry the access */
goto out; if (unlikely(old_pte & _PAGE_BUSY))
return 0;
/* If PTE permissions don't match, take page fault */
if (unlikely(access & ~old_pte))
return 1;
/* Try to lock the PTE, add ACCESSED and DIRTY if it was
* a write access */
new_pte = old_pte | _PAGE_BUSY | _PAGE_ACCESSED; new_pte = old_pte | _PAGE_BUSY | _PAGE_ACCESSED;
if (access & _PAGE_RW)
new_pte |= _PAGE_DIRTY;
} while(old_pte != __cmpxchg_u64((unsigned long *)ptep, } while(old_pte != __cmpxchg_u64((unsigned long *)ptep,
old_pte, new_pte)); old_pte, new_pte));
@ -121,8 +121,16 @@ repeat:
} }
} }
if (unlikely(slot == -2)) /*
panic("hash_huge_page: pte_insert failed\n"); * Hypervisor failure. Restore old pte and return -1
* similar to __hash_page_*
*/
if (unlikely(slot == -2)) {
*ptep = __pte(old_pte);
hash_failure_debug(ea, access, vsid, trap, ssize,
mmu_psize, old_pte);
return -1;
}
new_pte |= (slot << 12) & (_PAGE_F_SECOND | _PAGE_F_GIX); new_pte |= (slot << 12) & (_PAGE_F_SECOND | _PAGE_F_GIX);
} }
@ -131,9 +139,5 @@ repeat:
* No need to use ldarx/stdcx here * No need to use ldarx/stdcx here
*/ */
*ptep = __pte(new_pte & ~_PAGE_BUSY); *ptep = __pte(new_pte & ~_PAGE_BUSY);
return 0;
err = 0;
out:
return err;
} }

Просмотреть файл

@ -398,15 +398,15 @@ static int of_get_drconf_memory(struct device_node *memory, const u32 **dm)
} }
/* /*
* Retreive and validate the ibm,memblock-size property for drconf memory * Retreive and validate the ibm,lmb-size property for drconf memory
* from the device tree. * from the device tree.
*/ */
static u64 of_get_memblock_size(struct device_node *memory) static u64 of_get_lmb_size(struct device_node *memory)
{ {
const u32 *prop; const u32 *prop;
u32 len; u32 len;
prop = of_get_property(memory, "ibm,memblock-size", &len); prop = of_get_property(memory, "ibm,lmb-size", &len);
if (!prop || len < sizeof(unsigned int)) if (!prop || len < sizeof(unsigned int))
return 0; return 0;
@ -562,7 +562,7 @@ static unsigned long __init numa_enforce_memory_limit(unsigned long start,
static inline int __init read_usm_ranges(const u32 **usm) static inline int __init read_usm_ranges(const u32 **usm)
{ {
/* /*
* For each memblock in ibm,dynamic-memory a corresponding * For each lmb in ibm,dynamic-memory a corresponding
* entry in linux,drconf-usable-memory property contains * entry in linux,drconf-usable-memory property contains
* a counter followed by that many (base, size) duple. * a counter followed by that many (base, size) duple.
* read the counter from linux,drconf-usable-memory * read the counter from linux,drconf-usable-memory
@ -578,7 +578,7 @@ static void __init parse_drconf_memory(struct device_node *memory)
{ {
const u32 *dm, *usm; const u32 *dm, *usm;
unsigned int n, rc, ranges, is_kexec_kdump = 0; unsigned int n, rc, ranges, is_kexec_kdump = 0;
unsigned long memblock_size, base, size, sz; unsigned long lmb_size, base, size, sz;
int nid; int nid;
struct assoc_arrays aa; struct assoc_arrays aa;
@ -586,8 +586,8 @@ static void __init parse_drconf_memory(struct device_node *memory)
if (!n) if (!n)
return; return;
memblock_size = of_get_memblock_size(memory); lmb_size = of_get_lmb_size(memory);
if (!memblock_size) if (!lmb_size)
return; return;
rc = of_get_assoc_arrays(memory, &aa); rc = of_get_assoc_arrays(memory, &aa);
@ -611,7 +611,7 @@ static void __init parse_drconf_memory(struct device_node *memory)
continue; continue;
base = drmem.base_addr; base = drmem.base_addr;
size = memblock_size; size = lmb_size;
ranges = 1; ranges = 1;
if (is_kexec_kdump) { if (is_kexec_kdump) {
@ -1072,7 +1072,7 @@ static int hot_add_drconf_scn_to_nid(struct device_node *memory,
{ {
const u32 *dm; const u32 *dm;
unsigned int drconf_cell_cnt, rc; unsigned int drconf_cell_cnt, rc;
unsigned long memblock_size; unsigned long lmb_size;
struct assoc_arrays aa; struct assoc_arrays aa;
int nid = -1; int nid = -1;
@ -1080,8 +1080,8 @@ static int hot_add_drconf_scn_to_nid(struct device_node *memory,
if (!drconf_cell_cnt) if (!drconf_cell_cnt)
return -1; return -1;
memblock_size = of_get_memblock_size(memory); lmb_size = of_get_lmb_size(memory);
if (!memblock_size) if (!lmb_size)
return -1; return -1;
rc = of_get_assoc_arrays(memory, &aa); rc = of_get_assoc_arrays(memory, &aa);
@ -1100,7 +1100,7 @@ static int hot_add_drconf_scn_to_nid(struct device_node *memory,
continue; continue;
if ((scn_addr < drmem.base_addr) if ((scn_addr < drmem.base_addr)
|| (scn_addr >= (drmem.base_addr + memblock_size))) || (scn_addr >= (drmem.base_addr + lmb_size)))
continue; continue;
nid = of_drconf_to_nid_single(&drmem, &aa); nid = of_drconf_to_nid_single(&drmem, &aa);

Просмотреть файл

@ -69,7 +69,7 @@ static int pseries_remove_memory(struct device_node *np)
const char *type; const char *type;
const unsigned int *regs; const unsigned int *regs;
unsigned long base; unsigned long base;
unsigned int memblock_size; unsigned int lmb_size;
int ret = -EINVAL; int ret = -EINVAL;
/* /*
@ -87,9 +87,9 @@ static int pseries_remove_memory(struct device_node *np)
return ret; return ret;
base = *(unsigned long *)regs; base = *(unsigned long *)regs;
memblock_size = regs[3]; lmb_size = regs[3];
ret = pseries_remove_memblock(base, memblock_size); ret = pseries_remove_memblock(base, lmb_size);
return ret; return ret;
} }
@ -98,7 +98,7 @@ static int pseries_add_memory(struct device_node *np)
const char *type; const char *type;
const unsigned int *regs; const unsigned int *regs;
unsigned long base; unsigned long base;
unsigned int memblock_size; unsigned int lmb_size;
int ret = -EINVAL; int ret = -EINVAL;
/* /*
@ -116,36 +116,36 @@ static int pseries_add_memory(struct device_node *np)
return ret; return ret;
base = *(unsigned long *)regs; base = *(unsigned long *)regs;
memblock_size = regs[3]; lmb_size = regs[3];
/* /*
* Update memory region to represent the memory add * Update memory region to represent the memory add
*/ */
ret = memblock_add(base, memblock_size); ret = memblock_add(base, lmb_size);
return (ret < 0) ? -EINVAL : 0; return (ret < 0) ? -EINVAL : 0;
} }
static int pseries_drconf_memory(unsigned long *base, unsigned int action) static int pseries_drconf_memory(unsigned long *base, unsigned int action)
{ {
struct device_node *np; struct device_node *np;
const unsigned long *memblock_size; const unsigned long *lmb_size;
int rc; int rc;
np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
if (!np) if (!np)
return -EINVAL; return -EINVAL;
memblock_size = of_get_property(np, "ibm,memblock-size", NULL); lmb_size = of_get_property(np, "ibm,lmb-size", NULL);
if (!memblock_size) { if (!lmb_size) {
of_node_put(np); of_node_put(np);
return -EINVAL; return -EINVAL;
} }
if (action == PSERIES_DRCONF_MEM_ADD) { if (action == PSERIES_DRCONF_MEM_ADD) {
rc = memblock_add(*base, *memblock_size); rc = memblock_add(*base, *lmb_size);
rc = (rc < 0) ? -EINVAL : 0; rc = (rc < 0) ? -EINVAL : 0;
} else if (action == PSERIES_DRCONF_MEM_REMOVE) { } else if (action == PSERIES_DRCONF_MEM_REMOVE) {
rc = pseries_remove_memblock(*base, *memblock_size); rc = pseries_remove_memblock(*base, *lmb_size);
} else { } else {
rc = -EINVAL; rc = -EINVAL;
} }

Просмотреть файл

@ -445,7 +445,7 @@
*/ */
#define INIT_TASK_DATA_SECTION(align) \ #define INIT_TASK_DATA_SECTION(align) \
. = ALIGN(align); \ . = ALIGN(align); \
.data..init_task : { \ .data..init_task : AT(ADDR(.data..init_task) - LOAD_OFFSET) { \
INIT_TASK_DATA(align) \ INIT_TASK_DATA(align) \
} }