簡體   English   中英

ARM LL/SC 通過寄存器寬度或緩存線寬度獨占訪問?

[英]ARM LL/SC exclusive access by register width or cache line width?

我正在開發我的無鎖數據結構庫的下一個版本,在 ARM 上使用 LL/SC。

對於我的 LL/SC 用例,我需要將它與 LDREX 和 STREX 之間的單個 STR 一起使用。 (而不是用它來模擬 CAS。)

現在,我已經編寫了代碼並且這有效。 然而,我擔心的是它可能並不總是有效。 我在 PowerPC 上讀過,如果您訪問與 LL/SC 目標相同的緩存行,則會破壞 LL/SC。

所以我在想如果我的 STR 目標與我的 LL/SC 目標在同一個緩存行上,那么 pow,我死了。

現在,LL/SC 目標和 STR 目標始終位於不同的 malloc() 中,因此它們直接位於同一緩存行中的機會可能很小(我可以通過填充 LL/SC 目標來保證這一點,以便它從緩存線邊界並填充該緩存線)。

但是,如果 STR 目標位於內存中的正確(錯誤!)位置,則可能存在錯誤共享。

查看 LDREX/STREX 文檔,這描述了“物理地址”方面的獨占訪問。 這意味着寄存器寬度粒度,而不是緩存線寬度粒度。

這就是我的問題 - LDREX/STREX 是否對使用寄存器寬度粒度或緩存線寬度粒度的內存訪問敏感?

ARM 使用 Exclusive Monitors 通過 load-linked/store-conditional 實現對內存的獨占訪問。 [1] 有所有的細節,在這里我要說的重要的是以下幾點:

獨家保留顆粒

當獨占監視器標記一個地址時,可以標記為獨占訪問的最小區域稱為獨占保留顆粒 (ERG)。 ERG 是實現定義的,范圍為 8-2048 字節,是兩個字節的倍數。 可移植代碼不得假設任何有關 ERG 大小的信息。

所以在我看來你有點不走運。 無論如何,大多數實際實現可能會保留一個很小的值,但據我所知,基本 ARM 架構並不能保證這一點,但也許有更多經驗的人會發現我錯了。 :) 盡管如此,LL/SC 的所有實現都是某種形式的弱 LL/SC,所以你幾乎永遠不能完全確定 LL 和 SC 之間的存儲不會總是殺死 SC,或者大多數有時,或者可能永遠不會......它只是依賴於架構和實現,我個人堅持使用 LL/SC 在緊密循環中實現 CAS,並像往常一樣使用它並完成它。

[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJAGCFAF.html

請注意,LDREX/STREX 並沒有像許多人認為的那樣做。 它們用於多處理器系統,單處理器系統應考慮使用交換。 ARM 文檔通常非常好,但在這種特殊情況下,它們之間存在巨大差距。 Linux 一直在不正確地使用這些指令,擁有單處理器 ARM 內核的公司已經注意到這一點(由於人們在沒有適當研究的情況下添加代碼,Linux 有許多與 ARM 相關的錯誤,每個版本都必須修復)。 如果您在單處理器系統上有 L1 緩存,那您就可以了,因為緩存支持該訪問類型,如果它訪問 AXI 總線,AMBA/AXI 規范會告訴硬件工程師,對於單處理器系統,您不需要支持該事務類型。 不幸的是,ARM ARM/TRM 告訴軟件工程師你應該停止使用交換並開始使用 LDREX/STREX,這是不一致的,一方告訴不要這樣做,另一方告訴這樣做,但沒有任何好處。

這不是您的問題的答案,只是有關這些說明的一般信息,以試圖教育人們正確使用和所涉及的風險。 (是的,在那里,已經完成,因為使用這些指令而被燒毀,不得不修補 linux(在其他 linux 補丁之上))

編輯....更多細節

在 ARM ARM 中:

Historically, support for shared memory synchronization has been with the read-locked-write operations
that swap register contents with memory; the SWP and SWPB instructions described in...

...

ARMv6 provides a new mechanism to support more comprehensive non-blocking shared-memory synchronization primitives
that scale for multiple-processor system designs.

...

The swap and swap byte instructions are deprecated in ARMv6. It is recommended that all software
migrates to using the new synchronization primitives.

...

Uniprocessor systems are only required to support the non-shared memory model, allowing them to support
synchronization primitives with the minimum amount of hardware overhead.

...

Multi-processor systems are required to implement an address monitor for each processor.


STREX:

<Rd> Specifies the destination register for the returned status value. The value returned is:
0  if the operation updates memory
1 if the operation fails to update memory.


MemoryAccess(B-bit, E-bit)
if ConditionPassed(cond) then
  processor_id = ExecutingProcessor()
  physical_address = TLB(Rn)
  if IsExclusiveLocal(physical_address, processor_id, 4) then
    if Shared(Rn) == 1 then
      if IsExclusiveGlobal(physical_address, processor_id, 4) then
        Memory[Rn,4] = Rm
        Rd = 0
        ClearExclusiveByAddress(physical_address,processor_id,4)
      else
        Rd = 1
    else
      Memory[Rn,4] = Rm
      Rd = 0
  else
  Rd = 1
ClearExclusiveLocal(processor_id)

AMBA/AXI 規范

The ARLOCK[1:0] or AWLOCK[1:0] signal selects exclusive access, and the RRESP[1:0] 
or BRESP[1:0] signal (see Table 7-1 on page 7-2) indicates the success or failure 
of the exclusive access.

...

If the master attempts an exclusive read from a slave that does not support exclusive
accesses, the slave returns the OKAY response instead of the EXOKAY response. The
master can treat this as an error condition indicating that the exclusive access is not
supported. It is recommended that the master not perform the write portion of this
exclusive operation.

...

b00 OKAY
b01 EXOKAY

...

ARLOCK/AWLOCK

b00 normal access
b01 exclusive access

所以在軟件方面,ARM ARM 告訴我們使用 LDREX/STREX 而不是交換,部分原因是它可以擴展到多處理器、共享內存和系統。 但是他們也告訴我們單處理器系統不需要支持共享內存同步。 所以即使從軟件方面也有一個線索,你應該三思而后行......

我們從 STREX 的描述中知道,如果它返回exclusive rd = 0 那么它就起作用了。 如果 rd = 1 那么它不是排他的(或其他原因)。 LDREX 和 STREX 是成對完成的,共享內存系統邏輯正在同一地址處尋找這對,並且硬件驗證在兩者之間沒有對該地址的其他訪問。 你擔心誰介於兩者之間? 1)如果您中斷/交換並且很幸運2)另一個使用該內存的處理器。 linux 所做的,據我所知是進入一個緊密的無限循環,

