ARM WFI和WFE指令

1. 前言

蜗蜗很早以前就知道有WFI和WFE这两个指令存在,但一直似懂非懂。最近准备研究CPU idle framework,由于WFI是让CPU进入idle状态的一种方法,就下决心把它们弄清楚。

WFI(Wait for interrupt)和WFE(Wait for event)是两个让ARM核进入low-power standby模式的指令,由ARM architecture定义,由ARM core实现。听着挺简单,但怎么会有两个指令?它们的区别是什么?使用场景是什么?深究起来,还挺有意思,例如:能想象WFE和spinlock的关系吗?

2. WFI和WFE

1)共同点

WFI和WFE的功能非常类似,以ARMv8-A为例(参考DDI0487A_d_armv8_arm.pdf的描述),主要是“将ARMv8-A PE(Processing Element, 处理单元)设置为low-power standby state”。

需要说明的是,ARM architecture并没有规定“low-power standby state”的具体形式,因而可以由ARM core自行发挥,根据ARM的建议,一般可以实现为standby(关闭clock、保持供电)、dormant、shutdown等等。但有个原则,不能造成内存一致性的问题。以Cortex-A57 ARM core为例,它把WFI和WFE实现为“put the core in a low-power state by disabling the clocks in the core while keeping the core powered up”,即我们通常所说的standby模式,保持供电,关闭clock。

2)不同点

那它们的区别体现在哪呢?主要体现进入和退出的方式上。

对WFI来说,执行WFI指令后,ARM core会立即进入low-power standby state,直到有WFI Wakeup events发生。

而WFE则稍微不同,执行WFE指令后,根据Event Register(一个单bit的寄存器,每个PE一个)的状态,有两种情况:如果Event Register为1,该指令会把它清零,然后执行完成(不会standby);如果Event Register为0,和WFI类似,进入low-power standby state,直到有WFE Wakeup events发生。

WFI wakeup event和WFE wakeup event可以分别让Core从WFI和WFE状态唤醒,这两类Event大部分相同,如任何的IRQ中断、FIQ中断等等,一些细微的差别,可以参考“DDI0487A_d_armv8_arm.pdf“的描述。而最大的不同是,WFE可以被任何PE上执行的SEV指令唤醒。

所谓的SEV指令,就是一个用来改变Event Register的指令,有两个:SEV会修改所有PE上的寄存器;SEVL,只修改本PE的寄存器值。下面让我们看看WFE这种特殊设计的使用场景。

3. 使用场景

1)WFI

WFI一般用于cpuidle。

2)WFE

WFE的一个典型使用场景,是用在spinlock中(可参考arch_spin_lock,对arm64来说,位于arm64/include/asm/spinlock.h中)。spinlock的功能,是在不同CPU core之间,保护共享资源。使用WFE的流程是:

a)资源空闲

b)Core1访问资源,acquire lock,获得资源

c)Core2访问资源,此时资源不空闲,执行WFE指令,让core进入low-power state

d)Core1释放资源,release lock,释放资源,同时执行SEV指令,唤醒Core2

e)Core2获得资源

以往的spinlock,在获得不到资源时,让Core进入busy loop,而通过插入WFE指令,可以节省功耗,也算是因祸(损失了性能)得福(降低了功耗)吧。

 

转自。蜗窝科技,www.wowotech.net。


评论:

lamaboy
2016-08-17 23:01
你好,,
请问 spin_lock  只是用来在不同CPU core之间,保护共享资源吗??

对于那些单cpu 的spin_lock   有什么不同之处呢??
回复
wowo
2016-08-18 14:54
@lamaboy:spin的意思,就是让某个CPU在原地打转,这样就可以避免它去访问共享资源。
对于单个CPU来说,它只能顺序执行(除非被中断、异常打断),因此没有spin的必要,也就没有spin_lock的概念。
单CPU需要做同步的话,就把自己的中断关掉就行了。
回复
randy
2016-09-01 19:15
@wowo:单核下spin lock也是有意义的:用于线程之间的同步时禁止内核抢占,保证被中断后仍然回到原先的线程中;用于线程上下文和中断上下文之间的同步时根据上半部和下半部分别关中断和关下半部
回复
linuxer
2016-09-01 19:45
@randy:我想wowo同学的本意是:在单核情况下,spin lock退化成了禁止抢占,其实已经脱离了spin lock的本意,因此,他才说“对于单个CPU来说,它只能顺序执行(除非被中断、异常打断),因此没有spin的必要”

我其实比较挑战的是他的另外一句话“单CPU需要做同步的话,就把自己的中断关掉就行了”,一言不合就使用关中断这种大杀器我是反对的,可以选择使用最适合的同步方法。
回复
wowo
2016-09-02 08:55
@randy:是的,randy和linuxer的说法比我说的“精细”多了,多谢~~
我的风格是比较直接,不太希望把话题延伸的太宽,大家多包涵哈~~~
回复
坚持到底
2015-11-17 11:32
arm64 smp 中,并没有直接在 arch_spin_unlock() 里使用 sev 来唤醒执行 wfe 的 CPU,请问楼主有没研究过执行 wfe 的 CPU 怎么被唤醒的?
回复
wowo
2015-11-17 13:45
@坚持到底:当前ARM Kernel的spin_lock是不是没有利用WFE节省功耗?
请教linuxer,ldaxrh让CPU进入低功耗吗?如果不会,现在仅仅用了busy loop的方式。
回复
坚持到底
2015-11-17 14:11
@wowo:static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned int tmp;

    asm volatile(
    "    sevl\n"
    "1:    wfe\n"
    "2:    ldaxr    %w0, %1\n"
    "    cbnz    %w0, 1b\n"
    "    stxr    %w0, %w2, %1\n"
    "    cbnz    %w0, 2b\n"
    : "=&r" (tmp), "+Q" (lock->lock)
    : "r" (1)
    : "cc", "memory");
}

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
    asm volatile(
    "    stlr    %w1, %0\n"
    : "=Q" (lock->lock) : "r" (0) : "memory");
}

我的 linux kernel  3.10.61,是有用到 wfe 的。
回复
wowo
2015-11-17 14:33
@坚持到底:这两个配合:
"    sevl\n"
    "1:    wfe\n"
wfe不会进入低功耗模式的。
回复
坚持到底
2015-11-17 14:58
@wowo:会的。

假设 cpu0 执行下面伪代码:

"    sevl\n"  //set event register 1
    "1:    wfe\n"  //clear event register 为 0,执行 wfe 完毕,不睡眠。
    "2:    ldaxr    %w0, %1\n" //tmp = lock->lock, 测试 lock->lock是否有 CPU 占用,1 为有 CPU 占用,0 为没 CPU 占用。假设此时已经有 CPU 占用被设置为 1, 则非零,跳转到 1b, wfe.
    "    cbnz    %w0, 1b\n"
回复
linuxer
2015-11-17 15:05
@坚持到底:当没有获取spinlock的时候,cpu core当然会调用wfe,等待其他cpu使用sev来唤醒自己。linux kernel的ARM64代码当然不会busy loop了,那太业余了。具体可以参考:
http://www.wowotech.net/kernel_synchronization/spinlock.html

在ARM64中,arch_spin_unlock并没有显示的调用sev来唤醒其他cpu,而是通过stlr指令完成的。在ARM ARM文档中有说:在执行store操作的时候,如果要操作的地址被标记为exclusive的,那么global monitor的状态会从exclusive access变成open access,同时会触发一个事件,唤醒wfe中的cpu。
回复
wowo
2015-11-17 15:42
@linuxer:嗯嗯,这就对了,我也一直怀疑stlr,去memory barrier文档中找了很久没找到信息,原来在spin lock中。

@坚持到底:抱歉误导您了啊,好在有大神在,哈哈。
回复
linuxer
2015-11-17 18:27
@wowo:大神不敢当啊,我还是喜欢华南区(深圳,珠海除外)首席xxx这样的称号,^_^
回复
坚持到底
2015-11-17 15:54
@linuxer:是的,感谢啊。在 6000 页的 pdf 中找到:

The ARMv8 architecture adds the acquire and release semantics to Load-Exclusive and Store-Exclusive instructions, which allows them to gain ordering acquire and/or release semantics.

The Load-Exclusive instruction can be specified to have acquire semantics, and the Store-Exclusive instruction can be specified to have release semantics. These can be arbitrarily combined to allow the atomic update created by a successful Load-Exclusive and Store-Exclusive pair to have any of:

?
No Ordering semantics (using LDREX and STREX).
?
Acquire only semantics (using LDAEX and STREX).
?
Release only semantics (using LDREX and STLEX).
?
Sequentially consistent semantics (using LDAEX and STLEX).

In addition, the ARMv8 specification requires that the clearing of a global monitor will generate an event for the PE associated with the global monitor, which can simplify the use of WFE, by removing the need for a DSB barrier and SEV instruction.
回复
linuxer
2015-11-17 18:29
@坚持到底:关于这个问题可以参考ARM ARM文档中的Figure B2-5,这个图是PE(n)的global monitor的状态迁移图。当PE(n)对x地址发起了exclusive操作的时候,PE(n)的global monitor从open access迁移到exclusive access状态,来自其他PE上针对x(该地址已经被mark for PE(n))的store操作会导致PE(n)的global monitor从exclusive access迁移到open access状态,这时候,PE(n)的Event register会被写入event,就好象生成一个event,将该PE唤醒,从而可以省略一个SEV的指令。
回复
坚持到底
2015-11-19 01:01
@linuxer:这里我有个疑问。

假设 spinlock 场景:

1. cpu0 exclusive load, and exclusive store 操作 lock->lock.此时 global monitor 对于 lock->lock 的 status 依然是 exclusive.

2. cpu1 exclusive load, and wfe.

3. cpu2 exclusive load, and wfe.

4. cpu3 exclusive load, and wfe.

5. 此时只有 cpu0 能去 arch_spin_unlock(), exclusive store 操作 lock->lock, 本应该 excl -> open,会触发 global monitor generate an event for the PE.

但是从你上一段描述和 Figure B2-5 的意思又不一致。 上面 spinlock 的场景根本不会有其他 PE 上针对 x( mark for exclusive ) 的操作了。Figure B2-5 图中也表示 StoreExcl(Marked_address,n) 进行 excl access 操作,global monitor status 是不变的。
回复
linuxer
2015-11-19 10:34
@坚持到底:好吧,看来我表述的不是那么的清楚,针对你说的场景,我重新组织一下:
在cpu0调用unlock之前,各个状态机的状态如下:
1、cpu0上针对spink lockmemory的状态机是open access状态。当然,这个状态和具体实现相关,不过,在这个场景中,没有人关注它的状态。
2、cpu1上针对spink lockmemory的状态机是exclusive access状态,这时候cpu1处于wfe
3、cpu2状态机和cpu1相同
4、cpu3状态机和cpu1相同


cpu0执行了spin_unlock操作,各个状态机的迁移情况如下:
1、cpu0上针对spink lockmemory的状态机是怎样的呢?有人在乎吗?需要知道它的状态吗?当然不需要。
2、cpu1的状态迁移:这时候实际上产生的事件是有其他cpu(指cpu0)执行了针对marked address(指共享的spin lock memory地址)的store操作,在状态图上对应Store(Marked_address,!n)事件,因此,该状态机迁移到open access状态,这时候cpu1的Event register会被写入event,就好象生成一个event,将cpu1唤醒
3、cpu2类似cpu1
4、cpu3类似cpu1


上面是我的理解,请参考
回复
坚持到底
2015-11-19 23:51
@linuxer:对的,谢谢解释啊。^_^
omind
2015-05-28 11:48
都写到armv8了,先进啊
回复
shoujixiaodao
2015-04-29 22:41
我也一直疑惑这两条指令,今日才明白。如拨云见日。感谢!
回复
wowo
2015-04-29 23:13
@shoujixiaodao:不必客气,大家多交流~~
回复
rockwu
2015-01-26 13:44
核心是WFI只能由cpu硬件来唤醒,WFE除了硬件唤醒之外还支持软件唤醒。所以WFE的自由度更好,只要WFE和SEV成对使用。不知道理解是否正确。
回复
wowo
2015-01-26 22:32
@rockwu:应该就是您说理解的那个样子。
不过是不是必须“由CPU硬件唤醒”,这个不能太武断,因为到底什么是硬件的范畴,不太好界定。
回复
haichunzhao
2014-12-11 18:10
刚注册了个账号
不论cpuidle和平台sleep最后走的都是WFI。WFE没研究过,看到你写的,在spinlock中使用的话,感觉和信号量差不多了。
回复
wowo
2014-12-11 18:26
@haichunzhao:sleep应该是ARM core或者ARM cpu的低功耗状态,估计会在WFI之后,再关闭一些其它的东西。不知道是不是所有CPU都一样。
行为确实和信号量类似,但信号量依赖软件的调度,而使用WFE的spinlock是硬件行为。
回复
sea
2015-01-15 11:30
@wowo:wowo,“cpuidle和平台sleep最后走的都是WFI”,那sleep会调用cpuidle framework来走WFI吗?如果不是,那不就意味着sleep也就相当于使用的是《Linux cpuidle framework(1)_概述和软件架构》中“曾经有过一段时间的cpuidle过程”。而且像android这种移动端os,能够进入到idle进程的情况下,应该早就进行suspend操作了吧。这样的话cpuidle framework不就没什么意义了。或者说suspend也是使用cpuidle framework来走WFI,又或者有其它的应用场景?
回复
wowo
2015-01-15 13:18
@sea:1. sleep这个词可能过于含糊,不同平台的实现可能不同,因此是否会使用WFI,是不一定的。
2. WFI只是ARM体系结构的一个指令,谁会使用这个指令,spinlock?cpuidle?还是sleep?是没有限制的,因此也不一定非要走cpuidle framework。
3. 你说的很对,Android选择了autosleep作为自身电源管理的主要手段,cpuidle framework就没有太多用处了,甚至,大多数的Android设备,没有启用cpuidle framework功能。
4. 个人意见,cpuidle framework的主要使用场景,是在服务器系统中,这些系统的cpu core动辄就几十个,对系统响应能力的要求又比较高,通过cpuidle,既可以保证性能,也可以节省很多power。
回复
shoujixiaodao
2015-04-29 22:52
@sea:说说我的见解。只限于手机场景。不论cpuide和sleep虽都调用wfi,但流程不同。很多情况下是cpu idle状态。比如你把手机亮屏不做任何操作有可能进入cpu idle,灭屏听fm也可能会进入。
回复
wowo
2015-04-29 23:12
@shoujixiaodao:我接触的手机平台不多,不知道它们是否使能了cpuidle功能。从道理上来说,确实是这样的。
回复
shoujixiaodao
2015-04-29 22:45
@wowo:我的理解是:信号量依赖软件的调度,所以cpu还干活。而使用WFE的spinlock。则cpu进入低功耗模式。不干活了。
回复
wowo
2015-04-29 23:11
@shoujixiaodao:是的,完全正确,所以从这个角度看,spinlock并不是那么可怕了。
回复
tigger
2014-12-11 10:35
hi wowo
但是这个arch_spin_lock 使用到的地方很少啊。
好像只有raw_spin_lock_irqsave 会用到这个地方。
回复
wowo
2014-12-11 10:39
@tigger:应该不会,不过我没有细看,你可以顺着这个调用顺序看一下:__lock_acquire-->mark_lock-->graph_lock-->arch_spin_lock(arch/arm64/include/asm/spinlock.h)。
PS:有没有同学把spinlock分析一下啊?还挺有意思啊。
回复
tigger
2014-12-11 10:40
@wowo:linuxer 之前分析过的啊
回复
wowo
2014-12-11 11:11
@tigger:是吗?我怎么不记得了呢?
回复
linuxer
2014-12-11 11:57
@wowo:内核同步的第四篇就是spin lock,不过我还没有写呢,最近又沉迷与时间子系统,唉,时间总是不够用啊
回复
tigger
2014-12-11 12:00
@linuxer:我也感觉时间不够用,想学的东西太多了。总是东学一下,西学一下。
回复
linuxer
2014-12-11 10:17
最近准备研究CPU idle framework
-----------------------------------------------
太好了,我在看tick broadcast framework,和cpuidle framework有关,我还在想蜗窝应该写到这里了吧

另外,你文章中的PE的full name是什么?
回复
wowo
2014-12-11 10:36
@linuxer:Processing Element, Cortex-A57有一个ARM core的架构图,比较清晰。我不想写太多,光这个词,就可以写一篇文章啊,涉及到ARM的架构。

你可能感兴趣的:(arm汇编)