事務記憶體支援

POWER 核心對該特性的支援目前僅限於支援使用者程式使用它。核心本身目前不使用它。

此檔案旨在總結 Linux 如何支援它以及您可以從使用者程式中期望的行為。

基本概述

硬體事務記憶體在 POWER8 處理器上受支援,它是一種支援不同形式的原子記憶體訪問的特性。提供幾個新指令來分隔事務;事務保證要麼以原子方式完成,要麼回滾並撤消任何部分更改。

一個簡單的事務如下所示

begin_move_money:
  tbegin
  beq   abort_handler

  ld    r4, SAVINGS_ACCT(r3)
  ld    r5, CURRENT_ACCT(r3)
  subi  r5, r5, 1
  addi  r4, r4, 1
  std   r4, SAVINGS_ACCT(r3)
  std   r5, CURRENT_ACCT(r3)

  tend

  b     continue

abort_handler:
  ... test for odd failures ...

  /* Retry the transaction if it failed because it conflicted with
   * someone else: */
  b     begin_move_money

“tbegin”指令表示起始點,“tend”表示結束點。在這些點之間,處理器處於“事務”狀態;如果系統內沒有與其他事務或非事務訪問發生衝突,則任何記憶體引用都將一次性完成。在此示例中,如果其他處理器沒有觸及 SAVINGS_ACCT(r3) 或 CURRENT_ACCT(r3),則事務完成,就像它是正常的直線程式碼一樣;已執行從當前帳戶到儲蓄帳戶的原子轉賬。即使使用了普通的 ld/std 指令(注意沒有 lwarx/stwcx),要麼 *SAVINGS_ACCT(r3) 和 CURRENT_ACCT(r3)* 都將被更新,要麼都不會被更新。

如果與此同時,與事務訪問的位置發生衝突,CPU 將中止事務。暫存器和記憶體狀態將回滾到“tbegin”時的狀態,控制將從“tbegin+4”繼續。第二次將執行跳轉到 abort_handler 的分支;中止處理程式可以檢查失敗的原因並重試。

檢查點暫存器包括所有 GPR、FPR、VR/VSR、LR、CCR/CR、CTR、FPCSR 和一些其他狀態/標誌暫存器;有關詳細資訊,請參見 ISA。

事務中止的原因

  • 與其他處理器使用的快取行衝突

  • 訊號

  • 上下文切換

  • 有關將中止事務的所有內容的完整文件,請參見 ISA。

系統呼叫

從活動事務中發出的系統呼叫將不會被執行,並且該事務將被核心以失敗程式碼 TM_CAUSE_SYSCALL | TM_CAUSE_PERSISTENT 判定失敗。

從暫停事務中發出的系統呼叫將像往常一樣執行,並且事務不會被核心顯式判定失敗。但是,核心執行系統呼叫的方式可能會導致硬體判定事務失敗。系統呼叫以暫停模式執行,因此任何副作用都將是持久的,獨立於事務成功或失敗。核心不提供關於哪些系統呼叫將影響事務成功的保證。

如果呼叫是透過庫進行的,則在依賴於在活動事務期間中止的系統呼叫時必須小心。庫可能會快取值(這可能會給出成功的假象)或執行在進入核心之前導致事務失敗的操作(這可能會產生不同的失敗程式碼)。例如 glibc 的 getpid() 和惰性符號解析。

訊號

在事務期間傳遞訊號(同步和非同步)提供了第二個執行緒狀態 (ucontext/mcontext) 來表示第二個事務暫存器狀態。訊號傳遞 “treclaim” 來捕獲兩種暫存器狀態,因此訊號會中止事務。傳遞給訊號處理程式的常規 ucontext_t 表示檢查點/原始暫存器狀態;訊號似乎出現在 “tbegin+4”。

如果 sighandler ucontext 設定了 uc_link,則已傳遞第二個 ucontext。為了將來的相容性,應檢查 MSR.TS 欄位以確定事務狀態 -- 如果是,則 uc->uc_link 中的第二個 ucontext 表示訊號點的活動事務暫存器。

對於 64 位程序,uc->uc_mcontext.regs->msr 是完整的 64 位 MSR,其 TS 欄位顯示事務模式。

對於 32 位程序,mcontext 的 MSR 暫存器只有 32 位;高 32 位儲存在第二個 ucontext 的 MSR 中,即 uc->uc_link->uc_mcontext.regs->msr 中。頂部字包含事務狀態 TS。

但是,基本訊號處理程式不需要知道事務,只需從處理程式返回即可正確處理事情

事務感知訊號處理程式可以從第二個 ucontext 讀取事務暫存器狀態。例如,崩潰處理程式需要這樣做才能確定導致 SIGSEGV 的指令地址。

示例訊號處理程式

  void crash_handler(int sig, siginfo_t *si, void *uc)
  {
    ucontext_t *ucp = uc;
    ucontext_t *transactional_ucp = ucp->uc_link;

    if (ucp_link) {
      u64 msr = ucp->uc_mcontext.regs->msr;
      /* May have transactional ucontext! */
#ifndef __powerpc64__
      msr |= ((u64)transactional_ucp->uc_mcontext.regs->msr) << 32;
#endif
      if (MSR_TM_ACTIVE(msr)) {
         /* Yes, we crashed during a transaction.  Oops. */
 fprintf(stderr, "Transaction to be restarted at 0x%llx, but "
                         "crashy instruction was at 0x%llx\n",
                         ucp->uc_mcontext.regs->nip,
                         transactional_ucp->uc_mcontext.regs->nip);
      }
    }

    fix_the_problem(ucp->dar);
  }