while(1)
{
  ldrex
  strex
  if(rd==0) break;
}

現在在單處理器系統上,ARM 和 ARM 都建議他們不需要支持共享訪問,因為它更簡單(為什么需要增加這種復雜性?)。

作為程序員你沒有看到的東西。 為 ldrex 和 strex 設置了 ARLOCK 或 AWLOCK,如果您正在實現共享訪問,那么您關心這些位。 如果您正在實施共享訪問,那么如果兩者之間沒有訪問,則將 EXOKAY 返回給 strex。 EXOKAY是一個b01,在strex偽代碼中是全局獨占,rd = 0。如果硬件返回OKAY,b00,則不獨占,strex rd = 1。 然后 AMBA/AXI 規范說,如果您不支持共享系統,則可以為獨占訪問返回 OKAY。 因此,在尚未實現獨占訪問的單處理器上,strex 可以和/或將始終返回 OKAY,而不是 EXOKAY。 這意味着 strex 永遠不會得到 rd = 0 並且 linux 掛在無限循環中。

這里真正的 linux bug 是我們當時看到的代碼說 if(ARMv6 or newer) then use LDREX/STREX, else use SWP。 如果(ARMv6 或更新版本和多處理器)修復錯誤,則使用 LDREX/STREX,否則使用 SWP。

這意味着其他任何人出於任何其他原因想要使用 LDREX/STREX,這正是這張票中引起我注意的原因。

現在你問,緩存和它有什么關系? L1 緩存位於處理器內核內部,它不會在 AXI/AMBA 總線上消失。 它為 strex 返回 EXOKAY,和/或它完全實現共享。 因此,如果 L1 緩存打開,那么您將獲得 EXOKAY(第一次或最終,我不確定)。

現在你問,如果有緩存未命中怎么辦? 首先,如果 L1 緩存關閉,那么它會在沒有打開可緩存位的情況下到達 L2 緩存邊界。 因此 L2 緩存將按原樣傳遞它,並以獨占方式退出。 在 L1 緩存打開的情況下,命中它返回 EXOKAY,如上所述(最終或總是,不知道)如果 L1 未命中,則 L1 執行緩存行填充,它執行可緩存的 NOT LOCKED 讀取。 這會導致 L2 命中或未命中,如果 L2 未命中,則它會進入供應商特定的邏輯,在這種情況下返回 OKAY,但這沒關系,因為它沒有被鎖定,無論如何它是正常訪問。 一旦 l2 和 l1 被填充,則 L1 執行原始傳輸並返回 EXOKAY。

現在是關鍵,首先在硬件中實現它是一種浪費和風險,所以我希望單處理器 ARMv6 和更新版本不會返回 EXOKAY,您必須逐案測試。 第二,在關閉緩存的情況下運行 linux 是一個 PITA。 這實際上需要一些工作。 所以你通常不會在 linux 中看到這個。 但問題就在那里,人們已經看到了,每當您自己使用這些說明時,您都應該小心正確地使用它們。 使用裸機編程來測試系統以查看它是否會掛起應該非常簡單,編寫代碼應該需要幾秒鍾/幾分鍾。 將系統置於可以嘗試該代碼的狀態可能需要更長的時間(中斷引導加載程序,使用 jtag 跳轉等)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM