ARM64架构:为啥子刷icache之后要发IPI中断?

昨天在笨叔第一季VIP群里,有人问了一个很有意思和深度的问题:

ARM64架构:为啥子刷icache之后要发IPI中断?_第1张图片

这位同学问的问题,是关于Linux ARM64内核的flush_icache_range()函数,它是linux5.0/arch/arm64/include/asm/cacheflush.h文件里。

ARM64架构:为啥子刷icache之后要发IPI中断?_第2张图片

第87行的__flush_icache_range()函数会调用“ic ivau”指令来无效[start, end]地址空间对应的指令高速缓存。这位同学感觉比较疑惑的是,为什么第93~94行需要触发IPI中断呢?而且这个IPI中断的回调函数是一个空函数do_nothing()。

从第89·~91行的英文注释可知,给所有的CPU发送一个IPI中断可以让这些CPU经历一次上下文同步事件,从而强迫这些CPU重新从指令高速缓存中取指令。

其实,这个flush_icache_range()函数涉及到ARM64体系架构两个非常重要的知识点,一个是ic指令高速缓存维护指令另外一个是上下文同步事件(context synchronizationevent)

1. ic指令 - 指令高速缓存维护指令

ARM架构中的高速缓存可以分成指令高速缓存、数据高速缓存以及统一高速缓存(unified cache)。ic指令是用来指令高速缓存维护工作,对于指令高速缓存,一般只能做无效操作。dc指令是用于数据高速缓存维护工作,对于数据高速缓存,可以是clean、无效、清零三种操作。

ARM64的高速缓存维护指令有一个特点,它可以指定共享域,包括内部共享域(inner share domain)或者外部共享域(outer share domain)。指定共享域的一个好处就是,它可以在指定共享域里发送广播(boardcast),让共享域里的CPU核心把相应的本地高速缓存给无效或者clean掉,完成之后,这些CPU核心会回应一个ACK信号。ARM64架构规定,执行ic/dc指令的那个CPU必须执行dsb指令来等待这些CPU回应ACK信号。当dsb指令执行完成,那么说明,在指定共享域里所有的CPU都完成了 相应的高速缓存操作了。大家需要注意,这个boardcast不是IPI中断。

2. 上下文同步事件

上下文同步事件包括:

Ø  发生一个异常(exception)。

Ø  从一个异常返回。

Ø  执行了ISB指令。

发生上下文同步事件产生的影响包括:

Ø  在上下文同步事件发生时挂起的所有未屏蔽中断都会在上下文同步事件之后的第一个指令执行之前处理。

Ø  在触发上下文同步事件的指令后面的所有指令不会被执行,直到上下文同步事件处理完成。

Ø  在上下文同步事件之前完成的无效TLB、指令高速缓存以及分支预测器的操作,都会影响在上下文同步事件后面出现的指令。例如,在上下文同步事件之前完成了无效指令高速缓存的操作,那么在上下文同步事件之后,CPU会从指令高速缓存中重新取指令,相当于把流水线之前预取的指令给清空了。

我们以flush指令cache为例,当某个CPU(例如CPU0)执行了无效指令高速缓存指令,无效指令高速缓存会发送广播到指定共享域,指定共享域中的CPU收到广播之后也会无效本地对应的指令高速缓存,完成之后回应一个应答信号。CPU0通过执行DSB指令来等待其它CPU的回应信号。如果无效指令高速缓存完成之后,CPU想从指令高速缓存中重新取指令,那么需要执行一条ISB指令。ISB指令不会发送或者等待广播,也不会影响其他CPU的执行。如果其他CPU也想从指令高速缓存中重新获取指令的话,那么就需要各自执行ISB指令。

flush_icache_range()函数使用了一个高级的技巧,就是利用ARMv8架构的上下文同步事件来替代执行ISB指令。触发IPI中断,相当于一个异常的触发和返回,触发了上下文同步事件,而且IPI中断是给所有online CPU都发IPI中断。在上下文同步事件之前完成的无效TLB、指令高速缓存以及分支预测器的操作,都会影响在上下文同步事件后面出现的指令。例如,在上下文同步事件之前执行了无效指令高速缓存的操作,那么在上下文同步事件之后,CPU会从指令高速缓存中重新取指令,相当于把流水线之前预取的指令给清空了。这里通过给所有CPU发送IPI中断,相当于所有CPU触发了一个上下文同步事件。

关于上下文同步事件 是否会 冲刷流水线然后让CPU从指令cache重新取指令,笨叔认真阅读了ARMv8.6手册,没有找到很明确的说法,不过在ARMv8.6手册的Glossary-8112页中找到下面这句话:

这句话的意思是说:在上下文同步事件之前完成的无效TLB、指令高速缓存以及分支预测器的操作,都会影响在上下文同步事件后面出现的指令。

这句话说了会影响“affect”,但是没有具体说怎么影响。所以,有时候我觉的ARMv8的手册讲的很晦涩,也许是故意不想说清除。所以,我的理解是,如果在上下文同步事件触发之前执行了无效指令高速缓存,那么在上下文同步事件之后的指令,都会从指令高速缓存中重新取指令。

在ARM公司提交的社区补丁上说了,这种情况下会:flush the pipeline so that instructions fetched into pipeline before the I-cache invalidation are discarded.

ARM64架构:为啥子刷icache之后要发IPI中断?_第3张图片

你说吧,手册上不写清楚,我们外人咋知道呢?对吧

所以,这个flush_icache_range()函数里,

“无效icache + dsb + IPI中断”

等同于如下操作:

“无效icache + dsb + 共享域CPU都执行ISB指令” 

总之,上下文同步事件不等同于一定会flush the pipeline和从icache重新取指令,而是“无效icache + 上下文同步事件” 才有这种效果。

全球首个手把手和你解读ARMv8芯片手册的视频节目,全球首个ARM64实验课程,几十个实验等你来撸!

你可能感兴趣的:(linux)