diff mbox series

[v2] parisc: Try to fix random segmentation faults in package builds

Message ID Zkeo1UxDTcJtBZTl@atlas (mailing list archive)
State New
Headers show
Series [v2] parisc: Try to fix random segmentation faults in package builds | expand

Commit Message

John David Anglin May 17, 2024, 6:58 p.m. UTC
The majority of random segmentation faults that I have looked at
appear to be memory corruption in memory allocated using mmap and
malloc.

My first attempt at fixing the random faults didn't work. On
reviewing the cache code, I realized that there were two issues
which the existing code didn't handle correctly. Both relate
to cache move-in. Another issue is that the present bit in PTEs
is racy.

1) PA-RISC caches have a mind of their own and they can speculatively
load data and instructions to a page as long as there is a entry in
the TLB for the page which allows move-in. TLBs are local to each
CPU. Thus, the TLB entry for a page must be purged before flushing
the page. This is particularly important on SMP systems.

In some of the flush routines, the flush routine would be called
and then the TLB entry would be purged. This was because the flush
routine needed the TLB entry to do the flush.

2) My initial approach to trying the fix the random faults was to
try and use flush_cache_page_if_present for all flush operations.
This actually made things worse and led to a couple of hardware
lockups. It finally dawned on me that some lines weren't being
flushed because the pte check code was racy. This resulted in
random inequivalent mappings to physical pages.

The __flush_cache_page tmpalias flush sets up its own TLB entry
and it doesn't need the existing TLB entry. As long as we can find
the pte pointer for the vm page, we can get the pfn and physical
address of the page. We can also purge the TLB entry for the page
before doing the flush. Further, __flush_cache_page uses a special
TLB entry that inhibits cache move-in.

When switching page mappings, we need to ensure that lines are
removed from the cache.  It is not sufficient to just flush the
lines to memory.

This made it clear that we needed to implement all the required
flush operations using tmpalias routines. This includes flushes
for user and kernel pages.

This change is the result of that conversion. As far as I can
tell, it fixes the random segmentation faults on c8000.

Base for patch is 6.8.9.

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

Comments

Vidra.Jonas@seznam.cz May 26, 2024, 2:07 p.m. UTC | #1
---------- Original e-mail ----------
From: John David Anglin
To: linux-parisc@vger.kernel.org
CC: Helge Deller
Date: 17. 5. 2024 21:05:19
Subject: [PATCH v2] parisc: Try to fix random segmentation faults in package builds

> The majority of random segmentation faults that I have looked at
> appear to be memory corruption in memory allocated using mmap and
> malloc.
>
> [...]
>
> This made it clear that we needed to implement all the required
> flush operations using tmpalias routines. This includes flushes
> for user and kernel pages.
>
> This change is the result of that conversion. As far as I can
> tell, it fixes the random segmentation faults on c8000.
>
> Base for patch is 6.8.9.

Hello,

I applied the patch to a 6.8.9 kernel with Gentoo patches on my C8000
and ran it under heavy load over the weekend. The system has been much
more stable than in the past (yay!), but I've still experienced one
userspace program crash and a kernel panic.

The crash was a memory corruption while compiling Boost (version
boost-1.84.0-r3 from Gentoo). It might be caused by a kernel memory
handling bug, but it's hard to say. Upon recompiling, the problem didn't
manifest again. There was nothing in the syslog and the output is not
very informative:

