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
我的風格是比較直接,不太希望把話題延伸的太寬,大家多包涵哈~~~