[同步机制]-spinlock代码导读和分析

一、代码阅读分析

0、spin lock调用流程图

在这里插入图片描述

1、再kernel中调用spi_lock()或spin_unlock函数

spin_lock(&aacirun->lock)
spin_unlock(&aacirun->lock)

2、调用raw_spin_lock()和raw_spin_unlock()

linux/include/linux/spinlock.h

static __always_inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}

static __always_inline void spin_lock_bh(spinlock_t *lock)
{
	raw_spin_lock_bh(&lock->rlock);
}
static __always_inline void spin_lock_irq(spinlock_t *lock)
{
	raw_spin_lock_irq(&lock->rlock);
}

#define spin_lock_irqsave(lock, flags)				\
do {								\
	raw_spin_lock_irqsave(spinlock_check(lock), flags);	\
} 

static __always_inline void spin_unlock(spinlock_t *lock)
{
	raw_spin_unlock(&lock->rlock);
}

static __always_inline void spin_unlock_bh(spinlock_t *lock)
{
	raw_spin_unlock_bh(&lock->rlock);
}

static __always_inline void spin_unlock_irq(spinlock_t *lock)
{
	raw_spin_unlock_irq(&lock->rlock);
}

static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
	raw_spin_unlock_irqrestore(&lock->rlock, flags);
}

3、调用_raw_spin_lock()和_raw_spin_unlock()

#define raw_spin_lock(lock) _raw_spin_lock(lock)
#define raw_spin_unlock(lock) _raw_spin_unlock(lock)

4、调用__raw_spin_lock()和__raw_spin_unlock()

(linux/include/linux/spinlock_api_smp.h)

#ifdef CONFIG_INLINE_SPIN_LOCK
#define _raw_spin_lock(lock) __raw_spin_lock(lock)
#endif

(linux/kernel/locking/spinlock.c)
#ifndef CONFIG_INLINE_SPIN_LOCK
void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{
	__raw_spin_lock(lock);
}
EXPORT_SYMBOL(_raw_spin_lock);
#endif

5、调用do_raw_spin_lock()和do_raw_spin_unlock()

(linux/include/linux/spinlock_api_smp.h)
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);
}

static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
	spin_release(&lock->dep_map, 1, _RET_IP_);
	do_raw_spin_unlock(lock);
	preempt_enable();
}

在这一层中,我们看到了preempt_disable()和preempt_enable(),禁止抢占和允许抢占.

6、调用arch_spin_lock()和arch_spin_unlock()

(linux/include/linux/spinlock.h)
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
	__acquire(lock);
	arch_spin_lock(&lock->raw_lock);
}

static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
{
	arch_spin_unlock(&lock->raw_lock);
	__release(lock);
}

7、在arm64中arch_spin_lock()和arch_spin_unlock()的实现

在arch_spin_unlock中为什么没有sev指令,lock中的wfe在什么地方被唤醒呢?
等待自旋锁的时候,使用指令ldaxrh(带有获取语义的独占加载,h表示halfword,即2字节)读取服务号,独占加载操作会设置处理器的独占监视器,记录锁的物理地址。
释放锁的时候,使用stlrh指令修改锁的值,stlrh指令会清除所有监视锁的物理地址的处理器的独占监视器,清除独占监视器的时候会生成一个唤醒事件。

