mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 10:17:47 +07:00
01ab605704
The increased use of pdtlb/pitlb instructions seemed to increase the frequency of random segmentation faults building packages. Further, we had a number of cases where TLB inserts would repeatedly fail and all forward progress would stop. The Haskell ghc package caused a lot of trouble in this area. The final indication of a race in pte handling was this syslog entry on sibaris (C8000): swap_free: Unused swap offset entry 00000004 BUG: Bad page map in process mysqld pte:00000100 pmd:019bbec5 addr:00000000ec464000 vm_flags:00100073 anon_vma:0000000221023828 mapping: (null) index:ec464 CPU: 1 PID: 9176 Comm: mysqld Not tainted 4.0.0-2-parisc64-smp #1 Debian 4.0.5-1 Backtrace: [<0000000040173eb0>] show_stack+0x20/0x38 [<0000000040444424>] dump_stack+0x9c/0x110 [<00000000402a0d38>] print_bad_pte+0x1a8/0x278 [<00000000402a28b8>] unmap_single_vma+0x3d8/0x770 [<00000000402a4090>] zap_page_range+0xf0/0x198 [<00000000402ba2a4>] SyS_madvise+0x404/0x8c0 Note that the pte value is 0 except for the accessed bit 0x100. This bit shouldn't be set without the present bit. It should be noted that the madvise system call is probably a trigger for many of the random segmentation faults. In looking at the kernel code, I found the following problems: 1) The pte_clear define didn't take TLB lock when clearing a pte. 2) We didn't test pte present bit inside lock in exception support. 3) The pte and tlb locks needed to merged in order to ensure consistency between page table and TLB. This also has the effect of serializing TLB broadcasts on SMP systems. The attached change implements the above and a few other tweaks to try to improve performance. Based on the timing code, TLB purges are very slow (e.g., ~ 209 cycles per page on rp3440). Thus, I think it beneficial to test the split_tlb variable to avoid duplicate purges. Probably, all PA 2.0 machines have combined TLBs. I dropped using __flush_tlb_range in flush_tlb_mm as I realized all applications and most threads have a stack size that is too large to make this useful. I added some comments to this effect. Since implementing 1 through 3, I haven't had any random segmentation faults on mx3210 (rp3440) in about one week of building code and running as a Debian buildd. Signed-off-by: John David Anglin <dave.anglin@bell.net> Cc: stable@vger.kernel.org # v3.18+ Signed-off-by: Helge Deller <deller@gmx.de>
92 lines
2.6 KiB
C
92 lines
2.6 KiB
C
#ifndef _PARISC_TLBFLUSH_H
|
|
#define _PARISC_TLBFLUSH_H
|
|
|
|
/* TLB flushing routines.... */
|
|
|
|
#include <linux/mm.h>
|
|
#include <linux/sched.h>
|
|
#include <asm/mmu_context.h>
|
|
|
|
|
|
/* This is for the serialisation of PxTLB broadcasts. At least on the
|
|
* N class systems, only one PxTLB inter processor broadcast can be
|
|
* active at any one time on the Merced bus. This tlb purge
|
|
* synchronisation is fairly lightweight and harmless so we activate
|
|
* it on all systems not just the N class.
|
|
|
|
* It is also used to ensure PTE updates are atomic and consistent
|
|
* with the TLB.
|
|
*/
|
|
extern spinlock_t pa_tlb_lock;
|
|
|
|
#define purge_tlb_start(flags) spin_lock_irqsave(&pa_tlb_lock, flags)
|
|
#define purge_tlb_end(flags) spin_unlock_irqrestore(&pa_tlb_lock, flags)
|
|
|
|
extern void flush_tlb_all(void);
|
|
extern void flush_tlb_all_local(void *);
|
|
|
|
#define smp_flush_tlb_all() flush_tlb_all()
|
|
|
|
int __flush_tlb_range(unsigned long sid,
|
|
unsigned long start, unsigned long end);
|
|
|
|
#define flush_tlb_range(vma, start, end) \
|
|
__flush_tlb_range((vma)->vm_mm->context, start, end)
|
|
|
|
#define flush_tlb_kernel_range(start, end) \
|
|
__flush_tlb_range(0, start, end)
|
|
|
|
/*
|
|
* flush_tlb_mm()
|
|
*
|
|
* The code to switch to a new context is NOT valid for processes
|
|
* which play with the space id's. Thus, we have to preserve the
|
|
* space and just flush the entire tlb. However, the compilers,
|
|
* dynamic linker, etc, do not manipulate space id's, so there
|
|
* could be a significant performance benefit in switching contexts
|
|
* and not flushing the whole tlb.
|
|
*/
|
|
|
|
static inline void flush_tlb_mm(struct mm_struct *mm)
|
|
{
|
|
BUG_ON(mm == &init_mm); /* Should never happen */
|
|
|
|
#if 1 || defined(CONFIG_SMP)
|
|
/* Except for very small threads, flushing the whole TLB is
|
|
* faster than using __flush_tlb_range. The pdtlb and pitlb
|
|
* instructions are very slow because of the TLB broadcast.
|
|
* It might be faster to do local range flushes on all CPUs
|
|
* on PA 2.0 systems.
|
|
*/
|
|
flush_tlb_all();
|
|
#else
|
|
/* FIXME: currently broken, causing space id and protection ids
|
|
* to go out of sync, resulting in faults on userspace accesses.
|
|
* This approach needs further investigation since running many
|
|
* small applications (e.g., GCC testsuite) is faster on HP-UX.
|
|
*/
|
|
if (mm) {
|
|
if (mm->context != 0)
|
|
free_sid(mm->context);
|
|
mm->context = alloc_sid();
|
|
if (mm == current->active_mm)
|
|
load_context(mm->context);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static inline void flush_tlb_page(struct vm_area_struct *vma,
|
|
unsigned long addr)
|
|
{
|
|
unsigned long flags, sid;
|
|
|
|
sid = vma->vm_mm->context;
|
|
purge_tlb_start(flags);
|
|
mtsp(sid, 1);
|
|
pdtlb(addr);
|
|
if (unlikely(split_tlb))
|
|
pitlb(addr);
|
|
purge_tlb_end(flags);
|
|
}
|
|
#endif
|