```
gcc.compile.c++ bin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden/instantiate_re2c_lexer.o

    "hppa2.0-unknown-linux-gnu-g++"   -fvisibility-inlines-hidden  -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -O2 -pipe -march=2.0 -mschedule=8000 -ggdb -std=c++17 -fPIC -pthread -finline-functions -Wno-inline -Wall -fvisibility=hidden  -DBOOST_ALL_DYN_LINK=1 -DBOOST_ALL_NO_LIB=1 -DBOOST_COBALT_USE_STD_PMR=1 -DNDEBUG   -I"."  -c -o "bin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden/instantiate_re2c_lexer.o" "libs/wave/src/instantiate_re2c_lexer.cpp"

{standard input}: Assembler messages:
{standard input}:401704: Error: unknown pseudo-op: `.ule'
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x2
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x1
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x1
{standard input}:401704: Error: Unknown opcode: `ag�'
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x3
{standard input}:401704: Error: Unknown opcode: `uƀ'
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x1
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x10
{standard input}:401704: Error: junk at end of line, first unrecognized character valued 0x10
{standard input}:401704: Error: Unknown opcode: `a
                                                  ��'
{standard input}:401704: Error: Unknown opcode: `x15'
{standard input}:796319: Warning: end of file in string; '"' inserted
{standard input}:797107: Warning: missing closing '"'
{standard input}:797107: Error: Unknown opcode: `
'
gcc.compile.c++ bin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden/instantiate_re2c_lexer_str.o

    "hppa2.0-unknown-linux-gnu-g++"   -fvisibility-inlines-hidden  -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -O2 -pipe -march=2.0 -mschedule=8000 -ggdb -std=c++17 -fPIC -pthread -finline-functions -Wno-inline -Wall -fvisibility=hidden  -DBOOST_ALL_DYN_LINK=1 -DBOOST_ALL_NO_LIB=1 -DBOOST_COBALT_USE_STD_PMR=1 -DNDEBUG   -I"."  -c -o "bin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden/instantiate_re2c_lexer_str.o" "libs/wave/src/instantiate_re2c_lexer_str.cpp"

...skipped <pbin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden>libboost_wave.so.1.84.0 for lack of <pbin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden>instantiate_re2c_lexer.o...
...skipped <p/var/tmp/portage/dev-libs/boost-1.84.0-r3/work/boost_1_84_0-.hppa/stage/lib>libboost_wave.so.1.84.0 for lack of <pbin.v2/libs/wave/build/gcc-13.2/gentoorelease/pch-off/threading-multi/visibility-hidden>libboost_wave.so.1.84.0...
...failed updating 1 target...
```


The kernel panic happened after a few days of uptime. I got the
following output on the serial console, after which the machine
rebooted immediately and the usual boot output followed.

