mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-03 00:54:09 +08:00
0bfbc914d9
* Support for byte/half-word compare-and-exchange, emulated via LR/SC loops. * Support for Rust. * Support for Zihintpause in hwprobe. * Support for the PR_RISCV_SET_ICACHE_FLUSH_CTX prctl(). * Support for lockless lockrefs. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEKzw3R0RoQ7JKlDp6LhMZ81+7GIkFAmZN/hcTHHBhbG1lckBk YWJiZWx0LmNvbQAKCRAuExnzX7sYiVrGEACUT3gsbTx1q7fa11iQNxOjVkpl66Qn 7+kI+V9xt5+GuH2EjJk6AsSNHPKeQ8totbSTA8AZjINFvgVjXslN+DPpcjCFKvnh NN5/Lyd64X0PZMsxGWlN9SHTFWf2b7lalCnY51BlX/IpBbHWc/no9XUsPSVixx6u 9q+JoS3D1DDV92nGcA/UK9ICCsDcf4omWgZW7KbjnVWnuY9jt4ctTy11jtF2RM9R Z9KAWh0RqPzjz0vNbBBf9Iw7E4jt/Px6HDYPfZAiE2dVsCTHjdsC7TcGRYXzKt6F 4q9zg8kzwvUG5GaBl7/XprXO1vaeOUmPcTVoE7qlRkSdkknRH/iBz1P4hk+r0fze f+h5ZUV/oJP7vDb+vHm/BExtGufgLuJ2oMA2Bp9qI17EMcMsGiRMt7DsBMEafWDk bNrFcJdqqYBz6HxfTwzNH5ErxfS/59PuwYl913BTSOH//raCZCFXOfyrSICH7qXd UFOLLmBpMuApLa8ayFeI9Mp3flWfbdQHR52zLRLiUvlpWNEDKrNQN417juVwTXF0 DYkjJDhFPLfFOr/sJBboftOMOUdA9c/CJepY9o4kPvBXUvPtRHN1jdXDNSCVDZRb nErnsJ9rv0PzfxQU7Xjhd2QmCMeMlbCQDpXAKKETyyimpTbgF33rovN0i5ixX3m4 KG6RvKDubOzZdA== =YLoD -----END PGP SIGNATURE----- Merge tag 'riscv-for-linus-6.10-mw1' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux Pull RISC-V updates from Palmer Dabbelt: - Add byte/half-word compare-and-exchange, emulated via LR/SC loops - Support for Rust - Support for Zihintpause in hwprobe - Add PR_RISCV_SET_ICACHE_FLUSH_CTX prctl() - Support lockless lockrefs * tag 'riscv-for-linus-6.10-mw1' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux: (42 commits) riscv: defconfig: Enable CONFIG_CLK_SOPHGO_CV1800 riscv: select ARCH_HAS_FAST_MULTIPLIER riscv: mm: still create swiotlb buffer for kmalloc() bouncing if required riscv: Annotate pgtable_l{4,5}_enabled with __ro_after_init riscv: Remove redundant CONFIG_64BIT from pgtable_l{4,5}_enabled riscv: mm: Always use an ASID to flush mm contexts riscv: mm: Preserve global TLB entries when switching contexts riscv: mm: Make asid_bits a local variable riscv: mm: Use a fixed layout for the MM context ID riscv: mm: Introduce cntx2asid/cntx2version helper macros riscv: Avoid TLB flush loops when affected by SiFive CIP-1200 riscv: Apply SiFive CIP-1200 workaround to single-ASID sfence.vma riscv: mm: Combine the SMP and UP TLB flush code riscv: Only send remote fences when some other CPU is online riscv: mm: Broadcast kernel TLB flushes only when needed riscv: Use IPIs for remote cache/TLB flushes by default riscv: Factor out page table TLB synchronization riscv: Flush the instruction cache during SMP bringup riscv: hwprobe: export Zihintpause ISA extension riscv: misaligned: remove CONFIG_RISCV_M_MODE specific code ...
206 lines
5.0 KiB
C
206 lines
5.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/mm.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <asm/sbi.h>
|
|
#include <asm/mmu_context.h>
|
|
|
|
/*
|
|
* Flush entire TLB if number of entries to be flushed is greater
|
|
* than the threshold below.
|
|
*/
|
|
unsigned long tlb_flush_all_threshold __read_mostly = 64;
|
|
|
|
static void local_flush_tlb_range_threshold_asid(unsigned long start,
|
|
unsigned long size,
|
|
unsigned long stride,
|
|
unsigned long asid)
|
|
{
|
|
unsigned long nr_ptes_in_range = DIV_ROUND_UP(size, stride);
|
|
int i;
|
|
|
|
if (nr_ptes_in_range > tlb_flush_all_threshold) {
|
|
local_flush_tlb_all_asid(asid);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < nr_ptes_in_range; ++i) {
|
|
local_flush_tlb_page_asid(start, asid);
|
|
start += stride;
|
|
}
|
|
}
|
|
|
|
static inline void local_flush_tlb_range_asid(unsigned long start,
|
|
unsigned long size, unsigned long stride, unsigned long asid)
|
|
{
|
|
if (size <= stride)
|
|
local_flush_tlb_page_asid(start, asid);
|
|
else if (size == FLUSH_TLB_MAX_SIZE)
|
|
local_flush_tlb_all_asid(asid);
|
|
else
|
|
local_flush_tlb_range_threshold_asid(start, size, stride, asid);
|
|
}
|
|
|
|
/* Flush a range of kernel pages without broadcasting */
|
|
void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
|
{
|
|
local_flush_tlb_range_asid(start, end - start, PAGE_SIZE, FLUSH_TLB_NO_ASID);
|
|
}
|
|
|
|
static void __ipi_flush_tlb_all(void *info)
|
|
{
|
|
local_flush_tlb_all();
|
|
}
|
|
|
|
void flush_tlb_all(void)
|
|
{
|
|
if (num_online_cpus() < 2)
|
|
local_flush_tlb_all();
|
|
else if (riscv_use_sbi_for_rfence())
|
|
sbi_remote_sfence_vma_asid(NULL, 0, FLUSH_TLB_MAX_SIZE, FLUSH_TLB_NO_ASID);
|
|
else
|
|
on_each_cpu(__ipi_flush_tlb_all, NULL, 1);
|
|
}
|
|
|
|
struct flush_tlb_range_data {
|
|
unsigned long asid;
|
|
unsigned long start;
|
|
unsigned long size;
|
|
unsigned long stride;
|
|
};
|
|
|
|
static void __ipi_flush_tlb_range_asid(void *info)
|
|
{
|
|
struct flush_tlb_range_data *d = info;
|
|
|
|
local_flush_tlb_range_asid(d->start, d->size, d->stride, d->asid);
|
|
}
|
|
|
|
static void __flush_tlb_range(const struct cpumask *cmask, unsigned long asid,
|
|
unsigned long start, unsigned long size,
|
|
unsigned long stride)
|
|
{
|
|
unsigned int cpu;
|
|
|
|
if (cpumask_empty(cmask))
|
|
return;
|
|
|
|
cpu = get_cpu();
|
|
|
|
/* Check if the TLB flush needs to be sent to other CPUs. */
|
|
if (cpumask_any_but(cmask, cpu) >= nr_cpu_ids) {
|
|
local_flush_tlb_range_asid(start, size, stride, asid);
|
|
} else if (riscv_use_sbi_for_rfence()) {
|
|
sbi_remote_sfence_vma_asid(cmask, start, size, asid);
|
|
} else {
|
|
struct flush_tlb_range_data ftd;
|
|
|
|
ftd.asid = asid;
|
|
ftd.start = start;
|
|
ftd.size = size;
|
|
ftd.stride = stride;
|
|
on_each_cpu_mask(cmask, __ipi_flush_tlb_range_asid, &ftd, 1);
|
|
}
|
|
|
|
put_cpu();
|
|
}
|
|
|
|
static inline unsigned long get_mm_asid(struct mm_struct *mm)
|
|
{
|
|
return cntx2asid(atomic_long_read(&mm->context.id));
|
|
}
|
|
|
|
void flush_tlb_mm(struct mm_struct *mm)
|
|
{
|
|
__flush_tlb_range(mm_cpumask(mm), get_mm_asid(mm),
|
|
0, FLUSH_TLB_MAX_SIZE, PAGE_SIZE);
|
|
}
|
|
|
|
void flush_tlb_mm_range(struct mm_struct *mm,
|
|
unsigned long start, unsigned long end,
|
|
unsigned int page_size)
|
|
{
|
|
__flush_tlb_range(mm_cpumask(mm), get_mm_asid(mm),
|
|
start, end - start, page_size);
|
|
}
|
|
|
|
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
|
|
{
|
|
__flush_tlb_range(mm_cpumask(vma->vm_mm), get_mm_asid(vma->vm_mm),
|
|
addr, PAGE_SIZE, PAGE_SIZE);
|
|
}
|
|
|
|
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
|
unsigned long end)
|
|
{
|
|
unsigned long stride_size;
|
|
|
|
if (!is_vm_hugetlb_page(vma)) {
|
|
stride_size = PAGE_SIZE;
|
|
} else {
|
|
stride_size = huge_page_size(hstate_vma(vma));
|
|
|
|
/*
|
|
* As stated in the privileged specification, every PTE in a
|
|
* NAPOT region must be invalidated, so reset the stride in that
|
|
* case.
|
|
*/
|
|
if (has_svnapot()) {
|
|
if (stride_size >= PGDIR_SIZE)
|
|
stride_size = PGDIR_SIZE;
|
|
else if (stride_size >= P4D_SIZE)
|
|
stride_size = P4D_SIZE;
|
|
else if (stride_size >= PUD_SIZE)
|
|
stride_size = PUD_SIZE;
|
|
else if (stride_size >= PMD_SIZE)
|
|
stride_size = PMD_SIZE;
|
|
else
|
|
stride_size = PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
__flush_tlb_range(mm_cpumask(vma->vm_mm), get_mm_asid(vma->vm_mm),
|
|
start, end - start, stride_size);
|
|
}
|
|
|
|
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
|
{
|
|
__flush_tlb_range(cpu_online_mask, FLUSH_TLB_NO_ASID,
|
|
start, end - start, PAGE_SIZE);
|
|
}
|
|
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
void flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start,
|
|
unsigned long end)
|
|
{
|
|
__flush_tlb_range(mm_cpumask(vma->vm_mm), get_mm_asid(vma->vm_mm),
|
|
start, end - start, PMD_SIZE);
|
|
}
|
|
#endif
|
|
|
|
bool arch_tlbbatch_should_defer(struct mm_struct *mm)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *batch,
|
|
struct mm_struct *mm,
|
|
unsigned long uaddr)
|
|
{
|
|
cpumask_or(&batch->cpumask, &batch->cpumask, mm_cpumask(mm));
|
|
}
|
|
|
|
void arch_flush_tlb_batched_pending(struct mm_struct *mm)
|
|
{
|
|
flush_tlb_mm(mm);
|
|
}
|
|
|
|
void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
|
|
{
|
|
__flush_tlb_range(&batch->cpumask, FLUSH_TLB_NO_ASID, 0,
|
|
FLUSH_TLB_MAX_SIZE, PAGE_SIZE);
|
|
cpumask_clear(&batch->cpumask);
|
|
}
|