linux 內核自旋鎖spinlock實現詳解(基於ARM處理器)

1、自旋鎖結構

typedef struct {
union {
u32 slock;
struct __raw_tickets {
#ifdef __ARMEB__
u16 next; ------ 下一個可以獲取自旋鎖的處理器,處理器請求自旋鎖的時候會保存該值並對該值加1,然後與owner比較,檢查是否可以獲取到自旋鎖,每請求一次next都加1
u16 owner; ------ 當前獲取到/可以獲取自旋鎖的處理器,沒釋放一次都加1,這樣next與owner就保存一致
#else
u16 owner;
u16 next;
#endif
} tickets;
};
} arch_spinlock_t;

2、獲取自旋鎖

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned long tmp;
u32 newval;
arch_spinlock_t lockval;


prefetchw(&lock->slock);
__asm__ __volatile__(
"1: ldrex %0, [%3]\n" ------ lockval = lock->slock (如果lock->slock沒有被其他處理器獨佔,則標記當前執行處理器對lock->slock地址的獨佔訪問;否則不影響)
" add %1, %0, %4\n"------ newval = lockval + (1 << TICKET_SHIFT)
" strex %2, %1, [%3]\n" ------ strex tmp, newval, [&lock->slock] (如果當前執行處理器沒有獨佔lock->slock地址的訪問,不進行存儲,返回1;如果當前處理器已經獨佔lock->slock內存訪問,則對內存進行寫,返回0,清除獨佔標記) lock->tickets.next = lock->tickets.next + 1
" teq %2, #0\n"------ 檢查是否寫入成功lockval.tickets.next
" bne 1b"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
: "cc");


while (lockval.tickets.next != lockval.tickets.owner) {------ 初始化時lock->tickets.owner、lock->tickets.next都爲0,假設第一次執行arch_spin_lock,lockval = *lock,lock->tickets.next++,lockval.tickets.next等於lockval.tickets.owner,獲取到自旋鎖;自旋鎖未釋放,第二次執行的時候,lock->tickets.owner = 0, lock->tickets.next = 1,拷貝到lockval後,lockval.tickets.next != lockval.tickets.owner,會執行wfe等待被自旋鎖釋放被喚醒,自旋鎖釋放時會執行lock->tickets.owner++,lockval.tickets.owner重新賦值
wfe(); ------ 暫時中斷掛起執行
lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);------ 重新讀取lock->tickets.owner
}


smp_mb();
}

3、釋放自旋鎖

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
smp_mb();
lock->tickets.owner++; ------ lock->tickets.owner增加1,下一個被喚醒的處理器會檢查該值是否與自己的lockval.tickets.next相等,lock->tickets.owner代表可以獲取的自旋鎖的處理器,lock->tickets.next你一個可以獲取的自旋鎖的owner;處理器獲取自旋鎖時,會先讀取lock->tickets.next用於與lock->tickets.owner比較並且對lock->tickets.next加1,下一個處理器獲取到的lock->tickets.next就與當前處理器不一致了,兩個處理器都與lock->tickets.owner比較,肯定只有一個處理器會相等,自旋鎖釋放時時對lock->tickets.owner加1計算,因此,先申請自旋鎖多處理器lock->tickets.next值更新,自然先獲取到自旋鎖
dsb_sev(); ------ 執行sev指令,喚醒wfe等待的處理器
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章