```
[163003.648077] Backtrace:
[163003.648077]  [<00000000408d7740>] btrfs_add_delayed_data_ref+0x1d4/0x598
[163003.648077]  [<00000000407e61cc>] btrfs_alloc_reserved_file_extent+0x158/0x210
[163003.648077]  [<0000000040819638>] insert_reserved_file_extent+0x77c/0x840
[163003.648077]  [<0000000040825220>] btrfs_finish_one_ordered+0xa54/0x1310
[163003.648077]  [<0000000040825b0c>] btrfs_finish_ordered_io+0x30/0x70
[163003.648077]  [<000000004085cadc>] finish_ordered_fn+0x38/0x78
[163003.648077]  [<0000000040891c9c>] btrfs_work_helper+0x208/0x790
[163003.648077]  [<000000004025eb74>] process_one_work+0x228/0x540
[163003.648077]  [<000000004025f1ac>] worker_thread+0x320/0x760
[163003.648077]  [<0000000040273020>] kthread+0x254/0x280
[163003.648077]  [<00000000401df020>] ret_from_kernel_thread+0x20/0x28
[163003.648077] 
[163003.648077] 
[163003.648077] Page fault: no context: Code=15 (Data TLB miss fault) at addr 0000003b1047cdee
[163003.648077] CPU: 3 PID: 9804 Comm: kworker/u8:4 Not tainted 6.8.9-gentoo-64bit-debug #2
[163003.648077] Hardware name: 9000/785/C8000
[163003.648077] Workqueue: btrfs-endio-write btrfs_work_helper
[163003.648077] 
[163003.648077]      YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI
[163003.648077] PSW: 00001000000001000000000000001111 Not tainted
[163003.648077] r00-03  000000000804000f 0000000041235180 0000000040520374 00000003202e8d50
[163003.648077] r04-07  0000000041165180 0000000055076f20 0000000000000001 00000000408d7740
[163003.648077] r08-11  0000000000000c40 00000000000000b0 0000000000000000 00000000417b72a8
[163003.648077] r12-15  0000000000455a10 00000000556f2000 0000000000000001 00000002a326d620
[163003.648077] r16-19  00000003202e8b68 0000000000000000 0000000000000000 82140f831247cc96
[163003.648077] r20-23  00000000000d486a 0000000057ec22e0 00000000000d486b 000000004147c0d0
[163003.648077] r24-27  eecd47103bccf4da daf4cc3b1047cdee daf4cc3b1047cd96 0000000041165180
[163003.648077] r28-31  0000000000000058 00cd001000cc00da 00000003202e8e30 250b33c4efb8326a
[163003.648077] sr00-03  0000000007067400 0000000000000000 0000000000000000 0000000007067400
[163003.648077] sr04-07  0000000000000000 0000000000000000 0000000000000000 0000000000000000
[163003.648077] 
[163003.648077] IASQ: 0000000000000000 0000000000000000 IAOQ: 00000000405202d8 00000000405202dc
[163003.648077]  IIR: 0f9a00dc    ISR: 000000001af4cc00  IOR: 0000003b1047cdee
[163003.648077]  CPU:        3   CR30: 0000000057ec22e0 CR31: fffffffffffeffff
[163003.648077]  ORIG_R28: 00000000000009de
[163003.648077]  IAOQ[0]: kmem_cache_alloc+0x10c/0x520
[163003.648077]  IAOQ[1]: kmem_cache_alloc+0x110/0x520
[163003.648077]  RP(r2): kmem_cache_alloc+0x1a8/0x520
[163003.648077] Backtrace:
[163003.648077]  [<00000000408d7740>] btrfs_add_delayed_data_ref+0x1d4/0x598
[163003.648077]  [<00000000407e61cc>] btrfs_alloc_reserved_file_extent+0x158/0x210
[163003.648077]  [<0000000040819638>] insert_reserved_file_extent+0x77c/0x840
[163003.648077]  [<0000000040825220>] btrfs_finish_one_ordered+0xa54/0x1310
[163003.648077]  [<0000000040825b0c>] btrfs_finish_ordered_io+0x30/0x70
[163003.648077]  [<000000004085cadc>] finish_ordered_fn+0x38/0x78
[163003.648077]  [<0000000040891c9c>] btrfs_work_helper+0x208/0x790
[163003.648077]  [<000000004025eb74>] process_one_work+0x228/0x540
[163003.648077]  [<000000004025f1ac>] worker_thread+0x320/0x760
[163003.648077]  [<0000000040273020>] kthread+0x254/0x280
[163003.648077]  [<00000000401df020>] ret_from_kernel_thread+0x20/0x28
[163003.648077] 
<Cpu3> 0300109103e00000  0000000000000000  CC_PROCS_ENTRY_OUT
[163003.648077] Kernel panic - not syncing: Page fault: no context
<Cpu3> 160012c803e00000  0300000000000000  CC_MPS_SLAVE_DISPATCHER_ENT
<Cpu3> 0300096303e00000  0000000008000008  CC_BOOT_MEM_CPU_RENDEZVOUS
<Cpu3> 160012d303e00000  0300000000000000  CC_MPS_SLAVE_SLEEPING
<Cpu3> 080012d403e00000  000000f0f0d07dc0  CC_MPS_SLAVE_SLEEP_ADDR
<Cpu2> 3400006302e00000  0000000000000000  CC_BOOT_START
```
John David Anglin May 26, 2024, 3:41 p.m. UTC | #2
Hi Vidra,

Thanks for testing.

On 2024-05-26 10:07 a.m., Vidra.Jonas@seznam.cz wrote:
> I applied the patch to a 6.8.9 kernel with Gentoo patches on my C8000
> and ran it under heavy load over the weekend. The system has been much
> more stable than in the past (yay!), but I've still experienced one
> userspace program crash and a kernel panic.
Yes, the last version that I sent still has problems although things are improved.  I've
found a few more problems and will send an update after I do some more testing.

I could post latest version if you want to test.  I am hopeful that random faults are now
fixed but frequency varies.

I had a system hard drive failure on one of my systems and it has taken some time to
get it up and working again.  Old installer doesn't work - probably because of t64 transition.
>
> The crash was a memory corruption while compiling Boost (version
> boost-1.84.0-r3 from Gentoo). It might be caused by a kernel memory
> handling bug, but it's hard to say. Upon recompiling, the problem didn't
> manifest again. There was nothing in the syslog and the output is not
> very informative:
I've seen similar errors in debian builds.

Dave
diff mbox series

