经典自旋锁原理
待补充
数据结构
arch/arm64/include/asm/spinlock_types.h
typedef struct {
#ifdef __AARCH64EB__ /* 大端字节序(高位存放在低地址) */
u16 next;
u16 owner;
#else /* 小端字节序(低位存放在低地址) */
u16 owner; //低地址
u16 next; //高地址
#endif
} __aligned(4) arch_spinlock_t;
源码实现
spin_lock
//lockval => %0
//newval => %1
//tmp => %2
//*lock => %3
//lock->owner => %4
//1 << 16 => %5
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned int tmp;
arch_spinlock_t lockval, newval;
asm volatile(
/* 自动实现下一次排队 */
ARM64_LSE_ATOMIC_INSN(
/* LL/SC */
" prfm pstl1strm, %3\n"
"1: ldaxr %w0, %3\n" //把*lock加载到变量lockval中, lockval就是代表自己的锁(w代表写入长度,b=8;w=16;l=32)
" add %w1, %w0, %w5\n" //newval = lockval + 1 << TICKET_SHIFT =等价=> newval.next = lockval.next + 1
" stxr %w2, %w1, %3\n" //尝试独占写,*lock = newval
" cbnz %w2, 1b\n", //如果写*lock失败则跳转到1,意味着有别人抢先排队给*lock.next+1了
/* LSE atomics */
" mov %w2, %w5\n"
" ldadda %w2, %w0, %3\n"
" nop\n"
" nop\n"
" nop\n"
)
/* 是否获得了锁? */
" eor %w1, %w0, %w0, ror #16\n" //lockval右移16位获得owner域与自己比较,即比较lockval的owner和next成员
" cbz %w1, 3f\n" //如果是0,直接持锁,跳转3,进入临界区
/*
* No: spin on the owner. Send a local event to avoid missing an
* unlock before the exclusive load.
*/
/* 若没有获得锁则自旋 */
/* 发送本地事件,避免在独占加载前忘记解锁 */
" sevl\n" //不理解
"2: wfe\n" //WFE指令=>wait for event,进入睡眠模式(降低功耗)
" ldaxrh %w2, %4\n" //tmp = lock->owner(当前锁的owner)(ldaxrh中h代表halfword,即2字节,a不懂)
" eor %w1, %w2, %w0, lsr #16\n" //lockval.owner == lock->owner?相等则w1=0
" cbnz %w1, 2b\n" //若w1非0,则跳转回2
/* We got the lock. Critical section starts here. */
"3:"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock) //lockval=>%0 ... *lock=>%3
: "Q" (lock->owner), "I" (1 << TICKET_SHIFT) // %4 %5
: "memory");
}
补充:汇编指令
1、MOV
在寄存器之间移动数据,或者向寄存器中写立即数
MOV R1,R2 ;//R1=R2
2、LDR和STR
LDR是将内存的数据加载到寄存器中,或是载入立即数
LDR R1,=0xE0000000 ;//R1=0xE0000000
LDR R1,0xE0000000 ;//将内存中地址为0xE0000000的内容载入到R1
LDR R1,[R0] ;//将R0中的数所指定的地址的内容传输到R1
STR是将寄存器中的数字载入内存。格式如下:STR{条件} 源寄存器,<存储器地址>
STR R1,[R0] ;//将R1中的内容传输到R0中的数所指定的地址的内存中去
3、STXR
store exclusive
Syntax: STXR Ws, <Wt/Xt>, [Xn]
Ws indicates whether the store completed successfully(0=success)
4、CBZ和CBNZ
CBZ Rn, label //Rn为0则跳转到label
CBNZ Rn, label //Rb非0则跳转到label
//举例
1:xxx
yyy
cbnz %w2, 1b
5、EOR
逻辑异或操作指令
EOR{<cond>}{s} <Rd>, <Rn>, <shifter_operand>
<Rd>目标寄存器,也就是存结果的寄存器
<Rn>寄存器为第一个源操作数所在的寄存器
<shifter_operand>寄存器为第二个操作数
6、ROL和ROR
ROL循环左移,ROR循环右移
以ROR为例:ROR可完成对通用寄存器中的内容进行循环右移的操作,按操作数所指定的数量向右循环移位,**左端用右端移出的位来填充**
ROR的格式为:通用寄存器,ROR 操作数
7、LSL和LSR
LSL左移,ROR右移
LSR可完成对通用寄存器中的内容进行右移的操作,按操作数所指定的数量向右移位,左端用零来填充
LSR的格式为:
通用寄存器,LSR 操作数
问题
1、为什么同时用了ROR和LSR,貌似用一个就够了
https://zhuanlan.zhihu.com/p/83578174
2、大小端模式不同,为什么TICKET_SHIFT能写死为16
3、"eor %w1, %w2, %w0, lsr #16"为什么能去除lockval的next域
4、“发送本地事件,避免在独占加载前忘记解锁”