(linux/arch/arm64/asm/spinlock.h)
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
	unsigned int tmp;
	arch_spinlock_t lockval, newval;

	asm volatile(
	/* Atomically increment the next ticket. */
	ARM64_LSE_ATOMIC_INSN(
	/* LL/SC */
"	prfm	pstl1strm, %3\n"
"1:	ldaxr	%w0, %3\n"
"	add	%w1, %w0, %w5\n"
"	stxr	%w2, %w1, %3\n"
"	cbnz	%w2, 1b\n",
	/* LSE atomics */
"	mov	%w2, %w5\n"
"	ldadda	%w2, %w0, %3\n"
"	nop\n"
"	nop\n"
"	nop\n"
	)

	/* Did we get the lock? */
"	eor	%w1, %w0, %w0, ror #16\n"
"	cbz	%w1, 3f\n"
	/*
	 * No: spin on the owner. Send a local event to avoid missing an
	 * unlock before the exclusive load.
	 */
"	sevl\n"
"2:	wfe\n"
"	ldaxrh	%w2, %4\n"
"	eor	%w1, %w2, %w0, lsr #16\n"
"	cbnz	%w1, 2b\n"
	/* We got the lock. Critical section starts here. */
"3:"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
	: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
	: "memory");
}

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
	unsigned long tmp;

	asm volatile(ARM64_LSE_ATOMIC_INSN(
	/* LL/SC */
	"	ldrh	%w1, %0\n"
	"	add	%w1, %w1, #1\n"
	"	stlrh	%w1, %0",
	/* LSE atomics */
	"	mov	%w1, #1\n"
	"	nop\n"
	"	staddlh	%w1, %0")
	: "=Q" (lock->owner), "=&r" (tmp)
	:
	: "memory");
}

8、在arm32中arch_spin_lock()和arch_spin_unlock()的实现

(linux/arch/arm/asm/spinlock.h)
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();
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
	smp_mb();
	lock->tickets.owner++;
	dsb_sev();
}

8、相关结构体

(1)、spinlock_t
spinlock_t 结构体中,只有一个struct raw_spinlock rlock元素

typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;

(2)、raw_spinlock
在raw_spinlock中,有arch_spinlock_t raw_lock

typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
	unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

(3)、arch_spinlock_t

typedef struct {
#ifdef __AARCH64EB__
	u16 next;
	u16 owner;
#else
	u16 owner;
	u16 next;
#endif
} __aligned(4) arch_spinlock_t;

二、spin lock的排队原理

我们将arch_spinlock_t单独拎出来、将处理ower和next的地方也单独拎出来,翻译成C语言是这一个样子的。
这种做法的目的,主要是引入排队机制,谁先申请,谁先获得

struct spinlock {
        unsigned short owner;
        unsigned short next;
};


void spin_lock(struct spinlock *lock)
{
        unsigned short next = xadd(&lock->next, 1);
        while (lock->owner != next);
}


void spin_unlock(struct spinlock *lock)
{
        lock->owner++;
}

我们举个例子:

         init  cpu0-acquire  cpu4-acquire  cpu6-acquire  cpu1-acquire  
owner    1        1               1             1            1
next     0        1               2             3            4
  • 在spin_lock_init时,owenr=1, next=0;
  • 当cpu0 acquire锁时,next++后,next=1,在spin_lock中while循环成立,程序继续往下跑;
  • 此时,cpu4也试图拿锁,next++后,next=2,程序卡在while循环中;
  • 此时,cpu6也试图拿锁,next++后,next=3,程序卡在while循环中;
  • 此时,cpu1也试图拿锁,next++后,next=4,程序卡在while循环中;
  • 等到cpu0释放该锁了,owner++,owner=2,此时cpu4中的while循环退出,程序继续往下跑;

注意:在spin_lock_init时,初始化owenr=1, next=0

void __raw_spin_lock_init(raw_spinlock_t *lock, const char *name,
			  struct lock_class_key *key)
{
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	/*
	 * Make sure we are not reinitializing a held lock:
	 */
	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
	lockdep_init_map(&lock->dep_map, name, key, 0);
#endif
	lock->raw_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
	lock->magic = SPINLOCK_MAGIC;
	lock->owner = SPINLOCK_OWNER_INIT;
	lock->owner_cpu = -1;
}

#define __ARCH_SPIN_LOCK_UNLOCKED { 1 }

九、spin lock的总结

spi lock都干了那些事:
在spin_lock()时:

  • 禁止抢占;
  • 引入onwer/next排队机制循环执行while(1)排队;
  • 为了降低功耗引入wfe/sev指令,未获取该锁的cpu进程就是低功耗状态,等到有人释放该锁了再去执行while(1)排队

猜你喜欢

转载自blog.csdn.net/weixin_42135087/article/details/107461847