spinlock是用于线程间同步的自旋锁,由于我们希望使用BF561的两个核,因此它就显得极为重要。
1 定义
在内核中声明一个spinlock可以使用
DEFINE_SPINLOCK这个宏定义,它的定义在include/linux/spinlock_types.h中:
#define
DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
其中spinlock_t是在include/linux/spinlock_types.h中定义的一个结构体:
typedef
struct {
raw_spinlock_t raw_lock;
#if
defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
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
} spinlock_t;
因为我们只定义了CONFIG_SMP,因此这个结构体实际就是:
typedef
struct {
raw_spinlock_t raw_lock;
} spinlock_t;
raw_spinlock_t的定义在include/asm/spinlock_types.h中:
typedef
struct {
volatile unsigned int lock;
} raw_spinlock_t;
在此之前,我们只是简单地将其设置为
volatile
unsigned int的类型,而现在,我们则希望借助于VDSP的库进行核间同步,因此将其定义改为:
typedef
struct {
testset_t lock;
} raw_spinlock_t;
因为testset_t自动就是volatile类型的,在此就不用声明了。
2 VDSP对核间同步的支持
以下内容来自VDSP的帮助:
Synchronization Functions
VisualDSP++ 5.0 provides functionality for synchronization. There are two compiler intrinsics (built-in functions) and three locking routines.
The compiler intrinsics are:
#include <ccblkfn.h>
int testset(char *);
void untestset(char *);
The
testset() intrinsic generates a native TESTSET instruction, which can perform atomic updates on a memory location. The intrinsic returns the result of the CC flag produced by the TESTSET instruction. Refer to the instruction set reference for details.
The
untestset() intrinsic clears the memory location set by the
testset() intrinsic. This intrinsic is recommended in place of a normal memory write because the untestset() intrinsic acts as a stronger barrier to code movement during optimization.
The three locking routines are:
#include <ccblkfn.h>
void adi_acquire_lock(testset_t *);
int adi_try_lock(testset_t *);
void adi_release_lock(testset_t *);
The
adi_acquire_lock() routine repeatedly attempts to claim the lock by issuing testset() until successful, whereupon it returns to the caller. In contrast, the adi_try_lock() routine makes a single attempt—if it successfully claims the lock, it returns nonzero, otherwise it returns zero.
The
adi_release_lock() routine releases the lock obtained by either adi_acquire_lock() or adi_try_lock(). It assumes that the lock was already claimed and makes no attempt to verify that its caller is in fact the current owner of the lock. None of these intrinsics or functions disable interrupts—that is left to the caller’s discretion.
3 spin_lock
spin_lock的定义在include/linux/spinlock.h中:
#define
spin_lock(lock) _spin_lock(lock)
_spin_lock的定义在include/linux/spinlock_api_smp.h中:
void
__lockfunc _spin_lock(spinlock_t *lock) __acquires(lock);
而其实现则在kernel/spinlock.c中:
void
__lockfunc _spin_lock(spinlock_t *lock)
{
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
_raw_spin_lock(lock);
}
_raw_spin_lock的实现则是不同的体系结构有不同的方法,但是其实现一般都放在asm/spinlock.h中,我们也这样做:
static
inline void __raw_spin_lock(raw_spinlock_t *lock)
{
adi_acquire_lock(&lock->lock);
}
4 .spinlock.text
uclinux为spinlock相关的函数定义了一个段:
#define
__lockfunc fastcall __attribute__((section(".spinlock.text")))
因此我们需要在LDF文件中加上它:
.text
{
INPUT_SECTION_ALIGN(4)
. = (. + 3) / 4 * 4;
__text = .;
_text = .;
__stext = .;
INPUT_SECTIONS($OBJECTS_CORE_A(sdram_bank0) $LIBRARIES_CORE_A(sdram_bank0))
INPUT_SECTIONS($OBJECTS_CORE_A(VDK_ISR_code) $LIBRARIES_CORE_A(VDK_ISR_code))
INPUT_SECTIONS($OBJECTS_CORE_A(program) $LIBRARIES_CORE_A(program))
INPUT_SECTIONS($OBJECTS_CORE_A(noncache_code) $LIBRARIES_CORE_A(noncache_code))
INPUT_SECTIONS($LIBRARIES_CORE_A(.text.*))
INPUT_SECTIONS($LIBRARIES_CORE_A(.fixup))
INPUT_SECTIONS($LIBRARIES_CORE_A(.spinlock.text))
INPUT_SECTION_ALIGN(16)
. = (. + 15) / 16 * 16;
___start___ex_table = .;
INPUT_SECTIONS($LIBRARIES_CORE_A(__ex_table))
___stop___ex_table = .;
INPUT_SECTION_ALIGN(4)
. = (. + 3) / 4 * 4;
__etext = .;
} > MEM_SDRAM