當處於活動事務中接收到訊號時,我們需要小心堆疊。堆疊有可能在 tbegin 之後向上移動。這裡明顯的例子是 tbegin 在 tend 之前返回的函式內部被呼叫。在這種情況下,堆疊是檢查點事務記憶體狀態的一部分。如果我們以非事務方式或暫停方式覆蓋此內容,我們將遇到麻煩,因為如果我們獲得 tm 中止,程式計數器和堆疊指標將返回到 tbegin,但我們的記憶體堆疊將不再有效。

為了避免這種情況,當在活動事務中接收到訊號時,我們需要使用來自檢查點狀態的堆疊指標,而不是推測狀態。這確保了訊號上下文(寫入 tm 暫停)將被寫入回滾所需的堆疊下方。由於 treclaim,事務被中止,因此在 tbegin 和訊號之間寫入的任何記憶體都將被回滾。

對於在非 TM 或暫停模式下接收的訊號,我們使用常規/非檢查點堆疊指標。

在 sighandler 內部啟動並在從 sighandler 返回到核心時暫停的任何事務都將被回收和丟棄。

核心使用的失敗原因程式碼

這些在 <asm/reg.h> 中定義,並區分核心中止事務的不同原因

TM_CAUSE_RESCHED

執行緒已重新排程。

TM_CAUSE_TLBI

軟體 TLB 無效。

TM_CAUSE_FAC_UNAV

FP/VEC/VSX 不可用陷阱。

TM_CAUSE_SYSCALL

來自活動事務的系統呼叫。

TM_CAUSE_SIGNAL

訊號已傳遞。

TM_CAUSE_MISC

當前未使用。

TM_CAUSE_ALIGNMENT

對齊錯誤。

TM_CAUSE_EMULATE

觸控記憶體的模擬。

使用者程式的中止處理程式可以檢查這些程式碼作為 TEXASR[0:7]。如果設定了位 7,則表示該錯誤被認為是永續性的。例如,TM_CAUSE_ALIGNMENT 將是永續性的,而 TM_CAUSE_RESCHED 將不會。

GDB

GDB 和 ptrace 目前不感知 TM。如果在事務期間停止,它看起來就像事務剛剛開始(呈現檢查點狀態)。然後無法繼續該事務,並將採用失敗處理程式路徑。此外,事務的第二個暫存器狀態將無法訪問。目前可以在使用 TM 的程式中使用 GDB,但在事務中的部分中不能明智地使用。

POWER9

POWER9 上的 TM 在儲存完整的暫存器狀態時存在問題。這在此提交中描述

commit 4bb3c7a0208fc13ca70598efd109901a7cd45ae7
Author: Paul Mackerras <paulus@ozlabs.org>
Date:   Wed Mar 21 21:32:01 2018 +1100
KVM: PPC: Book3S HV: Work around transactional memory bugs in POWER9

為了解決這個問題,不同的 POWER9 晶片以不同的方式啟用了 TM。

在 POWER9N DD2.01 及以下版本中,TM 已停用。即未設定 HWCAP2[PPC_FEATURE2_HTM]。

在 POWER9N DD2.1 上,TM 由韌體配置為在發生 tm 暫停時始終中止事務。因此,tsuspend 將導致事務中止並回滾。核心異常也會導致事務中止並回滾,並且不會發生異常。如果使用者空間構造了一個啟用 TM 暫停的 sigcontext,則核心將拒絕該 sigcontext。此模式透過設定 HWCAP2[PPC_FEATURE2_HTM_NO_SUSPEND] 來向用戶通告。在此模式下未設定 HWCAP2[PPC_FEATURE2_HTM]。

在 POWER9N DD2.2 及以上版本中,KVM 和 POWERVM 模擬來賓的 TM(如提交 4bb3c7a0208f 中所述),因此為來賓啟用了 TM,即為來賓使用者空間設定了 HWCAP2[PPC_FEATURE2_HTM]。大量使用 TM 暫停的來賓(tsuspend 或核心暫停)將導致陷入 hypervisor,因此會導致效能下降。主機使用者空間已停用 TM,即未設定 HWCAP2[PPC_FEATURE2_HTM]。(儘管如果我們將來將模擬帶入主機使用者空間上下文切換,我們可能會在某些時候啟用它)。

POWER9C DD1.2 及以上版本僅適用於 POWERVM,因此 Linux 僅作為來賓執行。在這些系統上,TM 的模擬方式與 POWER9N DD2.2 上相同。

從 POWER8 到 POWER9 的來賓遷移將適用於 POWER9N DD2.2 和 POWER9C DD1.2。由於較早的 POWER9 處理器不支援 TM 模擬,因此不支援從 POWER8 到 POWER9 的遷移。

核心實現

h/rfid mtmsrd 怪癖

如 ISA 中定義的,rfid 有一個怪癖,它在早期異常處理中很有用。當在使用者空間事務中並且我們透過一些異常進入核心時,MSR 將最終變為 TM=0 和 TS=01(即 TM 關閉但 TM 暫停)。通常,核心想要更改 MSR 中的位,並將執行 rfid 來執行此操作。在這種情況下,rfid 可以具有 SRR0 TM = 0 和 TS = 00(即 TM 關閉和非事務),並且生成的 MSR 將保留之前的 TM = 0 和 TS=01(即保持暫停狀態)。這是架構中的一個怪癖,因為這通常是從 TS=01 到 TS=00(即暫停 -> 非事務)的轉換,這是一種非法轉換。

此怪癖在 rfid 的定義中的架構中描述,包含以下幾行

if (MSR 29:31 ¬ = 0b010 | SRR1 29:31 ¬ = 0b000) then

MSR 29:31 <- SRR1 29:31

hrfid 和 mtmsrd 具有相同的怪癖。

Linux 核心在其早期異常處理中使用此怪癖。