Patch

diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h
index ba4c05bc24d6..dd8a29aaff60 100644
--- a/arch/parisc/include/asm/cacheflush.h
+++ b/arch/parisc/include/asm/cacheflush.h
@@ -31,8 +31,6 @@  void flush_cache_all_local(void);
 void flush_cache_all(void);
 void flush_cache_mm(struct mm_struct *mm);
 
-void flush_kernel_dcache_page_addr(const void *addr);
-
 #define flush_kernel_dcache_range(start,size) \
 	flush_kernel_dcache_range_asm((start), (start)+(size));
 
@@ -77,17 +75,11 @@  void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
 void flush_cache_range(struct vm_area_struct *vma,
 		unsigned long start, unsigned long end);
 
-/* defined in pacache.S exported in cache.c used by flush_anon_page */
-void flush_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr);
-
 #define ARCH_HAS_FLUSH_ANON_PAGE
 void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr);
 
 #define ARCH_HAS_FLUSH_ON_KUNMAP
-static inline void kunmap_flush_on_unmap(const void *addr)
-{
-	flush_kernel_dcache_page_addr(addr);
-}
+void kunmap_flush_on_unmap(const void *addr);
 
 #endif /* _PARISC_CACHEFLUSH_H */
 
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 422f3e1e6d9c..0aa99c9d7cc3 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -36,15 +36,16 @@  int dcache_stride __ro_after_init;
 int icache_stride __ro_after_init;
 EXPORT_SYMBOL(dcache_stride);
 
+/* Internal implementation in arch/parisc/kernel/pacache.S */
 void flush_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr);
 EXPORT_SYMBOL(flush_dcache_page_asm);
 void purge_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr);
 void flush_icache_page_asm(unsigned long phys_addr, unsigned long vaddr);
-
-/* Internal implementation in arch/parisc/kernel/pacache.S */
 void flush_data_cache_local(void *);  /* flushes local data-cache only */
 void flush_instruction_cache_local(void); /* flushes local code-cache only */
 
+static void flush_kernel_dcache_page_addr(const void *addr);
+
 /* On some machines (i.e., ones with the Merced bus), there can be
  * only a single PxTLB broadcast at a time; this must be guaranteed
  * by software. We need a spinlock around all TLB flushes to ensure
@@ -321,6 +322,18 @@  __flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
 {
 	if (!static_branch_likely(&parisc_has_cache))
 		return;
+
+	/*
+	 * The TLB is the engine of coherence on parisc.  The CPU is
+	 * entitled to speculate any page with a TLB mapping, so here
+	 * we kill the mapping then flush the page along a special flush
+	 * only alias mapping. This guarantees that the page is no-longer
+	 * in the cache for any process and nor may it be speculatively
+	 * read in (until the user or kernel specifically accesses it,
+	 * of course).
+	 */
+	flush_tlb_page(vma, vmaddr);
+
 	preempt_disable();
 	flush_dcache_page_asm(physaddr, vmaddr);
 	if (vma->vm_flags & VM_EXEC)
@@ -328,46 +341,44 @@  __flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
 	preempt_enable();
 }
 
-static void flush_user_cache_page(struct vm_area_struct *vma, unsigned long vmaddr)
+static void flush_kernel_dcache_page_addr(const void *addr)
 {
-	unsigned long flags, space, pgd, prot;
-#ifdef CONFIG_TLB_PTLOCK
-	unsigned long pgd_lock;
-#endif
+	unsigned long vaddr = (unsigned long)addr;
+	unsigned long flags;
 
-	vmaddr &= PAGE_MASK;
+	/* Purge TLB entry to remove translation on all CPUs */
+	purge_tlb_start(flags);
+	pdtlb(SR_KERNEL, addr);
+	purge_tlb_end(flags);
 
+	/* Use tmpalias flush to prevent data cache move-in */
 	preempt_disable();
+	flush_dcache_page_asm(__pa(vaddr), vaddr);
+	preempt_enable();
+}
 
