diff options
Diffstat (limited to 'recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.16/0040-fix-tlb-flushing-for-page-table-pages.patch')
-rw-r--r-- | recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.16/0040-fix-tlb-flushing-for-page-table-pages.patch | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.16/0040-fix-tlb-flushing-for-page-table-pages.patch b/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.16/0040-fix-tlb-flushing-for-page-table-pages.patch new file mode 100644 index 00000000..1a6e44c8 --- /dev/null +++ b/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.16/0040-fix-tlb-flushing-for-page-table-pages.patch @@ -0,0 +1,240 @@ +From d0cba3dd900c1420176693c3f10ec11ada4553af Mon Sep 17 00:00:00 2001 +From: Martin Schwidefsky <schwidefsky@de.ibm.com> +Date: Wed, 11 Apr 2012 14:28:07 +0200 +Subject: [PATCH 40/68] fix tlb flushing for page table pages + +commit cd94154cc6a28dd9dc271042c1a59c08d26da886 upstream. + +Git commit 36409f6353fc2d7b6516e631415f938eadd92ffa "use generic RCU +page-table freeing code" introduced a tlb flushing bug. Partially revert +the above git commit and go back to s390 specific page table flush code. + +For s390 the TLB can contain three types of entries, "normal" TLB +page-table entries, TLB combined region-and-segment-table (CRST) entries +and real-space entries. Linux does not use real-space entries which +leaves normal TLB entries and CRST entries. The CRST entries are +intermediate steps in the page-table translation called translation paths. +For example a 4K page access in a three-level page table setup will +create two CRST TLB entries and one page-table TLB entry. The advantage +of that approach is that a page access next to the previous one can reuse +the CRST entries and needs just a single read from memory to create the +page-table TLB entry. The disadvantage is that the TLB flushing rules are +more complicated, before any page-table may be freed the TLB needs to be +flushed. + +In short: the generic RCU page-table freeing code is incorrect for the +CRST entries, in particular the check for mm_users < 2 is troublesome. + +This is applicable to 3.0+ kernels. + +Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + arch/s390/Kconfig | 1 - + arch/s390/include/asm/pgalloc.h | 3 -- + arch/s390/include/asm/tlb.h | 22 +------------- + arch/s390/mm/pgtable.c | 63 +++++++++++++++++++++++++++++++++++++-- + 4 files changed, 61 insertions(+), 28 deletions(-) + +diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig +index f929db9..a3c40e8 100644 +--- a/arch/s390/Kconfig ++++ b/arch/s390/Kconfig +@@ -90,7 +90,6 @@ config S390 + select HAVE_GET_USER_PAGES_FAST + select HAVE_ARCH_MUTEX_CPU_RELAX + select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 +- select HAVE_RCU_TABLE_FREE if SMP + select ARCH_SAVE_PAGE_KEYS if HIBERNATION + select ARCH_INLINE_SPIN_TRYLOCK + select ARCH_INLINE_SPIN_TRYLOCK_BH +diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h +index 8eef9b5..78e3041 100644 +--- a/arch/s390/include/asm/pgalloc.h ++++ b/arch/s390/include/asm/pgalloc.h +@@ -22,10 +22,7 @@ void crst_table_free(struct mm_struct *, unsigned long *); + + unsigned long *page_table_alloc(struct mm_struct *, unsigned long); + void page_table_free(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) + { +diff --git a/arch/s390/include/asm/tlb.h b/arch/s390/include/asm/tlb.h +index c687a2c..775a5ee 100644 +--- a/arch/s390/include/asm/tlb.h ++++ b/arch/s390/include/asm/tlb.h +@@ -30,14 +30,10 @@ + + struct mmu_gather { + struct mm_struct *mm; +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + struct mmu_table_batch *batch; +-#endif + unsigned int fullmm; +- unsigned int need_flush; + }; + +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + struct mmu_table_batch { + struct rcu_head rcu; + unsigned int nr; +@@ -49,7 +45,6 @@ struct mmu_table_batch { + + 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, + struct mm_struct *mm, +@@ -57,29 +52,20 @@ static inline void tlb_gather_mmu(struct mmu_gather *tlb, + { + tlb->mm = mm; + tlb->fullmm = full_mm_flush; +- tlb->need_flush = 0; +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + tlb->batch = NULL; +-#endif + if (tlb->fullmm) + __tlb_flush_mm(mm); + } + + static inline void tlb_flush_mmu(struct mmu_gather *tlb) + { +- if (!tlb->need_flush) +- return; +- tlb->need_flush = 0; +- __tlb_flush_mm(tlb->mm); +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + tlb_table_flush(tlb); +-#endif + } + + static inline void tlb_finish_mmu(struct mmu_gather *tlb, + unsigned long start, unsigned long end) + { +- tlb_flush_mmu(tlb); ++ tlb_table_flush(tlb); + } + + /* +@@ -105,10 +91,8 @@ 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, + unsigned long address) + { +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + if (!tlb->fullmm) + return page_table_free_rcu(tlb, (unsigned long *) pte); +-#endif + page_table_free(tlb->mm, (unsigned long *) pte); + } + +@@ -125,10 +109,8 @@ static inline void pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd, + #ifdef __s390x__ + if (tlb->mm->context.asce_limit <= (1UL << 31)) + return; +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + if (!tlb->fullmm) + return tlb_remove_table(tlb, pmd); +-#endif + crst_table_free(tlb->mm, (unsigned long *) pmd); + #endif + } +@@ -146,10 +128,8 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud, + #ifdef __s390x__ + if (tlb->mm->context.asce_limit <= (1UL << 42)) + return; +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE + if (!tlb->fullmm) + return tlb_remove_table(tlb, pud); +-#endif + crst_table_free(tlb->mm, (unsigned long *) pud); + #endif + } +diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c +index 301c84d..f8ceac4 100644 +--- a/arch/s390/mm/pgtable.c ++++ b/arch/s390/mm/pgtable.c +@@ -687,8 +687,6 @@ void page_table_free(struct mm_struct *mm, unsigned long *table) + } + } + +-#ifdef CONFIG_HAVE_RCU_TABLE_FREE +- + static void __page_table_free_rcu(void *table, unsigned bit) + { + struct page *page; +@@ -742,7 +740,66 @@ void __tlb_remove_table(void *_table) + free_pages((unsigned long) table, ALLOC_ORDER); + } + +-#endif ++static void tlb_remove_table_smp_sync(void *arg) ++{ ++ /* Simply deliver the interrupt */ ++} ++ ++static void tlb_remove_table_one(void *table) ++{ ++ /* ++ * This isn't an RCU grace period and hence the page-tables cannot be ++ * assumed to be actually RCU-freed. ++ * ++ * It is however sufficient for software page-table walkers that rely ++ * on IRQ disabling. See the comment near struct mmu_table_batch. ++ */ ++ smp_call_function(tlb_remove_table_smp_sync, NULL, 1); ++ __tlb_remove_table(table); ++} ++ ++static void tlb_remove_table_rcu(struct rcu_head *head) ++{ ++ struct mmu_table_batch *batch; ++ int i; ++ ++ batch = container_of(head, struct mmu_table_batch, rcu); ++ ++ for (i = 0; i < batch->nr; i++) ++ __tlb_remove_table(batch->tables[i]); ++ ++ free_page((unsigned long)batch); ++} ++ ++void tlb_table_flush(struct mmu_gather *tlb) ++{ ++ struct mmu_table_batch **batch = &tlb->batch; ++ ++ if (*batch) { ++ __tlb_flush_mm(tlb->mm); ++ call_rcu_sched(&(*batch)->rcu, tlb_remove_table_rcu); ++ *batch = NULL; ++ } ++} ++ ++void tlb_remove_table(struct mmu_gather *tlb, void *table) ++{ ++ struct mmu_table_batch **batch = &tlb->batch; ++ ++ if (*batch == NULL) { ++ *batch = (struct mmu_table_batch *) ++ __get_free_page(GFP_NOWAIT | __GFP_NOWARN); ++ if (*batch == NULL) { ++ __tlb_flush_mm(tlb->mm); ++ tlb_remove_table_one(table); ++ return; ++ } ++ (*batch)->nr = 0; ++ } ++ (*batch)->tables[(*batch)->nr++] = table; ++ if ((*batch)->nr == MAX_TABLE_BATCH) ++ tlb_table_flush(tlb); ++} + + /* + * switch on pgstes for its userspace process (for kvm) +-- +1.7.10 + |