介绍linux中riscv架构下,多核sfence的做法。
在RISCV架构下,仅定义了sfence.vma指令用来刷新当前CPU下的TLB。然而在多核系统中,如果一个hart修改了页表,执行sfence.vma之后,仅仅是刷新了当前hart的TLB,无法刷新其他hart的TLB。所以在linux必须要有一种机制来刷新remote hart的TLB,本文来介绍linux是如何做到的。
在linux源码里arch/riscv/mm/tlbflush.c
中
1 | static void __sbi_tlb_flush_range(struct mm_struct *mm, unsigned long start, |
这是linux中刷新tlb会调用的底层函数,其中:
1 | broadcast = cpumask_any_but(cmask, cpuid) < nr_cpu_ids; |
nr_cpu_ids: 当前所有可处于联机状态的CPU总数。
cpumask_any_but:TODO??
从这一行可以看出当tlbflush只需要刷新自己cpu时,broadcast
为0,直接调用local_flush
,也就是直接调用sfence指令来刷新本cpu的TLB。而当要刷新其他cpu时,broadcast
为1,则需要调用SBI call,让SBI来通知其他cpu。
在linux源码里arch/riscv/kernel/sbi.c
中调用SBI call。
1 | /** |
在opensbi源码里,lib/sbi/sbi_ecall_replace.c
中会处理这个call。
1 | case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA: |
在opensbi源码里,lib/sbi/sbi_tlb.c
中调用IPI。
1 | int sbi_tlb_request(ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo) |
在opensbi源码里,lib/sbi/sbi_ipi.c
中调用device的IPI。
1 | static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid, |
最后应该会调到,lib/utils/ipi/aclint_mswi.c
,最终往msip里写值通知另一个hart。
1 | static void mswi_ipi_send(u32 target_hart) |
总结来看,linux是采用SBI call + IPI
的方式来实现remote sfence的。
Reference