aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.16/0040-fix-tlb-flushing-for-page-table-pages.patch
diff options
context:
space:
mode:
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.patch240
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
+