簡體   English   中英

AArch64 程序集中的原子鏈表 LIFO,在 ldxr / stxr 之間使用加載或存儲

[英]atomic linked-list LIFO in AArch64 assembly, using load or store between ldxr / stxr

我使用 ARMv8 64 位的程序集為共享的 memory 上下文實現了 LIFO。

LIFO 在開頭插入一個節點,每個節點結構的第一個屬性必須是下一個指針。

這是為 LIFO 實現原子插入和刪除的正確程序集嗎?

它使用LL/SC在 LDXR 和 STXR 之間進行額外的加載或存儲來讀取 head->next 或將指針存儲到新節點中。

typedef union {
   void * head[1];
}lifo;
int atomic_lifo_init(lifo * h) {
if (h) {
   h->head[0]=NULL;
  }
}
inline void *
 atomic_lifo_delete (lifo *h)
 {
         void    *ret = NULL;
         /*sa_ignore UNUSED_VAR*/
         void *  tmp = NULL;
 
         asm volatile ("\n"
                 "2:  ldxr      %0,[%2] \n" //load the head from h, which points the 1st node of list to local ret pointer.
                 "    cbz     %0, 3f \n" //check if the lifo is empty.
                 "    ldr      %1,[%0] \n" //store in tmp the 2nd node by derefencing the ret (h->head points 1st node. value of each node is next node as 1st attribute of node structure is pointing next.)
                 "    stxr     %w1, %1,[%2] \n" //update h->head with tmp.
                 "    cbnz     %w1, 2b \n" //continue if failed
                 "3:              \n"
                 : "=&r" (ret), "=&r" (tmp)
                 : "r" (h)
                 : "memory"
                 );
         return(ret);
 }
 
 /*
  * atomic_lifo_insert()
  *      Put an element on a list, protected against SMP
  */
 void
 atomic_lifo_insert (lifo *h, void *__new)
 {
         /*sa_ignore UNUSED_VAR*/
         void * next = NULL;
         void * flag = NULL;
         asm volatile (" \n"
                 "1: ldxr      %1,[%2] \n" //load head[0] from h,which points 1st node to local next pointer
                 "   str      %1,[%3] \n" //store the local next pointer to value of __new, as 1st attribute of the any node is next (convention used here). so __new's next is pointing current 1st node.
                 "   stxr    %w0, %3,[%2] \n" //update the h->head with 
   __next.
                 "   cbnz    %w0, 1b \n" //if stxr is failure try again.
                 : "=&r" (flag), "=&r" (next)
                 : "r" (h), "r" (__new)
                 : "memory"
                 );
 }

我對 ARM 組件真的很陌生,所以非常感謝任何幫助。

您的內聯 asm 約束看起來正確,這應該按照您的預期方式編譯。 您可能可以使用"+m" (*h)讓編譯器選擇尋址模式,而不是使用"r"(h)[%2]對其進行硬編碼。

就排序而言, ldr在 ldxr 之后是依賴排序的(如 C11 memory_order_consume ),因此有效。

但是在stxr將地址發布到其他線程之后insert中的 LL/SC 之間的str可能不會變得可見。 所以我認為你需要stlxr (一個發布商店)在insert


在 LDXR/STXR 之間進行額外的加載或存儲是不安全的。 Wikipedia 的LL/SC 文章提到,如果您在 LL 和 SC 之間進行任何加載或存儲,某些實現可能會虛假失敗。 Wiki 說 PowerPC 確實允許這樣做。 但 AArch64 通常明確不:根據 ARM 參考手冊( 請參閱@James Greenhalgh 的評論):

只有在 [...] Load-Exclusive 和 Store-Exclusive 之間沒有明確的 memory 訪問時,LoadExcl/StoreExcl 循環才能保證向前推進

可能有一些 AArch64 CPU 會在其中創建stxr故障的無限循環,但可能還有其他 CPU 可以正常工作。 如果它在您測試的 CPU 上實際工作,那么查看是否有任何文檔支持它可能是個好主意。

如果new_節點恰好與頭節點位於同一高速緩存行(或 LL/SC 獨占塊)中,則這很可能是一個問題。 如果這在您關心的微架構上完全有效,請確保您測試該案例,或者以某種方式使其成為不可能。


除此之外,我認為您的整體算法看起來是正確的,所以如果您已經對其進行了測試並發現它有效,那么這可能很好。

但是,我並沒有真正仔細考慮過您的算法。 我也沒有任何設計或使用原始 LL/SC 的經驗。 我原則上知道它們是如何工作的,但我不准備說這絕對是正確的。 從我所做的一點點思考來看,我沒有看到任何問題,但這並不意味着沒有任何問題。

暫無
暫無

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

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