load-link与store-conditional (LL/SC)是一对用于并发同步访问内存的CPU指令。Load-link返回内存位置处的当前值,随后的store-conditional在该内存位置处保存新值(如果从load-link后没有被修改)。这被用于实现无锁算法与read-modify-write原子操作。
linux
在<asm/atomic.h>
、<asm/system.h>
、<asm/cmpxchg.h>
、<asm/bitops.h>
、<asm/local.h>
中实现了多种基本的原子操作,以最简单的atomic_add
来举例:
/*
* atomic_add - add integer to atomic variable
* @i: integer value to add
* @v: pointer of type atomic_t
*
* Atomically adds @i to @v.
*/
static __inline__ void atomic_add(int i, atomic_t * v)
{
if (kernel_uses_llsc && R10000_LLSC_WAR) {
int temp;
__asm__ __volatile__(
" .set mips3
"
"1: ll %0, %1 # atomic_add
"
" addu %0, %2
"
" sc %0, %1
"
" beqzl %0, 1b
"
" .set mips0
"
: "=&r" (temp), "=m" (v->counter)
: "Ir" (i), "m" (v->counter));
} else if (kernel_uses_llsc) {
int temp;
__asm__ __volatile__(
" .set mips3
"
"1: ll %0, %1 # atomic_add
"
" addu %0, %2
"
" sc %0, %1
"
" beqz %0, 2f
"
" .subsection 2
"
"2: b 1b
"
" .previous
"
" .set mips0
"
: "=&r" (temp), "=m" (v->counter)
: "Ir" (i), "m" (v->counter));
} else {
unsigned long flags;
raw_local_irq_save(flags);
v->counter += i;
raw_local_irq_restore(flags);
}
}
其中,kernel_uses_llsc
是一个宏定义,当定义为0
时就需要软件实现。raw_local_irq_save
与raw_local_irq_restore
函数定义在<linux/irqflags.h>
,对于不同的mips cpu
有不同的实现,最简单的实现如下:
.macro irq_enable_hazard; _ssnop; _ssnop; _ssnop;; .endm
.macro irq_disable_hazard; nop; nop; nop; .endm
.macro raw_local_irq_save result
.set push
.set reorder
.set noat
mfc0
esult, $12
ori $1,
esult, 0x1f
xori $1, 0x1f
.set noreorder
mtc0 $1, $12
irq_disable_hazard
.set pop
.endm
.macro raw_local_irq_restore flags
.set push
.set noreorder
.set noat
mfc0 $1, $12
andi flags, 1
ori $1, 0x1f
xori $1, 0x1f
or flags, $1
mtc0 flags, $12
irq_disable_hazard
.set pop
.endm
raw_local_irq_save
与raw_local_irq_restore
被实现为两个mips
的宏定义,raw_local_irq_save
对cp0
的status
寄存器进行修改,让cpu
进入kernek mode
,ERL
与EXL
置0
,同时禁止中断,从而保证了原子性。raw_local_irq_restore
将中断使能打开。