-	/* Set context for flush */
-	local_irq_save(flags);
-	prot = mfctl(8);
-	space = mfsp(SR_USER);
-	pgd = mfctl(25);
-#ifdef CONFIG_TLB_PTLOCK
-	pgd_lock = mfctl(28);
-#endif
-	switch_mm_irqs_off(NULL, vma->vm_mm, NULL);
-	local_irq_restore(flags);
-
-	flush_user_dcache_range_asm(vmaddr, vmaddr + PAGE_SIZE);
-	if (vma->vm_flags & VM_EXEC)
-		flush_user_icache_range_asm(vmaddr, vmaddr + PAGE_SIZE);
-	flush_tlb_page(vma, vmaddr);
+static void flush_kernel_icache_page_addr(const void *addr)
+{
+	unsigned long vaddr = (unsigned long)addr;
+	unsigned long flags;
 
-	/* Restore previous context */
-	local_irq_save(flags);
-#ifdef CONFIG_TLB_PTLOCK
-	mtctl(pgd_lock, 28);
-#endif
-	mtctl(pgd, 25);
-	mtsp(space, SR_USER);
-	mtctl(prot, 8);
-	local_irq_restore(flags);
+	/* Purge TLB entry to remove translation on all CPUs */
+	purge_tlb_start(flags);
+	pdtlb(SR_KERNEL, addr);
+	purge_tlb_end(flags);
 
+	/* Use tmpalias flush to prevent instruction cache move-in */
+	preempt_disable();
+	flush_icache_page_asm(__pa(vaddr), vaddr);
 	preempt_enable();
 }
 
