[PATCH/RFC] parisc: Flush kernel data mapping in set_pte_at() when installing pte for user page

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



For years, there have been random segmentation faults in userspace on SMP PA-RISC machines.  It
occurred to me that this might be a problem in set_pte_at().  MIPS and some other architectures
do cache flushes when installing PTEs with the present bit set.

Here I have adapted the code in update_mmu_cache() to flush the kernel mapping when the kernel
flush is deferred, or when the kernel mapping may alias with the user mapping.  This simplifies
calls to update_mmu_cache().

I also changed the barrier in set_pte() from a compiler barrier to a full memory barrier.  I know
this change is not sufficient to fix the problem.  It might not be needed.

I have had a few days of operation with 5.14.16 to 5.15.1 and haven't seen any random segmentation
faults on rp3440 or c8000 so far.

Signed-off-by: John David Anglin <dave.anglin@xxxxxxxx>
---

diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h
index 43937af127b1..9ea9872212cb 100644
--- a/arch/parisc/include/asm/pgtable.h
+++ b/arch/parisc/include/asm/pgtable.h
@@ -65,6 +65,8 @@ extern int pa_serialize_tlb_flushes;
  * are slow on SMP machines since the purge must be broadcast to all CPUs.
  */

+extern void __update_cache(pte_t pte);
+
 static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
 {
 	unsigned long flags;
@@ -80,16 +82,19 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
  * within a page table are directly modified.  Thus, the following
  * hook is made available.
  */
-#define set_pte(pteptr, pteval)			\
-	do {					\
-		*(pteptr) = (pteval);		\
-		barrier();			\
+#define set_pte(pteptr, pteval)				\
+	do {						\
+		*(pteptr) = (pteval);			\
+		mb();					\
 	} while(0)

-#define set_pte_at(mm, addr, pteptr, pteval)	\
-	do {					\
-		*(pteptr) = (pteval);		\
-		purge_tlb_entries(mm, addr);	\
+#define set_pte_at(mm, addr, pteptr, pteval)		\
+	do {						\
+		if (pte_present(pteval) &&		\
+		    pte_user(pteval))			\
+			__update_cache(pteval);		\
+		*(pteptr) = (pteval);			\
+		purge_tlb_entries(mm, addr);		\
 	} while (0)

 #endif /* !__ASSEMBLY__ */
@@ -303,6 +308,7 @@ extern unsigned long *empty_zero_page;

 #define pte_none(x)     (pte_val(x) == 0)
 #define pte_present(x)	(pte_val(x) & _PAGE_PRESENT)
+#define pte_user(x)	(pte_val(x) & _PAGE_USER)
 #define pte_clear(mm, addr, xp)  set_pte_at(mm, addr, xp, __pte(0))

 #define pmd_flag(x)	(pmd_val(x) & PxD_FLAG_MASK)
@@ -410,7 +416,7 @@ extern void paging_init (void);

 #define PG_dcache_dirty         PG_arch_1

-extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t *);
+#define update_mmu_cache(vms,addr,ptep) __update_cache(*ptep)

 /* Encode and de-code a swap entry */

diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 86a1a63563fd..c9f09d2a4461 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -83,9 +83,9 @@ EXPORT_SYMBOL(flush_cache_all_local);
 #define pfn_va(pfn)	__va(PFN_PHYS(pfn))

 void
-update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
+__update_cache(pte_t pte)
 {
-	unsigned long pfn = pte_pfn(*ptep);
+	unsigned long pfn = pte_pfn(pte);
 	struct page *page;

 	/* We don't have pte special.  As a result, we can be called with



[Index of Archives]     [Linux SoC]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux