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。
评论:
2015-11-17 11:32
2015-11-17 13:45
请教linuxer,ldaxrh让CPU进入低功耗吗?如果不会,现在仅仅用了busy loop的方式。
2015-11-17 14:11
{
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 的。
2015-11-17 15:05
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。
2015-11-17 15:42
@坚持到底:抱歉误导您了啊,好在有大神在,哈哈。
2015-11-17 15:54
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.
2015-11-17 18:29
2015-11-19 01:01
假设 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 是不变的。
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
上面是我的理解,请参考
2014-12-11 18:10
不论cpuidle和平台sleep最后走的都是WFI。WFE没研究过,看到你写的,在spinlock中使用的话,感觉和信号量差不多了。
2014-12-11 18:26
行为确实和信号量类似,但信号量依赖软件的调度,而使用WFE的spinlock是硬件行为。
2015-01-15 11:30
2015-01-15 13:18
2. WFI只是ARM体系结构的一个指令,谁会使用这个指令,spinlock?cpuidle?还是sleep?是没有限制的,因此也不一定非要走cpuidle framework。
3. 你说的很对,Android选择了autosleep作为自身电源管理的主要手段,cpuidle framework就没有太多用处了,甚至,大多数的Android设备,没有启用cpuidle framework功能。
4. 个人意见,cpuidle framework的主要使用场景,是在服务器系统中,这些系统的cpu core动辄就几十个,对系统响应能力的要求又比较高,通过cpuidle,既可以保证性能,也可以节省很多power。
2015-04-29 22:52
2014-12-11 10:17
-----------------------------------------------
太好了,我在看tick broadcast framework,和cpuidle framework有关,我还在想蜗窝应该写到这里了吧
另外,你文章中的PE的full name是什么?
2014-12-11 10:36
2016-08-17 23:01
请问 spin_lock 只是用来在不同CPU core之间,保护共享资源吗??
对于那些单cpu 的spin_lock 有什么不同之处呢??
2016-08-18 14:54
对于单个CPU来说,它只能顺序执行(除非被中断、异常打断),因此没有spin的必要,也就没有spin_lock的概念。
单CPU需要做同步的话,就把自己的中断关掉就行了。
2016-09-01 19:15
2016-09-01 19:45
我其实比较挑战的是他的另外一句话“单CPU需要做同步的话,就把自己的中断关掉就行了”,一言不合就使用关中断这种大杀器我是反对的,可以选择使用最适合的同步方法。
2016-09-02 08:55
我的风格是比较直接,不太希望把话题延伸的太宽,大家多包涵哈~~~