Commit 378b1e7a authored by Heiko Carstens's avatar Heiko Carstens Committed by Martin Schwidefsky

s390/mm: fix pmd_huge() usage for kernel mapping

pmd_huge() will always return 0 on !HUGETLBFS, however we use that helper
function when walking the kernel page tables to decide if we have a
1MB page frame or not.
Since we create 1MB frames for the kernel 1:1 mapping independently of
HUGETLBFS this can lead to incorrect storage accesses since the code
can assume that we have a pointer to a page table instead of a pointer
to a 1MB frame.

Fix this by adding a pmd_large() primitive like other architectures have
it already and remove all references to HUGETLBFS/HUGETLBPAGE from the
code that walks kernel page tables.
Reviewed-by: default avatarGerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 521b3d79
......@@ -507,6 +507,15 @@ static inline int pmd_none(pmd_t pmd)
return (pmd_val(pmd) & _SEGMENT_ENTRY_INV) != 0UL;
}
static inline int pmd_large(pmd_t pmd)
{
#ifdef CONFIG_64BIT
return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE);
#else
return 0;
#endif
}
static inline int pmd_bad(pmd_t pmd)
{
unsigned long mask = ~_SEGMENT_ENTRY_ORIGIN & ~_SEGMENT_ENTRY_INV;
......
......@@ -21,7 +21,7 @@ static void change_page_attr(unsigned long addr, int numpages,
pgdp = pgd_offset(&init_mm, addr);
pudp = pud_offset(pgdp, addr);
pmdp = pmd_offset(pudp, addr);
if (pmd_huge(*pmdp)) {
if (pmd_large(*pmdp)) {
WARN_ON_ONCE(1);
continue;
}
......
......@@ -79,7 +79,8 @@ static pte_t __ref *vmem_pte_alloc(unsigned long address)
*/
static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
{
unsigned long address;
unsigned long end = start + size;
unsigned long address = start;
pgd_t *pg_dir;
pud_t *pu_dir;
pmd_t *pm_dir;
......@@ -87,7 +88,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pte_t pte;
int ret = -ENOMEM;
for (address = start; address < start + size; address += PAGE_SIZE) {
while (address < end) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
pu_dir = vmem_pud_alloc();
......@@ -108,12 +109,11 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pm_dir = pmd_offset(pu_dir, address);
#if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
(address + HPAGE_SIZE <= start + size) &&
(address >= HPAGE_SIZE)) {
if (MACHINE_HAS_EDAT1 && address && !(address & ~PMD_MASK) &&
(address + PMD_SIZE <= end)) {
pte_val(pte) |= _SEGMENT_ENTRY_LARGE;
pmd_val(*pm_dir) = pte_val(pte);
address += HPAGE_SIZE - PAGE_SIZE;
address += PMD_SIZE;
continue;
}
#endif
......@@ -126,10 +126,11 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pt_dir = pte_offset_kernel(pm_dir, address);
*pt_dir = pte;
address += PAGE_SIZE;
}
ret = 0;
out:
flush_tlb_kernel_range(start, start + size);
flush_tlb_kernel_range(start, end);
return ret;
}
......@@ -139,7 +140,8 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
*/
static void vmem_remove_range(unsigned long start, unsigned long size)
{
unsigned long address;
unsigned long end = start + size;
unsigned long address = start;
pgd_t *pg_dir;
pud_t *pu_dir;
pmd_t *pm_dir;
......@@ -147,7 +149,7 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
pte_t pte;
pte_val(pte) = _PAGE_TYPE_EMPTY;
for (address = start; address < start + size; address += PAGE_SIZE) {
while (address < end) {
pg_dir = pgd_offset_k(address);
pu_dir = pud_offset(pg_dir, address);
if (pud_none(*pu_dir))
......@@ -155,17 +157,16 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir))
continue;
if (pmd_huge(*pm_dir)) {
if (pmd_large(*pm_dir)) {
pmd_clear(pm_dir);
address += HPAGE_SIZE - PAGE_SIZE;
address += PMD_SIZE;
continue;
}
pt_dir = pte_offset_kernel(pm_dir, address);
*pt_dir = pte;
address += PAGE_SIZE;
}
flush_tlb_kernel_range(start, start + size);
flush_tlb_kernel_range(start, end);
}
/*
......
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