sync spinlock - ceragon/LinuxDoc GitHub Wiki

spinlock

自旋互斥锁

结构体

typedef struct spinlock {
    union {
        struct raw_spinlock rlock;
    };
} spinlock_t;
typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;
} raw_spinlock_t;
typedef struct arch_spinlock {
    unsigned int slock;
} arch_spinlock_t;

lock

static inline void spin_lock(spinlock_t *lock){
//  raw_spin_lock(&lock->rlock);
//  _raw_spin_lock(&lock->rlock);
    __raw_spin_lock(lock);
}
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);
//  do_raw_spin_lock(lock)
//  __acquire(lock);
//    arch_spin_lock(&lock->raw_lock);
//    arch_spin_lock(lock);
//    __ticket_spin_lock(lock);
    short inc = 0x0100;
	asm volatile (
		LOCK_PREFIX "xaddw %w0, %1\n" // 16位,inc = lock ; lock = lock + 0x0100
		"1:\t"
		"cmpb %h0, %b0\n\t"     // 比较 inc 高8位与低8位
		"je 2f\n\t"             // 相等调整到 2 位置
		"rep ; nop\n\t"         // cpu relax
		"movb %1, %b0\n\t"      // 将 lock 当前值的低8位赋值到 inc 的低8位
		/* don't need lfence here, because loads are in-order */
		"jmp 1b\n"              // 强制跳转到 1 的位置
		"2:"
		: "+Q" (inc), "+m" (lock->slock)
		:
		: "memory", "cc");
}

unlock

static inline void spin_unlock(spinlock_t *lock){
//	raw_spin_unlock(&lock->rlock);
    __raw_spin_unlock(lock);
}
static inline void __raw_spin_unlock(raw_spinlock_t *lock) {
//	spin_release(&lock->dep_map, 1, _RET_IP_);
//	do_raw_spin_unlock(lock);
    arch_spin_unlock(&lock->raw_lock);
//	__release(lock);
    
//	preempt_enable();
}
static __always_inline void arch_spin_unlock(arch_spinlock_t *lock) {
//	__ticket_spin_unlock(lock);
    asm volatile(UNLOCK_LOCK_PREFIX 
                "incb %0"   // 将 lock 的低8位 + 1
                : "+m" (lock->slock)
                :
                : "memory", "cc");
}

总结

lock 被分为了高8位和低8位,高位的值用于表示获取锁的顺序或者说编号,低位的值用于释放锁。 只有当高位的编号与低位的值相同时获取锁成功,这样就实现了一个公平自旋锁。

自旋读写锁

结构体

typedef struct {
	arch_rwlock_t raw_lock;
    
} rwlock_t;
typedef struct {
	unsigned int lock;
} arch_rwlock_t;

read lock

# define do_raw_read_lock(rwlock)	do { \
    __acquire(lock);                   \
    arch_read_lock(&(rwlock)->raw_lock); \
} while (0)
static inline void arch_read_lock(arch_rwlock_t *rw) {
	asm volatile(LOCK_PREFIX 
                " subl $1,(%0)\n\t" // subl 操作32位的减法
                // 当最高位为0时跳转,说明lock还没有变成负数               
                "jns 1f\n"
                // 走到这里说明read lock 的信号量用完了,需要自旋等待
                "call __read_lock_failed\n\t"
                "1:\n"
                :
                // 需要将 rw 也就是 lock 的地址放入到 rdi 寄存器中
                :LOCK_PTR_REG (rw)  // "D" (rw)
                // 本次汇编会修改内存
                : "memory");
}

%rdi 中存储了 *rw

ENTRY(__read_lock_failed)
	CFI_STARTPROC // 处理开始
	LOCK_PREFIX // lock 前缀
	incl (%rdi) // 将 lock +1。如果之前是 -1,现在会变为0 
1:	rep
	nop         // cpu relax
	cmpl $1,(%rdi) // 比较立即数 1lock
	js 1b       // 如果 lock 高位为 1 则跳转到1处。也就是说 lock - 1 < 0
	LOCK_PREFIX // lock 前缀。走到这里说明 lock >= 1
	decl (%rdi) // 将 lock - 1 
	js __read_lock_failed // 如果 lock 为负,则递归调用
	ret     // 获取 read lock 成功
	CFI_ENDPROC
END(__read_lock_failed)

read unlock

# define do_raw_read_unlock(rwlock)	do { \
    arch_read_unlock(&(rwlock)->raw_lock); \
    __release(lock);                     \
} while (0)
static inline void arch_read_unlock(arch_rwlock_t *rw) {
	asm volatile(LOCK_PREFIX  // lock 前缀
                "incl %0"     // lock + 1
                :"+m" (rw->lock) // +m: 表示数据在内存中,+号表示既是输入也是输出
                : 
                : "memory");
}

write lock

# define do_raw_write_lock(rwlock)	do { \
    __acquire(lock);                    \
    arch_write_lock(&(rwlock)->raw_lock); \
} while (0)
static inline void arch_write_lock(arch_rwlock_t *rw) {
	asm volatile(LOCK_PREFIX // lock 前缀
                " subl %1,(%0)\n\t" // 将 lock - 0x01000000
                "jz 1f\n"   // 为0 跳转到 1
                "call __write_lock_failed\n\t" // 不为0,执行获取写锁失败
                "1:\n"
                :
//              :LOCK_PTR_REG (rw), "i" (RW_LOCK_BIAS) 
                : "D" (rw), "i" (0x01000000)
                : "memory");
}
ENTRY(__write_lock_failed)
	CFI_STARTPROC
	LOCK_PREFIX // lock 前缀
	addl $RW_LOCK_BIAS,(%rdi) // 把刚才减掉的值再加回来
1:	rep
	nop             // cpu relax
	cmpl $RW_LOCK_BIAS,(%rdi) // 比较 lock - 0x01000000
	jne 1b             // 不相等跳转到 1
	LOCK_PREFIX     // lock 前缀, 此步骤说明 lock == 0x01000000
	subl $RW_LOCK_BIAS,(%rdi)   // 再尝试获取写锁
	jnz  __write_lock_failed    // 不为0 就进行递归。
	ret                     // 获取写锁成功
	CFI_ENDPROC
END(__write_lock_failed)

write unlock

# define do_raw_write_unlock(rwlock)	do { \
    arch_write_unlock(&(rwlock)->raw_lock); \
    __release(lock);                      \
} while (0)
static inline void arch_write_unlock(arch_rwlock_t *rw) {
	asm volatile(LOCK_PREFIX 
                "addl %1, %0"  // lock + 0x01000000
                : "+m" (rw->lock) 
                : "i" (RW_LOCK_BIAS) 
                : "memory");
}

总结

读锁之间不互斥,读写互斥,写写互斥。

⚠️ **GitHub.com Fallback** ⚠️