天天看点

rk3288 spin_lock自旋锁的实现自旋锁参考资料

自旋锁

自己原地打转,等待资源可用,一旦可用就上锁霸占它。

  1. 多SMP系统中,CPU x原地打转,CPUy会解锁。
  2. 单CPU中,自旋锁就没有自选功能了,只有禁止抢占、禁止中断

自旋锁的内核结构体

spinlock对应的结构体如下定义,不同的架构可能有不同的实现:

//include/linux/spinlock_types.h
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;

//include/linux/spinlock_types.h
typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;									//定义了依据arch结构的锁
#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;

//arch/arm/include/asm/spinlock_types.h
typedef struct {
    union {
        u32 slock;            
        struct __raw_tickets {
#ifdef __ARMEB__
            u16 next;         				//next和owner是SMP中实现spinlock的关键
            u16 owner;        
#else     
            u16 owner;        
            u16 next;
#endif    
        } tickets;
    };    
} arch_spinlock_t;
           

spinlock在UP系统中的实现

在单CPU系统中, spin_lock的实现

// include/linux/spinlock.h
static __always_inline void spin_lock(spinlock_t *lock)
{
    raw_spin_lock(&lock->rlock);
}

#define raw_spin_lock(lock) _raw_spin_lock(lock) 

// include/linux/spinlock_api_up.h
#define _raw_spin_lock(lock)            __LOCK(lock)			//单CPU中的内核实现

#define __LOCK(lock) \
  do { preempt_disable(); ___LOCK(lock); } while (0)			//禁止抢占

#define ___LOCK(lock) \
  do { __acquire(lock); (void)(lock); } while (0)				

// include/linux/compiler.h
#ifdef __CHECKER__
# define __acquire(x)   __context__(x,1)
#else
# define __acquire(x) (void)0									//最后__acquire是一个空函数
           

在单CPU中spin_lock()退化为prrmpt_disable(),如果用的内核不支持preempt,那么spin_lock()什么都不做。

单CPU系统中,spin_lock_irq()的实现

static __always_inline void spin_lock_irq(spinlock_t *lock)
{
    raw_spin_lock_irq(&lock->rlock);
}
#define raw_spin_lock_irq(lock)     _raw_spin_lock_irq(lock)

#define _raw_spin_lock_irq(lock)        __LOCK_IRQ(lock)

#define __LOCK_IRQ(lock) \
  do { local_irq_disable(); __LOCK(lock); } while (0)		//这里先关中断,在调用__LOCK()

#define __LOCK(lock) \
  do { preempt_disable(); ___LOCK(lock); } while (0)		//接着调用preempt_disable()
           

单CPU系统中,spin_lock_bh()的实现

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

#define raw_spin_lock_bh(lock)      _raw_spin_lock_bh(lock)

#define _raw_spin_lock_bh(lock)         __LOCK_BH(lock)

#define __LOCK_BH(lock) \
  do { __local_bh_disable_ip(_THIS_IP_, SOFTIRQ_LOCK_OFFSET); ___LOCK(lock); } while (0)

/*
 * The preempt_count offset needed for things like:
 *
 *  spin_lock_bh()
 *
 * Which need to disable both preemption (CONFIG_PREEMPT_COUNT) and		//这里既禁止preempt,又禁止softirq
 * softirqs, such that unlock sequences of:
 *
 *  spin_unlock();
 *  local_bh_enable();
 *
 * Work as expected.
 */
#define SOFTIRQ_LOCK_OFFSET (SOFTIRQ_DISABLE_OFFSET + PREEMPT_LOCK_OFFSET)
           

单CPU系统中,spin_lock_irqsave()的实现

#define __LOCK_IRQSAVE(lock, flags) \
  do { local_irq_save(flags); __LOCK(lock); } while (0)			//这里和irq类似,就是先irqsave在preempt_disable()

#define __LOCK(lock) \
  do { preempt_disable(); ___LOCK(lock); } while (0)

           

spinlock在SMP系统中的实现

在ARMv6及以上的ARM架构中,支持SMP系统,它的spinlock结构体定义如下:

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;


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;

typedef struct {
    union {
        u32 slock;
        struct __raw_tickets {
#ifdef __ARMEB__
            u16 next;
            u16 owner;
#else
            u16 owner;
            u16 next;
#endif
        } tickets;
    };
} arch_spinlock_t;
//旧版本的内核spin lock结构体只有一个u32 ,为0则可以获得锁,不为0就自旋等待。 新的自旋锁加了next和owner让线程可以按获取锁的顺序排队。
           

在多CPU系统中, spin_lock的实现

// include/linux/spinlock.h
#define raw_spin_lock(lock) _raw_spin_lock(lock)

// include/llocking/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

// 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);
}

// 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);
} 

// arch/arm/include/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"				//读取lock->slock
"   add %1, %0, %4\n"				//新取的加1
"   strex   %2, %1, [%3]\n"			//新的内容写入到newval中
"   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();						//原地休息一会,CPU低功耗运行
        lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);		//一定间隔再看一下owner
    }
	//跳出循环了说明获得了锁 owner相等了
    smp_mb();
}
           

访问临界资源后,调用spin_unlock

// arch/arm/include/asm/spinlock.h
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
    smp_mb();
    lock->tickets.owner++;				//不可能有多个程序同时修改owner,所以不需要加什么互斥措施
    dsb_sev();		//假如有其他程序正在spin_lock函数中循环,就立刻判断手上next是否等于lock->tickets.owner,如果相等就获得锁
}
           

参考资料

深入分析_linux_spinlock_实现机制

https://blog.csdn.net/electrombile/article/details/51289813

深入分析Linux自旋锁

http://blog.chinaunix.net/uid-20543672-id-3252604.html

Linux内核同步机制之(四):spin lock

http://www.wowotech.net/kernel_synchronization/spinlock.html

继续阅读