Commit 38860b2c authored by John David Anglin's avatar John David Anglin Committed by Helge Deller

parisc: Flush kernel data mapping in set_pte_at() when installing pte for user page

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: default avatarJohn David Anglin <dave.anglin@bell.net>
Signed-off-by: default avatarHelge Deller <deller@gmx.de>
Cc: stable@kernel.org # 5.12+
parent f0d1cfac
......@@ -76,6 +76,8 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
purge_tlb_end(flags);
}
extern void __update_cache(pte_t pte);
/* Certain architectures need to do special things when PTEs
* within a page table are directly modified. Thus, the following
* hook is made available.
......@@ -83,11 +85,14 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
#define set_pte(pteptr, pteval) \
do { \
*(pteptr) = (pteval); \
barrier(); \
mb(); \
} while(0)
#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)
......@@ -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 */
......
......@@ -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
......
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