Commit 36409f63 authored by Martin Schwidefsky's avatar Martin Schwidefsky

[S390] use generic RCU page-table freeing code

Replace the s390 specific rcu page-table freeing code with the
generic variant. This requires to duplicate the definition for the
struct mmu_table_batch as s390 does not use the generic tlb flush
code.

While we are at it remove the restriction that page table fragments
can not be reused after a single fragment has been freed with rcu
and split out allocation and freeing of page tables with pgstes.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 3ec90878
...@@ -89,6 +89,7 @@ config S390 ...@@ -89,6 +89,7 @@ config S390
select HAVE_GET_USER_PAGES_FAST select HAVE_GET_USER_PAGES_FAST
select HAVE_ARCH_MUTEX_CPU_RELAX select HAVE_ARCH_MUTEX_CPU_RELAX
select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
select HAVE_RCU_TABLE_FREE if SMP
select ARCH_INLINE_SPIN_TRYLOCK select ARCH_INLINE_SPIN_TRYLOCK
select ARCH_INLINE_SPIN_TRYLOCK_BH select ARCH_INLINE_SPIN_TRYLOCK_BH
select ARCH_INLINE_SPIN_LOCK select ARCH_INLINE_SPIN_LOCK
......
...@@ -17,15 +17,15 @@ ...@@ -17,15 +17,15 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/mm.h> #include <linux/mm.h>
#define check_pgt_cache() do {} while (0)
unsigned long *crst_table_alloc(struct mm_struct *); unsigned long *crst_table_alloc(struct mm_struct *);
void crst_table_free(struct mm_struct *, unsigned long *); void crst_table_free(struct mm_struct *, unsigned long *);
void crst_table_free_rcu(struct mm_struct *, unsigned long *);
unsigned long *page_table_alloc(struct mm_struct *); unsigned long *page_table_alloc(struct mm_struct *);
void page_table_free(struct mm_struct *, unsigned long *); void page_table_free(struct mm_struct *, unsigned long *);
void page_table_free_rcu(struct mm_struct *, unsigned long *); #ifdef CONFIG_HAVE_RCU_TABLE_FREE
void page_table_free_rcu(struct mmu_gather *, unsigned long *);
void __tlb_remove_table(void *_table);
#endif
static inline void clear_table(unsigned long *s, unsigned long val, size_t n) static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
{ {
......
...@@ -26,67 +26,60 @@ ...@@ -26,67 +26,60 @@
#include <linux/swap.h> #include <linux/swap.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/smp.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
struct mmu_gather { struct mmu_gather {
struct mm_struct *mm; struct mm_struct *mm;
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
struct mmu_table_batch *batch;
#endif
unsigned int fullmm; unsigned int fullmm;
unsigned int nr_ptes; unsigned int need_flush;
unsigned int nr_pxds;
unsigned int max;
void **array;
void *local[8];
}; };
static inline void __tlb_alloc_page(struct mmu_gather *tlb) #ifdef CONFIG_HAVE_RCU_TABLE_FREE
{ struct mmu_table_batch {
unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0); struct rcu_head rcu;
unsigned int nr;
void *tables[0];
};
if (addr) { #define MAX_TABLE_BATCH \
tlb->array = (void *) addr; ((PAGE_SIZE - sizeof(struct mmu_table_batch)) / sizeof(void *))
tlb->max = PAGE_SIZE / sizeof(void *);
} extern void tlb_table_flush(struct mmu_gather *tlb);
} extern void tlb_remove_table(struct mmu_gather *tlb, void *table);
#endif
static inline void tlb_gather_mmu(struct mmu_gather *tlb, static inline void tlb_gather_mmu(struct mmu_gather *tlb,
struct mm_struct *mm, struct mm_struct *mm,
unsigned int full_mm_flush) unsigned int full_mm_flush)
{ {
tlb->mm = mm; tlb->mm = mm;
tlb->max = ARRAY_SIZE(tlb->local);
tlb->array = tlb->local;
tlb->fullmm = full_mm_flush; tlb->fullmm = full_mm_flush;
tlb->need_flush = 0;
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
tlb->batch = NULL;
#endif
if (tlb->fullmm) if (tlb->fullmm)
__tlb_flush_mm(mm); __tlb_flush_mm(mm);
else
__tlb_alloc_page(tlb);
tlb->nr_ptes = 0;
tlb->nr_pxds = tlb->max;
} }
static inline void tlb_flush_mmu(struct mmu_gather *tlb) static inline void tlb_flush_mmu(struct mmu_gather *tlb)
{ {
if (!tlb->fullmm && (tlb->nr_ptes > 0 || tlb->nr_pxds < tlb->max)) if (!tlb->need_flush)
return;
tlb->need_flush = 0;
__tlb_flush_mm(tlb->mm); __tlb_flush_mm(tlb->mm);
while (tlb->nr_ptes > 0) #ifdef CONFIG_HAVE_RCU_TABLE_FREE
page_table_free_rcu(tlb->mm, tlb->array[--tlb->nr_ptes]); tlb_table_flush(tlb);
while (tlb->nr_pxds < tlb->max) #endif
crst_table_free_rcu(tlb->mm, tlb->array[tlb->nr_pxds++]);
} }
static inline void tlb_finish_mmu(struct mmu_gather *tlb, static inline void tlb_finish_mmu(struct mmu_gather *tlb,
unsigned long start, unsigned long end) unsigned long start, unsigned long end)
{ {
tlb_flush_mmu(tlb); tlb_flush_mmu(tlb);
rcu_table_freelist_finish();
/* keep the page table cache within bounds */
check_pgt_cache();
if (tlb->array != tlb->local)
free_pages((unsigned long) tlb->array, 0);
} }
/* /*
...@@ -112,11 +105,10 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) ...@@ -112,11 +105,10 @@ static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
unsigned long address) unsigned long address)
{ {
if (!tlb->fullmm) { #ifdef CONFIG_HAVE_RCU_TABLE_FREE
tlb->array[tlb->nr_ptes++] = pte; if (!tlb->fullmm)
if (tlb->nr_ptes >= tlb->nr_pxds) return page_table_free_rcu(tlb, (unsigned long *) pte);
tlb_flush_mmu(tlb); #endif
} else
page_table_free(tlb->mm, (unsigned long *) pte); page_table_free(tlb->mm, (unsigned long *) pte);
} }
...@@ -133,11 +125,10 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd, ...@@ -133,11 +125,10 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd,
#ifdef __s390x__ #ifdef __s390x__
if (tlb->mm->context.asce_limit <= (1UL << 31)) if (tlb->mm->context.asce_limit <= (1UL << 31))
return; return;
if (!tlb->fullmm) { #ifdef CONFIG_HAVE_RCU_TABLE_FREE
tlb->array[--tlb->nr_pxds] = pmd; if (!tlb->fullmm)
if (tlb->nr_ptes >= tlb->nr_pxds) return tlb_remove_table(tlb, pmd);
tlb_flush_mmu(tlb); #endif
} else
crst_table_free(tlb->mm, (unsigned long *) pmd); crst_table_free(tlb->mm, (unsigned long *) pmd);
#endif #endif
} }
...@@ -155,11 +146,10 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud, ...@@ -155,11 +146,10 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
#ifdef __s390x__ #ifdef __s390x__
if (tlb->mm->context.asce_limit <= (1UL << 42)) if (tlb->mm->context.asce_limit <= (1UL << 42))
return; return;
if (!tlb->fullmm) { #ifdef CONFIG_HAVE_RCU_TABLE_FREE
tlb->array[--tlb->nr_pxds] = pud; if (!tlb->fullmm)
if (tlb->nr_ptes >= tlb->nr_pxds) return tlb_remove_table(tlb, pud);
tlb_flush_mmu(tlb); #endif
} else
crst_table_free(tlb->mm, (unsigned long *) pud); crst_table_free(tlb->mm, (unsigned long *) pud);
#endif #endif
} }
......
This diff is collapsed.
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