`ll/sc` 指令在`linux`中的软件实现

load-linkstore-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_saveraw_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_saveraw_local_irq_restore被实现为两个mips的宏定义,raw_local_irq_savecp0status寄存器进行修改,让cpu进入kernek modeERLEXL0,同时禁止中断,从而保证了原子性。raw_local_irq_restore将中断使能打开。

原文地址:https://www.cnblogs.com/PowerofChoas/p/11247281.html