spinlock自旋锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rikeyone/article/details/86542542

spinlock加锁过程

代码调用流程:

spin_lock
->raw_spin_lock
-->_raw_spin_lock
--->__raw_spin_lock
---->arch_spin_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); //请求锁
}

这里实际的工作都是由LOCK_CONTENDED来做的:

#ifdef CONFIG_LOCK_STAT         //如果定义了CONFIG_LOCK_STAT

#define LOCK_CONTENDED(_lock, try, lock)            \
do {                                \
    if (!try(_lock)) {                  \
        lock_contended(&(_lock)->dep_map, _RET_IP_);    \
        lock(_lock);                    \
    }                           \
    lock_acquired(&(_lock)->dep_map, _RET_IP_);         \
} while (0)


#else
#define LOCK_CONTENDED(_lock, try, lock) \     //如果没有定义CONFIG_LOCK_STAT
     lock(_lock)

#endif

void do_raw_spin_lock(raw_spinlock_t *lock)
{   
    debug_spin_lock_before(lock);
    if (unlikely(!arch_spin_trylock(&lock->raw_lock)))     //先try一次去获取,如果不成功,那么在进一步使用spin lock获取
        __spin_lock_debug(lock);
    debug_spin_lock_after(lock);
}
    
int do_raw_spin_trylock(raw_spinlock_t *lock)
{   
    int ret = arch_spin_trylock(&lock->raw_lock);

    if (ret)
        debug_spin_lock_after(lock);
#ifndef CONFIG_SMP 
    /*
     * Must not happen on UP:
     */
    SPIN_BUG_ON(!ret, lock, "trylock failure on UP");
#endif
    return ret;
}

我们先来看spin lock的情况,它先进行try一次,失败后执行__spin_lock_debug,这里会进行实际的spin lock处理:

static void __spin_lock_debug(raw_spinlock_t *lock)
{
    u64 i;
    u64 loops = loops_per_jiffy * HZ;

    for (i = 0; i < loops; i++) {
        if (arch_spin_trylock(&lock->raw_lock))
            return;
        __delay(1);
    }
    /* lockup suspected: */
    spin_dump(lock, "lockup suspected");
#ifdef CONFIG_SMP
    trigger_all_cpu_backtrace();
#endif

    /*
     * The trylock above was causing a livelock.  Give the lower level arch
     * specific lock code a chance to acquire the lock. We have already
     * printed a warning/backtrace at this point. The non-debug arch
     * specific code might actually succeed in acquiring the lock.  If it is
     * not successful, the end-result is the same - there is no forward
     * progress.
     */
    arch_spin_lock(&lock->raw_lock);
}


可以看到此函数最后一句是arch_spin_lock平台相关的操作,但是在使用平台函数加锁之前,还有很多个处理,上面会通过一个for循环不停的trylock,为什么要这么做?
因为我们的arch_spin_lock操作实际最终会使用wfe操作使CPU进入standby状态,但是这个状态维持多久,我们很难统计一个时间出来,但是通过我们的for循环,它会不停进行运算,那么时间就可以统计出来了,这里统计的时间为一个HZ,也就是1S,如果超过1S我们还没有获取到一个spinlock,那么就可以怀疑这时候发生了死锁,会打印对应的log和backtrace。这个处理也是新版本加入的功能,旧版本的spinlock可能压根不经过这个for循环处理了,而是直接进入wfe去等待锁。

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned long tmp;
    u32 newval;
    arch_spinlock_t lockval;

    __asm__ __volatile__(
"1:    ldrex    %0, [%3]\n"    //把lockval读到tmp, 并且(分别在Local monitor和Global monitor中)设置独占标志
"    add    %1, %0, %4\n"      //lockval + (1 << TICKET_SHIFT) 存到newval。(next票号+1)
"    strex    %2, %1, [%3]\n"  //把newval保存到&lock->slock中,此时 Exclusive monitors会发挥作用,将保存是否成功的标志放入tmp中。
"    teq    %2, #0\n"          //测试strex是否成功(tmp ?= 0 )
"    bne    1b"                //如果发现strex失败,从1:再次执行
    : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
    : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
    : "cc");

    while (lockval.tickets.next != lockval.tickets.owner) {  //查看本次获取的票号是否到号
        wfe();          //不想等说明没有到号,进入wfe继续等待
        lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
    }

    smp_mb(); 
}

spinlock解锁过程

spin_unlock
->raw_spin_unlock
-->_raw_spin_unlock
--->__raw_spin_unlock
---->arch_spin_unlock
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();
}

void do_raw_spin_unlock(raw_spinlock_t *lock)
{
    debug_spin_unlock(lock);
    arch_spin_unlock(&lock->raw_lock);
}   

static inline void debug_spin_unlock(raw_spinlock_t *lock)
{
    SPIN_BUG_ON(lock->magic != SPINLOCK_MAGIC, lock, "bad magic");        //当没获得spin lock 就释放,会报错
    SPIN_BUG_ON(!raw_spin_is_locked(lock), lock, "already unlocked");     //当已经释放spin lock再想释放,会报错
    SPIN_BUG_ON(lock->owner != current, lock, "wrong owner");             //不是持有该spin lock的进程来释放该锁,会报错
    SPIN_BUG_ON(lock->owner_cpu != raw_smp_processor_id(),                //不是持有该spin lock的CPU来释放该锁,会报错
                            lock, "wrong CPU");
    lock->owner = SPINLOCK_OWNER_INIT;
    lock->owner_cpu = -1;
}

static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
    smp_mb();               //内存屏障
    lock->tickets.owner++;  //叫下个票号
    dsb_sev();  //唤醒WFE状态的CPU
}

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/86542542