mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
alpha: fix user-space corruption during memory compaction
Alpha systems can suffer sporadic user-space crashes and heap corruption when memory compaction is enabled. Symptoms include SIGSEGV, glibc allocator failures (e.g. "unaligned tcache chunk"), and compiler internal errors. The failures disappear when compaction is disabled or when using global TLB invalidation. The root cause is insufficient TLB shootdown during page migration. Alpha relies on ASN-based MM context rollover for instruction cache coherency, but this alone is not sufficient to prevent stale data or instruction translations from surviving migration. Fix this by introducing a migration-specific helper that combines: - MM context invalidation (ASN rollover), - immediate per-CPU TLB invalidation (TBI), - synchronous cross-CPU shootdown when required. The helper is used only by migration/compaction paths to avoid changing global TLB semantics. Additionally, update flush_tlb_other(), pte_clear(), to use READ_ONCE()/WRITE_ONCE() for correct SMP memory ordering. This fixes observed crashes on both UP and SMP Alpha systems. Reviewed-by: Ivan Kokshaysky <ink@unseen.parts> Tested-by: Matoro Mahri <matoro_mailinglist_kernel@matoro.tk> Tested-by: Michael Cree <mcree@orcon.net.nz> Signed-off-by: Magnus Lindholm <linmag7@gmail.com> Link: https://lore.kernel.org/r/20260102173603.18247-2-linmag7@gmail.com Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#include <asm/processor.h> /* For TASK_SIZE */
|
||||
#include <asm/machvec.h>
|
||||
#include <asm/setup.h>
|
||||
#include <linux/page_table_check.h>
|
||||
|
||||
struct mm_struct;
|
||||
struct vm_area_struct;
|
||||
@@ -183,6 +184,9 @@ extern inline void pud_set(pud_t * pudp, pmd_t * pmdp)
|
||||
{ pud_val(*pudp) = _PAGE_TABLE | ((((unsigned long) pmdp) - PAGE_OFFSET) << (32-PAGE_SHIFT)); }
|
||||
|
||||
|
||||
extern void migrate_flush_tlb_page(struct vm_area_struct *vma,
|
||||
unsigned long addr);
|
||||
|
||||
extern inline unsigned long
|
||||
pmd_page_vaddr(pmd_t pmd)
|
||||
{
|
||||
@@ -202,7 +206,7 @@ extern inline int pte_none(pte_t pte) { return !pte_val(pte); }
|
||||
extern inline int pte_present(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
|
||||
extern inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
|
||||
{
|
||||
pte_val(*ptep) = 0;
|
||||
WRITE_ONCE(pte_val(*ptep), 0);
|
||||
}
|
||||
|
||||
extern inline int pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
|
||||
@@ -264,6 +268,33 @@ extern inline pte_t * pte_offset_kernel(pmd_t * dir, unsigned long address)
|
||||
|
||||
extern pgd_t swapper_pg_dir[1024];
|
||||
|
||||
#ifdef CONFIG_COMPACTION
|
||||
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
|
||||
|
||||
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
|
||||
unsigned long address,
|
||||
pte_t *ptep)
|
||||
{
|
||||
pte_t pte = READ_ONCE(*ptep);
|
||||
|
||||
pte_clear(mm, address, ptep);
|
||||
return pte;
|
||||
}
|
||||
|
||||
#define __HAVE_ARCH_PTEP_CLEAR_FLUSH
|
||||
|
||||
static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
|
||||
unsigned long addr, pte_t *ptep)
|
||||
{
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
pte_t pte = ptep_get_and_clear(mm, addr, ptep);
|
||||
|
||||
page_table_check_pte_clear(mm, pte);
|
||||
migrate_flush_tlb_page(vma, addr);
|
||||
return pte;
|
||||
}
|
||||
|
||||
#endif
|
||||
/*
|
||||
* The Alpha doesn't have any external MMU info: the kernel page
|
||||
* tables contain all the necessary information.
|
||||
|
||||
Reference in New Issue
Block a user