Commit 9ec8fa8d authored by David Hildenbrand's avatar David Hildenbrand Committed by Heiko Carstens

s390/vmemmap: extend modify_pagetable() to handle vmemmap

Extend our shiny new modify_pagetable() to handle !direct (vmemmap)
mappings. Convert vmemmap_populate() and implement vmemmap_free().

Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: default avatarDavid Hildenbrand <david@redhat.com>
Message-Id: <20200722094558.9828-4-david@redhat.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 3e0d3e40
...@@ -29,6 +29,15 @@ static void __ref *vmem_alloc_pages(unsigned int order) ...@@ -29,6 +29,15 @@ static void __ref *vmem_alloc_pages(unsigned int order)
return (void *) memblock_phys_alloc(size, size); return (void *) memblock_phys_alloc(size, size);
} }
static void vmem_free_pages(unsigned long addr, int order)
{
/* We don't expect boot memory to be removed ever. */
if (!slab_is_available() ||
WARN_ON_ONCE(PageReserved(phys_to_page(addr))))
return;
free_pages(addr, order);
}
void *vmem_crst_alloc(unsigned long val) void *vmem_crst_alloc(unsigned long val)
{ {
unsigned long *table; unsigned long *table;
...@@ -54,10 +63,12 @@ pte_t __ref *vmem_pte_alloc(void) ...@@ -54,10 +63,12 @@ pte_t __ref *vmem_pte_alloc(void)
return pte; return pte;
} }
static void modify_pte_table(pmd_t *pmd, unsigned long addr, unsigned long end, /* __ref: we'll only call vmemmap_alloc_block() via vmemmap_populate() */
bool add) static int __ref modify_pte_table(pmd_t *pmd, unsigned long addr,
unsigned long end, bool add, bool direct)
{ {
unsigned long prot, pages = 0; unsigned long prot, pages = 0;
int ret = -ENOMEM;
pte_t *pte; pte_t *pte;
prot = pgprot_val(PAGE_KERNEL); prot = pgprot_val(PAGE_KERNEL);
...@@ -69,20 +80,34 @@ static void modify_pte_table(pmd_t *pmd, unsigned long addr, unsigned long end, ...@@ -69,20 +80,34 @@ static void modify_pte_table(pmd_t *pmd, unsigned long addr, unsigned long end,
if (!add) { if (!add) {
if (pte_none(*pte)) if (pte_none(*pte))
continue; continue;
if (!direct)
vmem_free_pages(pfn_to_phys(pte_pfn(*pte)), 0);
pte_clear(&init_mm, addr, pte); pte_clear(&init_mm, addr, pte);
} else if (pte_none(*pte)) { } else if (pte_none(*pte)) {
pte_val(*pte) = addr | prot; if (!direct) {
void *new_page = vmemmap_alloc_block(PAGE_SIZE,
NUMA_NO_NODE);
if (!new_page)
goto out;
pte_val(*pte) = __pa(new_page) | prot;
} else
pte_val(*pte) = addr | prot;
} else } else
continue; continue;
pages++; pages++;
} }
ret = 0;
update_page_count(PG_DIRECT_MAP_4K, add ? pages : -pages); out:
if (direct)
update_page_count(PG_DIRECT_MAP_4K, add ? pages : -pages);
return ret;
} }
static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end, /* __ref: we'll only call vmemmap_alloc_block() via vmemmap_populate() */
bool add) static int __ref modify_pmd_table(pud_t *pud, unsigned long addr,
unsigned long end, bool add, bool direct)
{ {
unsigned long next, prot, pages = 0; unsigned long next, prot, pages = 0;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -103,6 +128,9 @@ static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end, ...@@ -103,6 +128,9 @@ static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end,
if (pmd_large(*pmd) && !add) { if (pmd_large(*pmd) && !add) {
if (IS_ALIGNED(addr, PMD_SIZE) && if (IS_ALIGNED(addr, PMD_SIZE) &&
IS_ALIGNED(next, PMD_SIZE)) { IS_ALIGNED(next, PMD_SIZE)) {
if (!direct)
vmem_free_pages(pmd_deref(*pmd),
get_order(PMD_SIZE));
pmd_clear(pmd); pmd_clear(pmd);
pages++; pages++;
} }
...@@ -111,11 +139,27 @@ static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end, ...@@ -111,11 +139,27 @@ static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end,
} else if (pmd_none(*pmd)) { } else if (pmd_none(*pmd)) {
if (IS_ALIGNED(addr, PMD_SIZE) && if (IS_ALIGNED(addr, PMD_SIZE) &&
IS_ALIGNED(next, PMD_SIZE) && IS_ALIGNED(next, PMD_SIZE) &&
MACHINE_HAS_EDAT1 && addr && MACHINE_HAS_EDAT1 && addr && direct &&
!debug_pagealloc_enabled()) { !debug_pagealloc_enabled()) {
pmd_val(*pmd) = addr | prot; pmd_val(*pmd) = addr | prot;
pages++; pages++;
continue; continue;
} else if (!direct && MACHINE_HAS_EDAT1) {
void *new_page;
/*
* Use 1MB frames for vmemmap if available. We
* always use large frames even if they are only
* partially used. Otherwise we would have also
* page tables since vmemmap_populate gets
* called for each section separately.
*/
new_page = vmemmap_alloc_block(PMD_SIZE,
NUMA_NO_NODE);
if (!new_page)
goto out;
pmd_val(*pmd) = __pa(new_page) | prot;
continue;
} }
pte = vmem_pte_alloc(); pte = vmem_pte_alloc();
if (!pte) if (!pte)
...@@ -124,16 +168,19 @@ static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end, ...@@ -124,16 +168,19 @@ static int modify_pmd_table(pud_t *pud, unsigned long addr, unsigned long end,
} else if (pmd_large(*pmd)) } else if (pmd_large(*pmd))
continue; continue;
modify_pte_table(pmd, addr, next, add); ret = modify_pte_table(pmd, addr, next, add, direct);
if (ret)
goto out;
} }
ret = 0; ret = 0;
out: out:
update_page_count(PG_DIRECT_MAP_1M, add ? pages : -pages); if (direct)
update_page_count(PG_DIRECT_MAP_1M, add ? pages : -pages);
return ret; return ret;
} }
static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end,
bool add) bool add, bool direct)
{ {
unsigned long next, prot, pages = 0; unsigned long next, prot, pages = 0;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -162,7 +209,7 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, ...@@ -162,7 +209,7 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end,
} else if (pud_none(*pud)) { } else if (pud_none(*pud)) {
if (IS_ALIGNED(addr, PUD_SIZE) && if (IS_ALIGNED(addr, PUD_SIZE) &&
IS_ALIGNED(next, PUD_SIZE) && IS_ALIGNED(next, PUD_SIZE) &&
MACHINE_HAS_EDAT2 && addr && MACHINE_HAS_EDAT2 && addr && direct &&
!debug_pagealloc_enabled()) { !debug_pagealloc_enabled()) {
pud_val(*pud) = addr | prot; pud_val(*pud) = addr | prot;
pages++; pages++;
...@@ -175,18 +222,19 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, ...@@ -175,18 +222,19 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end,
} else if (pud_large(*pud)) } else if (pud_large(*pud))
continue; continue;
ret = modify_pmd_table(pud, addr, next, add); ret = modify_pmd_table(pud, addr, next, add, direct);
if (ret) if (ret)
goto out; goto out;
} }
ret = 0; ret = 0;
out: out:
update_page_count(PG_DIRECT_MAP_2G, add ? pages : -pages); if (direct)
update_page_count(PG_DIRECT_MAP_2G, add ? pages : -pages);
return ret; return ret;
} }
static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end, static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end,
bool add) bool add, bool direct)
{ {
unsigned long next; unsigned long next;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -206,7 +254,7 @@ static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end, ...@@ -206,7 +254,7 @@ static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end,
goto out; goto out;
} }
ret = modify_pud_table(p4d, addr, next, add); ret = modify_pud_table(p4d, addr, next, add, direct);
if (ret) if (ret)
goto out; goto out;
} }
...@@ -215,7 +263,8 @@ static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end, ...@@ -215,7 +263,8 @@ static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end,
return ret; return ret;
} }
static int modify_pagetable(unsigned long start, unsigned long end, bool add) static int modify_pagetable(unsigned long start, unsigned long end, bool add,
bool direct)
{ {
unsigned long addr, next; unsigned long addr, next;
int ret = -ENOMEM; int ret = -ENOMEM;
...@@ -239,7 +288,7 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add) ...@@ -239,7 +288,7 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add)
pgd_populate(&init_mm, pgd, p4d); pgd_populate(&init_mm, pgd, p4d);
} }
ret = modify_p4d_table(pgd, addr, next, add); ret = modify_p4d_table(pgd, addr, next, add, direct);
if (ret) if (ret)
goto out; goto out;
} }
...@@ -250,14 +299,14 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add) ...@@ -250,14 +299,14 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add)
return ret; return ret;
} }
static int add_pagetable(unsigned long start, unsigned long end) static int add_pagetable(unsigned long start, unsigned long end, bool direct)
{ {
return modify_pagetable(start, end, true); return modify_pagetable(start, end, true, direct);
} }
static int remove_pagetable(unsigned long start, unsigned long end) static int remove_pagetable(unsigned long start, unsigned long end, bool direct)
{ {
return modify_pagetable(start, end, false); return modify_pagetable(start, end, false, direct);
} }
/* /*
...@@ -265,7 +314,7 @@ static int remove_pagetable(unsigned long start, unsigned long end) ...@@ -265,7 +314,7 @@ static int remove_pagetable(unsigned long start, unsigned long end)
*/ */
static int vmem_add_range(unsigned long start, unsigned long size) static int vmem_add_range(unsigned long start, unsigned long size)
{ {
return add_pagetable(start, start + size); return add_pagetable(start, start + size, true);
} }
/* /*
...@@ -274,7 +323,7 @@ static int vmem_add_range(unsigned long start, unsigned long size) ...@@ -274,7 +323,7 @@ static int vmem_add_range(unsigned long start, unsigned long size)
*/ */
static void vmem_remove_range(unsigned long start, unsigned long size) static void vmem_remove_range(unsigned long start, unsigned long size)
{ {
remove_pagetable(start, start + size); remove_pagetable(start, start + size, true);
} }
/* /*
...@@ -283,92 +332,14 @@ static void vmem_remove_range(unsigned long start, unsigned long size) ...@@ -283,92 +332,14 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
struct vmem_altmap *altmap) struct vmem_altmap *altmap)
{ {
unsigned long pgt_prot, sgt_prot; /* We don't care about the node, just use NUMA_NO_NODE on allocations */
unsigned long address = start; return add_pagetable(start, end, false);
pgd_t *pg_dir;
p4d_t *p4_dir;
pud_t *pu_dir;
pmd_t *pm_dir;
pte_t *pt_dir;
int ret = -ENOMEM;
pgt_prot = pgprot_val(PAGE_KERNEL);
sgt_prot = pgprot_val(SEGMENT_KERNEL);
if (!MACHINE_HAS_NX) {
pgt_prot &= ~_PAGE_NOEXEC;
sgt_prot &= ~_SEGMENT_ENTRY_NOEXEC;
}
for (address = start; address < end;) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
p4_dir = vmem_crst_alloc(_REGION2_ENTRY_EMPTY);
if (!p4_dir)
goto out;
pgd_populate(&init_mm, pg_dir, p4_dir);
}
p4_dir = p4d_offset(pg_dir, address);
if (p4d_none(*p4_dir)) {
pu_dir = vmem_crst_alloc(_REGION3_ENTRY_EMPTY);
if (!pu_dir)
goto out;
p4d_populate(&init_mm, p4_dir, pu_dir);
}
pu_dir = pud_offset(p4_dir, address);
if (pud_none(*pu_dir)) {
pm_dir = vmem_crst_alloc(_SEGMENT_ENTRY_EMPTY);
if (!pm_dir)
goto out;
pud_populate(&init_mm, pu_dir, pm_dir);
}
pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir)) {
/* Use 1MB frames for vmemmap if available. We always
* use large frames even if they are only partially
* used.
* Otherwise we would have also page tables since
* vmemmap_populate gets called for each section
* separately. */
if (MACHINE_HAS_EDAT1) {
void *new_page;
new_page = vmemmap_alloc_block(PMD_SIZE, node);
if (!new_page)
goto out;
pmd_val(*pm_dir) = __pa(new_page) | sgt_prot;
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
pt_dir = vmem_pte_alloc();
if (!pt_dir)
goto out;
pmd_populate(&init_mm, pm_dir, pt_dir);
} else if (pmd_large(*pm_dir)) {
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
pt_dir = pte_offset_kernel(pm_dir, address);
if (pte_none(*pt_dir)) {
void *new_page;
new_page = vmemmap_alloc_block(PAGE_SIZE, node);
if (!new_page)
goto out;
pte_val(*pt_dir) = __pa(new_page) | pgt_prot;
}
address += PAGE_SIZE;
}
ret = 0;
out:
return ret;
} }
void vmemmap_free(unsigned long start, unsigned long end, void vmemmap_free(unsigned long start, unsigned long end,
struct vmem_altmap *altmap) struct vmem_altmap *altmap)
{ {
remove_pagetable(start, end, false);
} }
void vmem_remove_mapping(unsigned long start, unsigned long size) void vmem_remove_mapping(unsigned long start, unsigned long size)
......
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