sync spinlock - ceragon/LinuxDoc GitHub Wiki
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;
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");
}
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;
# 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) // 比较立即数 1 和 lock
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)
# 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");
}
# 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)
# 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");
}
读锁之间不互斥,读写互斥,写写互斥。