diff options
Diffstat (limited to 'arch/s390/mm/pgtable.c')
| -rw-r--r-- | arch/s390/mm/pgtable.c | 142 | 
1 files changed, 43 insertions, 99 deletions
| diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 33f589459113..b33f66110ca9 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -18,6 +18,7 @@  #include <linux/rcupdate.h>  #include <linux/slab.h>  #include <linux/swapops.h> +#include <linux/sysctl.h>  #include <linux/ksm.h>  #include <linux/mman.h> @@ -920,6 +921,40 @@ unsigned long get_guest_storage_key(struct mm_struct *mm, unsigned long addr)  }  EXPORT_SYMBOL(get_guest_storage_key); +static int page_table_allocate_pgste_min = 0; +static int page_table_allocate_pgste_max = 1; +int page_table_allocate_pgste = 0; +EXPORT_SYMBOL(page_table_allocate_pgste); + +static struct ctl_table page_table_sysctl[] = { +	{ +		.procname	= "allocate_pgste", +		.data		= &page_table_allocate_pgste, +		.maxlen		= sizeof(int), +		.mode		= S_IRUGO | S_IWUSR, +		.proc_handler	= proc_dointvec, +		.extra1		= &page_table_allocate_pgste_min, +		.extra2		= &page_table_allocate_pgste_max, +	}, +	{ } +}; + +static struct ctl_table page_table_sysctl_dir[] = { +	{ +		.procname	= "vm", +		.maxlen		= 0, +		.mode		= 0555, +		.child		= page_table_sysctl, +	}, +	{ } +}; + +static int __init page_table_register_sysctl(void) +{ +	return register_sysctl_table(page_table_sysctl_dir) ? 0 : -ENOMEM; +} +__initcall(page_table_register_sysctl); +  #else /* CONFIG_PGSTE */  static inline int page_table_with_pgste(struct page *page) @@ -963,7 +998,7 @@ unsigned long *page_table_alloc(struct mm_struct *mm)  	struct page *uninitialized_var(page);  	unsigned int mask, bit; -	if (mm_has_pgste(mm)) +	if (mm_alloc_pgste(mm))  		return page_table_alloc_pgste(mm);  	/* Allocate fragments of a 4K page as 1K/2K page table */  	spin_lock_bh(&mm->context.list_lock); @@ -1165,116 +1200,25 @@ static inline void thp_split_mm(struct mm_struct *mm)  }  #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ -static unsigned long page_table_realloc_pmd(struct mmu_gather *tlb, -				struct mm_struct *mm, pud_t *pud, -				unsigned long addr, unsigned long end) -{ -	unsigned long next, *table, *new; -	struct page *page; -	spinlock_t *ptl; -	pmd_t *pmd; - -	pmd = pmd_offset(pud, addr); -	do { -		next = pmd_addr_end(addr, end); -again: -		if (pmd_none_or_clear_bad(pmd)) -			continue; -		table = (unsigned long *) pmd_deref(*pmd); -		page = pfn_to_page(__pa(table) >> PAGE_SHIFT); -		if (page_table_with_pgste(page)) -			continue; -		/* Allocate new page table with pgstes */ -		new = page_table_alloc_pgste(mm); -		if (!new) -			return -ENOMEM; - -		ptl = pmd_lock(mm, pmd); -		if (likely((unsigned long *) pmd_deref(*pmd) == table)) { -			/* Nuke pmd entry pointing to the "short" page table */ -			pmdp_flush_lazy(mm, addr, pmd); -			pmd_clear(pmd); -			/* Copy ptes from old table to new table */ -			memcpy(new, table, PAGE_SIZE/2); -			clear_table(table, _PAGE_INVALID, PAGE_SIZE/2); -			/* Establish new table */ -			pmd_populate(mm, pmd, (pte_t *) new); -			/* Free old table with rcu, there might be a walker! */ -			page_table_free_rcu(tlb, table, addr); -			new = NULL; -		} -		spin_unlock(ptl); -		if (new) { -			page_table_free_pgste(new); -			goto again; -		} -	} while (pmd++, addr = next, addr != end); - -	return addr; -} - -static unsigned long page_table_realloc_pud(struct mmu_gather *tlb, -				   struct mm_struct *mm, pgd_t *pgd, -				   unsigned long addr, unsigned long end) -{ -	unsigned long next; -	pud_t *pud; - -	pud = pud_offset(pgd, addr); -	do { -		next = pud_addr_end(addr, end); -		if (pud_none_or_clear_bad(pud)) -			continue; -		next = page_table_realloc_pmd(tlb, mm, pud, addr, next); -		if (unlikely(IS_ERR_VALUE(next))) -			return next; -	} while (pud++, addr = next, addr != end); - -	return addr; -} - -static unsigned long page_table_realloc(struct mmu_gather *tlb, struct mm_struct *mm, -					unsigned long addr, unsigned long end) -{ -	unsigned long next; -	pgd_t *pgd; - -	pgd = pgd_offset(mm, addr); -	do { -		next = pgd_addr_end(addr, end); -		if (pgd_none_or_clear_bad(pgd)) -			continue; -		next = page_table_realloc_pud(tlb, mm, pgd, addr, next); -		if (unlikely(IS_ERR_VALUE(next))) -			return next; -	} while (pgd++, addr = next, addr != end); - -	return 0; -} -  /*   * switch on pgstes for its userspace process (for kvm)   */  int s390_enable_sie(void)  { -	struct task_struct *tsk = current; -	struct mm_struct *mm = tsk->mm; -	struct mmu_gather tlb; +	struct mm_struct *mm = current->mm;  	/* Do we have pgstes? if yes, we are done */ -	if (mm_has_pgste(tsk->mm)) +	if (mm_has_pgste(mm))  		return 0; - +	/* Fail if the page tables are 2K */ +	if (!mm_alloc_pgste(mm)) +		return -EINVAL;  	down_write(&mm->mmap_sem); +	mm->context.has_pgste = 1;  	/* split thp mappings and disable thp for future mappings */  	thp_split_mm(mm); -	/* Reallocate the page tables with pgstes */ -	tlb_gather_mmu(&tlb, mm, 0, TASK_SIZE); -	if (!page_table_realloc(&tlb, mm, 0, TASK_SIZE)) -		mm->context.has_pgste = 1; -	tlb_finish_mmu(&tlb, 0, TASK_SIZE);  	up_write(&mm->mmap_sem); -	return mm->context.has_pgste ? 0 : -ENOMEM; +	return 0;  }  EXPORT_SYMBOL_GPL(s390_enable_sie); |