| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Alpha TLB shootdown helpers | |
| * | |
| * Copyright (C) 2025 Magnus Lindholm <linmag7@gmail.com> | |
| * | |
| * Alpha-specific TLB flush helpers that cannot be expressed purely | |
| * as inline functions. | |
| * | |
| * These helpers provide combined MM context handling (ASN rollover) | |
| * and immediate TLB invalidation for page migration and memory | |
| * compaction paths, where lazy shootdowns are insufficient. | |
| */ | |
| #include <linux/mm.h> | |
| #include <linux/smp.h> | |
| #include <linux/sched.h> | |
| #include <asm/tlbflush.h> | |
| #include <asm/pal.h> | |
| #include <asm/mmu_context.h> | |
| #define asn_locked() (cpu_data[smp_processor_id()].asn_lock) | |
| /* | |
| * Migration/compaction helper: combine mm context (ASN) handling with an | |
| * immediate per-page TLB invalidate and (for exec) an instruction barrier. | |
| * | |
| * This mirrors the SMP combined IPI handler semantics, but runs locally on UP. | |
| */ | |
| #ifndef CONFIG_SMP | |
| void migrate_flush_tlb_page(struct vm_area_struct *vma, | |
| unsigned long addr) | |
| { | |
| struct mm_struct *mm = vma->vm_mm; | |
| int tbi_type = (vma->vm_flags & VM_EXEC) ? 3 : 2; | |
| /* | |
| * First do the mm-context side: | |
| * If we're currently running this mm, reload a fresh context ASN. | |
| * Otherwise, mark context invalid. | |
| * | |
| * On UP, this is mostly about matching the SMP semantics and ensuring | |
| * exec/i-cache tagging assumptions hold when compaction migrates pages. | |
| */ | |
| if (mm == current->active_mm) | |
| flush_tlb_current(mm); | |
| else | |
| flush_tlb_other(mm); | |
| /* | |
| * Then do the immediate translation kill for this VA. | |
| * For exec mappings, order instruction fetch after invalidation. | |
| */ | |
| tbi(tbi_type, addr); | |
| } | |
| #else | |
| struct tlb_mm_and_addr { | |
| struct mm_struct *mm; | |
| unsigned long addr; | |
| int tbi_type; /* 2 = DTB, 3 = ITB+DTB */ | |
| }; | |
| static void ipi_flush_mm_and_page(void *x) | |
| { | |
| struct tlb_mm_and_addr *d = x; | |
| /* Part 1: mm context side (Alpha uses ASN/context as a key mechanism). */ | |
| if (d->mm == current->active_mm && !asn_locked()) | |
| __load_new_mm_context(d->mm); | |
| else | |
| flush_tlb_other(d->mm); | |
| /* Part 2: immediate per-VA invalidation on this CPU. */ | |
| tbi(d->tbi_type, d->addr); | |
| } | |
| void migrate_flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) | |
| { | |
| struct mm_struct *mm = vma->vm_mm; | |
| struct tlb_mm_and_addr d = { | |
| .mm = mm, | |
| .addr = addr, | |
| .tbi_type = (vma->vm_flags & VM_EXEC) ? 3 : 2, | |
| }; | |
| /* | |
| * One synchronous rendezvous: every CPU runs ipi_flush_mm_and_page(). | |
| * This is the "combined" version of flush_tlb_mm + per-page invalidate. | |
| */ | |
| preempt_disable(); | |
| on_each_cpu(ipi_flush_mm_and_page, &d, 1); | |
| /* | |
| * mimic flush_tlb_mm()'s mm_users<=1 optimization. | |
| */ | |
| if (atomic_read(&mm->mm_users) <= 1) { | |
| int cpu, this_cpu; | |
| this_cpu = smp_processor_id(); | |
| for (cpu = 0; cpu < NR_CPUS; cpu++) { | |
| if (!cpu_online(cpu) || cpu == this_cpu) | |
| continue; | |
| if (READ_ONCE(mm->context[cpu])) | |
| WRITE_ONCE(mm->context[cpu], 0); | |
| } | |
| } | |
| preempt_enable(); | |
| } | |
| #endif |