+void kunmap_flush_on_unmap(const void *addr)
+{
+	flush_kernel_dcache_page_addr(addr);
+}
+EXPORT_SYMBOL(kunmap_flush_on_unmap);
+
 void flush_icache_pages(struct vm_area_struct *vma, struct page *page,
 		unsigned int nr)
 {
@@ -375,7 +386,7 @@  void flush_icache_pages(struct vm_area_struct *vma, struct page *page,
 
 	for (;;) {
 		flush_kernel_dcache_page_addr(kaddr);
-		flush_kernel_icache_page(kaddr);
+		flush_kernel_icache_page_addr(kaddr);
 		if (--nr == 0)
 			break;
 		kaddr += PAGE_SIZE;
@@ -404,12 +415,6 @@  static inline pte_t *get_ptep(struct mm_struct *mm, unsigned long addr)
 	return ptep;
 }
 
-static inline bool pte_needs_flush(pte_t pte)
-{
-	return (pte_val(pte) & (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_NO_CACHE))
-		== (_PAGE_PRESENT | _PAGE_ACCESSED);
-}
-
 void flush_dcache_folio(struct folio *folio)
 {
 	struct address_space *mapping = folio_flush_mapping(folio);
@@ -458,50 +463,23 @@  void flush_dcache_folio(struct folio *folio)
 		if (addr + nr * PAGE_SIZE > vma->vm_end)
 			nr = (vma->vm_end - addr) / PAGE_SIZE;
 
-		if (parisc_requires_coherency()) {
-			for (i = 0; i < nr; i++) {
-				pte_t *ptep = get_ptep(vma->vm_mm,
-							addr + i * PAGE_SIZE);
-				if (!ptep)
-					continue;
-				if (pte_needs_flush(*ptep))
-					flush_user_cache_page(vma,
-							addr + i * PAGE_SIZE);
-				/* Optimise accesses to the same table? */
-				pte_unmap(ptep);
-			}
-		} else {
+		if (old_addr == 0 || (old_addr & (SHM_COLOUR - 1))
+					!= (addr & (SHM_COLOUR - 1))) {
+			for (i = 0; i < nr; i++)
+				__flush_cache_page(vma,
+					addr + i * PAGE_SIZE,
+					(pfn + i) * PAGE_SIZE);
 			/*
-			 * The TLB is the engine of coherence on parisc:
-			 * The CPU is entitled to speculate any page
-			 * with a TLB mapping, so here we kill the
-			 * mapping then flush the page along a special
-			 * flush only alias mapping. This guarantees that
-			 * the page is no-longer in the cache for any
-			 * process and nor may it be speculatively read
-			 * in (until the user or kernel specifically
-			 * accesses it, of course)
+			 * Software is allowed to have any number
+			 * of private mappings to a page.
 			 */
-			for (i = 0; i < nr; i++)
-				flush_tlb_page(vma, addr + i * PAGE_SIZE);
-			if (old_addr == 0 || (old_addr & (SHM_COLOUR - 1))
-					!= (addr & (SHM_COLOUR - 1))) {
-				for (i = 0; i < nr; i++)
-					__flush_cache_page(vma,
-						addr + i * PAGE_SIZE,
-						(pfn + i) * PAGE_SIZE);
-				/*
-				 * Software is allowed to have any number
-				 * of private mappings to a page.
-				 */
-				if (!(vma->vm_flags & VM_SHARED))
-					continue;
-				if (old_addr)
-					pr_err("INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %pD\n",
-						old_addr, addr, vma->vm_file);
-				if (nr == folio_nr_pages(folio))
-					old_addr = addr;
-			}
+			if (!(vma->vm_flags & VM_SHARED))
+				continue;
+			if (old_addr)
+				pr_err("INEQUIVALENT ALIASES 0x%lx and 0x%lx in file %pD\n",
+					old_addr, addr, vma->vm_file);
+			if (nr == folio_nr_pages(folio))
+				old_addr = addr;
 		}
 		WARN_ON(++count == 4096);
 	}
@@ -591,35 +569,22 @@  extern void purge_kernel_dcache_page_asm(unsigned long);
 extern void clear_user_page_asm(void *, unsigned long);
 extern void copy_user_page_asm(void *, void *, unsigned long);
 
-void flush_kernel_dcache_page_addr(const void *addr)
-{
-	unsigned long flags;
-
-	flush_kernel_dcache_page_asm(addr);
-	purge_tlb_start(flags);
-	pdtlb(SR_KERNEL, addr);
-	purge_tlb_end(flags);
-}
-EXPORT_SYMBOL(flush_kernel_dcache_page_addr);
-
 static void flush_cache_page_if_present(struct vm_area_struct *vma,
-	unsigned long vmaddr, unsigned long pfn)
+	unsigned long vmaddr)
 {
-	bool needs_flush = false;
 	pte_t *ptep;
+	unsigned long pfn;
 
-	/*
-	 * The pte check is racy and sometimes the flush will trigger
-	 * a non-access TLB miss. Hopefully, the page has already been
-	 * flushed.
-	 */
 	ptep = get_ptep(vma->vm_mm, vmaddr);
-	if (ptep) {
-		needs_flush = pte_needs_flush(*ptep);
-		pte_unmap(ptep);
-	}
-	if (needs_flush)
-		flush_cache_page(vma, vmaddr, pfn);
+	if (!ptep)
+		return;
+
+	pfn = pte_pfn(*ptep);
+	if (WARN_ON(!pfn_valid(pfn)))
+		return;
+
+	__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
+	pte_unmap(ptep);
 }
 
 void copy_user_highpage(struct page *to, struct page *from,
@@ -629,7 +594,7 @@  void copy_user_highpage(struct page *to, struct page *from,
 
 	kfrom = kmap_local_page(from);
 	kto = kmap_local_page(to);
-	flush_cache_page_if_present(vma, vaddr, page_to_pfn(from));
+	__flush_cache_page(vma, vaddr, PFN_PHYS(page_to_pfn(from)));
 	copy_page_asm(kto, kfrom);
 	kunmap_local(kto);
 	kunmap_local(kfrom);
@@ -638,7 +603,7 @@  void copy_user_highpage(struct page *to, struct page *from,
 void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
 		unsigned long user_vaddr, void *dst, void *src, int len)
 {
-	flush_cache_page_if_present(vma, user_vaddr, page_to_pfn(page));
+	__flush_cache_page(vma, user_vaddr, PFN_PHYS(page_to_pfn(page)));
 	memcpy(dst, src, len);
 	flush_kernel_dcache_range_asm((unsigned long)dst, (unsigned long)dst + len);
 }
@@ -646,7 +611,7 @@  void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
 void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
 		unsigned long user_vaddr, void *dst, void *src, int len)
 {
-	flush_cache_page_if_present(vma, user_vaddr, page_to_pfn(page));
+	__flush_cache_page(vma, user_vaddr, PFN_PHYS(page_to_pfn(page)));
 	memcpy(dst, src, len);
 }
 
@@ -681,32 +646,10 @@  int __flush_tlb_range(unsigned long sid, unsigned long start,
 
 static void flush_cache_pages(struct vm_area_struct *vma, unsigned long start, unsigned long end)
 {
-	unsigned long addr, pfn;
-	pte_t *ptep;
+	unsigned long addr;
 
-	for (addr = start; addr < end; addr += PAGE_SIZE) {
-		bool needs_flush = false;
-		/*
-		 * The vma can contain pages that aren't present. Although
-		 * the pte search is expensive, we need the pte to find the
-		 * page pfn and to check whether the page should be flushed.
-		 */
-		ptep = get_ptep(vma->vm_mm, addr);
-		if (ptep) {
-			needs_flush = pte_needs_flush(*ptep);
-			pfn = pte_pfn(*ptep);
-			pte_unmap(ptep);
-		}
-		if (needs_flush) {
-			if (parisc_requires_coherency()) {
-				flush_user_cache_page(vma, addr);
-			} else {
-				if (WARN_ON(!pfn_valid(pfn)))
-					return;
-				__flush_cache_page(vma, addr, PFN_PHYS(pfn));
-			}
-		}
-	}
+	for (addr = start; addr < end; addr += PAGE_SIZE)
+		flush_cache_page_if_present(vma, addr);
 }
 
 static inline unsigned long mm_total_size(struct mm_struct *mm)
@@ -766,12 +709,7 @@  void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned
 
 void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
 {
-	if (WARN_ON(!pfn_valid(pfn)))
-		return;
-	if (parisc_requires_coherency())
-		flush_user_cache_page(vma, vmaddr);
-	else
-		__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
+	__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
 }
 
 void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr)
@@ -779,34 +717,29 @@  void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned lon
 	if (!PageAnon(page))
 		return;
 
-	if (parisc_requires_coherency()) {
-		if (vma->vm_flags & VM_SHARED)
-			flush_data_cache();
-		else
-			flush_user_cache_page(vma, vmaddr);
-		return;
-	}
-
-	flush_tlb_page(vma, vmaddr);
-	preempt_disable();
-	flush_dcache_page_asm(page_to_phys(page), vmaddr);
-	preempt_enable();
+	__flush_cache_page(vma, vmaddr, PFN_PHYS(page_to_pfn(page)));
 }
 
 void flush_kernel_vmap_range(void *vaddr, int size)
 {
 	unsigned long start = (unsigned long)vaddr;
 	unsigned long end = start + size;
+	unsigned long addr;
+
+	/* Remove TLB entries for range */
+	flush_tlb_kernel_range(start, end);
 
 	if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
 	    (unsigned long)size >= parisc_cache_flush_threshold) {
-		flush_tlb_kernel_range(start, end);
 		flush_data_cache();
 		return;
 	}
 
-	flush_kernel_dcache_range_asm(start, end);
-	flush_tlb_kernel_range(start, end);
+	/* Use tmpalias flush to ensure no lines remain in data cache */
+	preempt_disable();
+	for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE)
+		flush_dcache_page_asm(__pa(addr), addr);
+	preempt_enable();
 }
 EXPORT_SYMBOL(flush_kernel_vmap_range);
 
@@ -814,19 +747,25 @@  void invalidate_kernel_vmap_range(void *vaddr, int size)
 {
 	unsigned long start = (unsigned long)vaddr;
 	unsigned long end = start + size;
+	unsigned long addr;
 
 	/* Ensure DMA is complete */
 	asm_syncdma();
 
+	/* Remove TLB entries for range */
+	flush_tlb_kernel_range(start, end);
+
 	if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
 	    (unsigned long)size >= parisc_cache_flush_threshold) {
-		flush_tlb_kernel_range(start, end);
 		flush_data_cache();
 		return;
 	}
 
-	purge_kernel_dcache_range_asm(start, end);
-	flush_tlb_kernel_range(start, end);
+	/* Use tmpalias purge to ensure no lines remain in data cache */
+	preempt_disable();
+	for (addr = start & PAGE_MASK; addr < end; addr += PAGE_SIZE)
+		purge_dcache_page_asm(__pa(addr), addr);
+	preempt_enable();
 }
 EXPORT_SYMBOL(invalidate_kernel_vmap_range);