所有竞态原因
大概意思
原子操作写的时候 可以同时发生 ,但不会同时成功,只有一个成功(成功的同时置位占用标识)
失败的那个,如果再去获取
先去读占用标识 (肯定是已经被占用,程序上就不再获取了,而是一直读占用标识)
实现
static inline void spin_lock(spinlock_t *lock)
{
raw_spin_lock(&lock->rlock);
}
#define raw_spin_lock(lock) _raw_spin_lock(lock)
#ifndef CONFIG_INLINE_SPIN_LOCK
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{
__raw_spin_lock(lock);
}
EXPORT_SYMBOL(_raw_spin_lock);
#endif
https:
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
https:
#define LOCK_CONTENDED(_lock, try, lock) \
do { \
if (!try(_lock)) { \
lock_contended(&(_lock)->dep_map, _RET_IP_); \
lock(_lock); \
} \
lock_acquired(&(_lock)->dep_map, _RET_IP_); \
} while (0)
https:
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
__acquire(lock);
arch_spin_lock(&lock->raw_lock);
}
https:
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"
" add %1, %0, %4\n"
" strex %2, %1, [%3]\n"
" teq %2, #0\n"
" bne 1b"
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
: "cc");
while (lockval.tickets.next != lockval.tickets.owner) {
wfe();
lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
}
smp_mb();
}
https:
#define spin_lock_init(_lock) \
do { \
spinlock_check(_lock); \
raw_spin_lock_init(&(_lock)->rlock); \
} while (0)
https:
# define raw_spin_lock_init(lock) \
do { *(lock) = __RAW_SPIN_LOCK_UNLOCKED(lock); } while (0)
https:
#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \
(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
https:
#define __RAW_SPIN_LOCK_INITIALIZER(lockname) \
{ \
.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED \
}
https:
#define __ARCH_SPIN_LOCK_UNLOCKED { { 0 } }
{
.tickets = {
.owner = 0
}
}
https:
typedef struct spinlock {
union {
struct raw_spinlock rlock;
};
} spinlock_t;
https:
typedef struct raw_spinlock {
arch_spinlock_t raw_lock;
} raw_spinlock_t;
https:
typedef struct {
union {
u32 slock;
struct __raw_tickets {
u16 owner;
u16 next;
} tickets;
};
} arch_spinlock_t;
如何索引
spinlock_t *spin_lock_val ;
spin_lock_val->rlock.raw_lock.tickets.owner
spin_lock_val->rlock.raw_lock.tickets.next
spin_lock_val->rlock.raw_lock.slock
arch_spinlock_t *raw_spinlock_val;
arch_spinlock_val->tickets.owner
arch_spinlock_val->tickets.next
arch_spinlock_val->slock
arm 架构 原子指令
LDREX
用来读取内存中的值,并标记对该段内存的独占访问:
LDREX Rx, [Ry]
读取寄存器Ry指向的4字节内存值,将其保存到Rx寄存器中
同时
发现指向内存区域已经被标记为独占访问,则不会改变 "独占访问标记位"
或
发现指向内存区域没有被标记为独占访问,则 将 指向内存区域 标记为独占访问
STREX
在更新内存数值时,会检查该段内存是否已经被标记为独占访问,并以此来决定是否更新内存中的值:
STREX Rd, Rx, [Ry]
执行这条指令的时候发现已经被标记为独占访问了
1.将寄存器Rx中的值更新到寄存器Ry指向的内存,并将寄存器Rd设置成0
2.将独占访问标记位清除
或
执行这条指令的时候发现没有设置独占标记
1.不会更新内存,不会将寄存器Rd的值设置成1
2.不会改变 "独占访问标记位"
---
arch_spin_trylock 时
会先 load ,
如果load 结果为0 , 就 store 1
store 成功(Rd为0) , 就返回成功(0)
store 失败(Rd为1) , 就返回失败(非0)
如果load 结果为1 , 就返回失败(非0)
场景分析
第一种情况AB12
A.LDREX
A.STREX
B.LDREX
B.STREX
第二种情况A1B2
A.LDREX
B.LDREX
A.STREX
B.STREX
第三种情况A12B
A.LDREX
B.LDREX
B.STREX
A.STREX
其他情况
A.LDREX & B.LDREX (不管A和B在什么时候发生,都会正常执行)
A.STREX & B.STREX (同时发生,只有一个会将Rd弄成0,另一个为1)
arm 是根据 该内存 是否 标记独占 , strex 来做 不同的事情
riscv 是根据 该内存中的值 是否 为0 , amoswap 来做不同的事情
AMOSWAP amoswap.{w/d}.{aqrl} rd, rs2, (rs1) 原子交换指令,
会判断 rs1 是否为0
如果为0 , 则 rd = *rs1, *rs1 = rs2
做成功了 , rd 为 0
做失败了 , rd 为 1
如果为1 , 则 , rd = 1 , *rs1 = rs2
arch_spin_trylock 时
会做 amoswap ,
如果rs1 结果为0 , 就 尝试 swap
swap 成功(rd为0) , 就返回成功(0)
swap 失败(rd为1) , 就返回失败(非0)
如果rs1 结果为1 , 就返回失败(非0)