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等待的处理器
}
 

你可能感兴趣的:(Kernel,linux,kernel,spinlock,arm)