arm上ldrex和strexeq指令用来尝试获取独占内存权限和设置在独占权限时回写

【duan注】关于LDREX 和 STREX这两对指令的具体使用方法,及其作用(独占访问存储器),请查看 DUI0204IC_rvct_assembler_guide.pdf  手册的148页。


 

__raw_spin_lock在ARM处理器上的实现

/******include/asm-arm/spinlock_types.h***/

 typedef struct {
        volatile unsigned int lock;
        } raw_spinlock_t;

#define __RAW_SPIN_LOCK_UNLOCKED { 0 }

/******include/asm-arm/spinlock.h***/

#if __LINUX_ARM_ARCH__ < 6
        #error SMP not supported on pre-ARMv6 CPUs //ARMv6后,才有多核ARM处理器
        #endif
        ……
        static inline void __raw_spin_lock(raw_spinlock_t *lock)
        {
                unsigned long tmp;
                __asm__ __volatile__(
        "1: ldrex        %0, [%1]\n"
        //取lock->lock放在 tmp里,并且设置&lock->lock这个内存地址为独占访问
        "        teq %0, #0\n"
        // 测试lock_lock是否为0,影响标志位z
        #ifdef CONFIG_CPU_32v6K
        "        wfene\n"
        #endif
        "        strexeq %0, %2, [%1]\n"
        //如果lock_lock是0,并且是独占访问这个内存,就向lock->lock里写入1,并向tmp返回0,同时清除独占标记
        "        teqeq %0, #0\n"
        //如果lock_lock是0,并且strexeq返回了0,表示加锁成功,返回
        " bne 1b"
        //如果上面的条件(1:lock->lock里不为0,2:strexeq失败)有一个符合,就在原地打转
                : "=&r" (tmp) //%0:输出放在tmp里,可以是任意寄存器
                : "r" (&lock->lock), "r" (1) 
        //%1:取&lock->lock放在任意寄存器,%2:任意寄存器放入1
                : "cc"); //状态寄存器可能会改变
                smp_mb();
        }

上述代码关键在于LDREX和STREX指令的应用。DREX和STREX指令是在V6以后才出现的,代替了V6以前的 swp指令。可以让bus监控LDREX和STREX指令之间有无其它CPU和DMA来存取过这个地址,若有的话STREX指令的第一个寄存器里设置为 1(动作失败),若没有,指令的第一个寄存器里设置为0(动作成功)。

不仅是自旋锁用到LDREX和STREX指令,信号量的实现也是利用LDREX和STREX指令来实现的。



文件:
Spinlock_types.h (\kernel\arch\arm\include\asm)


#define TICKET_SHIFT<span style="white-space:pre">	</span>16  //在lock时,用到它来讲0x100加到slock上,也即加到next成员上


typedef struct {
<span style="white-space:pre">	</span>union {
<span style="white-space:pre">		</span>u32 slock;  //当spin lock成功后,slock的高16位的值比低16位值大1,否则两部分的的值相等
<span style="white-space:pre">		</span>struct __raw_tickets { 
#ifdef __ARMEB__
<span style="white-space:pre">			</span>u16 next;
<span style="white-space:pre">			</span>u16 owner;
#else
<span style="white-space:pre">			</span>u16 owner;  //与slock成员构成了一个union体。owner是低地址
<span style="white-space:pre">			</span>u16 next;
#endif
<span style="white-space:pre">		</span>} tickets;
<span style="white-space:pre">	</span>};
} arch_spinlock_t;




文件:
Spinlock.h (\kernel\arch\arm\include\asm)


//枷锁
static inline void arch_spin_lock(arch_spinlock_t *lock)  
{
<span style="white-space:pre">	</span>unsigned long tmp, flags = 0;
<span style="white-space:pre">	</span>u32 newval;
<span style="white-space:pre">	</span>arch_spinlock_t lockval;
<span style="white-space:pre">	</span>//从以下可以看出,当spin lock成功后,slock的高16位的值比低16位值大1.
<span style="white-space:pre">	</span>__asm__ __volatile__(
"1:<span style="white-space:pre">	</span>ldrex<span style="white-space:pre">	</span>%0, [%3]\n"   //lockval=lock->slock
"<span style="white-space:pre">	</span>add<span style="white-space:pre">	</span>%1, %0, %4\n"    //newval=lockval+0x100
"<span style="white-space:pre">	</span>strex<span style="white-space:pre">	</span>%2, %1, [%3]\n" //lock->slock=newval;如果LDREX和STREX指令之间无其它CPU和DMA来存取过这个地址&lock->slock,则STREX返回0表示枷锁成功(否则返回1),返回值放到tmp中
"<span style="white-space:pre">	</span>teq<span style="white-space:pre">	</span>%2, #0\n"  //检查tmp的值
"<span style="white-space:pre">	</span>bne<span style="white-space:pre">	</span>1b"
<span style="white-space:pre">	</span>: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
<span style="white-space:pre">	</span>: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
<span style="white-space:pre">	</span>: "cc");


<span style="white-space:pre">	</span>while (lockval.tickets.next != lockval.tickets.owner) {
<span style="white-space:pre">		</span>if (msm_krait_need_wfe_fixup) {  //不满足
<span style="white-space:pre">			</span>local_save_flags(flags);
<span style="white-space:pre">			</span>local_fiq_disable();
<span style="white-space:pre">			</span>__asm__ __volatile__(
<span style="white-space:pre">			</span>"mrc<span style="white-space:pre">	</span>p15, 7, %0, c15, c0, 5\n"
<span style="white-space:pre">			</span>: "=r" (tmp)
<span style="white-space:pre">			</span>:
<span style="white-space:pre">			</span>: "cc");
<span style="white-space:pre">			</span>tmp &= ~(0x10000);
<span style="white-space:pre">			</span>__asm__ __volatile__(
<span style="white-space:pre">			</span>"mcr<span style="white-space:pre">	</span>p15, 7, %0, c15, c0, 5\n"
<span style="white-space:pre">			</span>:
<span style="white-space:pre">			</span>: "r" (tmp)
<span style="white-space:pre">			</span>: "cc");
<span style="white-space:pre">			</span>isb();
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>wfe();
<span style="white-space:pre">		</span>if (msm_krait_need_wfe_fixup) {
<span style="white-space:pre">			</span>tmp |= 0x10000;
<span style="white-space:pre">			</span>__asm__ __volatile__(
<span style="white-space:pre">			</span>"mcr<span style="white-space:pre">	</span>p15, 7, %0, c15, c0, 5\n"
<span style="white-space:pre">			</span>:
<span style="white-space:pre">			</span>: "r" (tmp)
<span style="white-space:pre">			</span>: "cc");
<span style="white-space:pre">			</span>isb();
<span style="white-space:pre">			</span>local_irq_restore(flags);
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
<span style="white-space:pre">	</span>}


<span style="white-space:pre">	</span>smp_mb();
}


//解锁
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
<span style="white-space:pre">	</span>smp_mb();
<span style="white-space:pre">	</span>lock->tickets.owner++;  //owner++使得它与next的值相等
<span style="white-space:pre">	</span>dsb_sev();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值