Analysis of Implementation of spinlock on arm and x86 platforms in Linux

Source: Internet
Author: User

Author: Liu Hongtao,Hua Qing vision embedded college lecturer.

This article mainly uses the 2.6.22.6 kernel to analyze the implementation of spinlock on the arm and x86 platforms in Linux (there are some differences in kernel implementation forms in different versions, but the principles are roughly the same ). By default, you are familiar with the use of the spinlock, and focus on the implementation of the architecture that is easy to confuse.

I. Implementation of spin_lock (LOCK)

/*** Include/Linux/spinlock. h ***/

# If defined (config_smp) | defined (config_debug_spinlock)
// If SMP or spin lock debugging is configured
# Include <Linux/spinlock_api_smp.h>
# Else // if it is a single processor and the spin lock debugging function is not configured
# Include <Linux/spinlock_api_up.h>
# Endif
......
# Define spin_lock (LOCK) _ spin_lock (LOCK)

1. For a single processor

/***** Include/Linux/spinlock_api_up.h ****/

# DEFINE _ spin_lock (LOCK) _ Lock (Lock)
# DEFINE _ Lock (Lock )/
Do {preempt_disable (); _ acquire (Lock); (void) (Lock);} while (0)

(1) preempt_disable (): preemptible
(2) _ acquire (LOCK): defined in include/Linux/compiler. h
# Ifdef _ checker __
......
# DEFINE _ acquire (x) _ context _ (x, 1)
# DEFINE _ release (x) _ context _ (x,-1)
# Else
......
# DEFINE _ acquires (X)
# DEFINE _ Releases (X)

This is a pair of interrelated function definitions used by sparse for code detection. The first sentence indicates to increase the Count of variable X. The increment is 1, and the second sentence is the opposite, this is used for function compilation. If an imbalance occurs in the code, an alarm is triggered during sparse detection. If you want to use the sparse detection function, you need to install the sparse tool (refer to the relevant installation methods), and then compile the kernel

# Make zimage c = 1 (C = 1, only the newly compiled files are detected, and c = 2 is to check all files)
Sparse defines _ checker __. if you do not use the sparse tool, __acquire (LOCK) is defined as null.

(3) (void) (LOCK): Insert a value expression of the variable itself so that the compiler does not trigger an alarm, for example, "variable 'lock' is defined but never used ". This evaluation does not affect the running speed.

2. If SMP is configured

/***** 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 ();
// Disable Preemption
Spin_acquire (& lock-> dep_map, 0, 0, _ ret_ip _);
// For spin lock debugging, it is an empty function when no spin lock debugging is defined
_ Raw_spin_lock (Lock );
}

/*** Include/Linux/spinlock. h ***/

# Ifdef config_debug_spinlock
Extern void _ raw_spin_lock (spinlock_t * Lock); // implemented in lib/spinlock_debug.c
# Else // SMP
# DEFINE _ raw_spin_lock (LOCK) _ raw_spin_lock (& (LOCK)-> raw_lock)

3. Implementation of _ raw_spin_lock on arm Processors

/****** 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 only after multi-core ARM processor
# Endif
......
Static inline void _ raw_spin_lock (raw_spinlock_t * Lock)
{
Unsigned long TMP;
_ ASM _ volatile __(
"1: ldrex % 0, [% 1]/n"
// Put lock-> lock in TMP, and set & lock-> lock to the memory address for exclusive access.
"TEQ % 0, #0/N"
// Test whether lock_lock is 0 and affects the flag Z.
# Ifdef config_cpu_32v6k
"Wfene/N"
# Endif
"Strexeq % 0, % 2, [% 1]/n"
// If lock_lock is 0 and exclusively accesses this memory, write 1 to lock-> lock and return 0 to TMP. At the same time, clear the exclusive mark.
"Teqeq % 0, #0/N"
// If lock_lock is 0 and strexeq returns 0, it indicates that the lock is successful and return
"BNE 1B"
// If one of the above conditions (1: Lock-> lock is not 0, 2: strexeq failed) is met, it will be replayed in the original place.
: "= & R" (TMP) // % 0: the output is placed in TMP, which can be any register.
: "R" (& lock-> lock), "R" (1)
// % 1: Put & lock-> lock in any register, % 2: put any register into 1
: "Cc"); // The Status Register may change.
Smp_mb ();
}

The key to the above Code lies in the application of ldrex and strex commands. The Drex and strex commands appear after V6, instead of the SWP commands before V6. It allows the bus to monitor whether there are other CPUs and DMA between ldrex and strex commands to access this address. If so, the first register of the strex command is set to 1 (action failed ), if no, set it to 0 in the first register of the instruction (the action is successful ).

Not only does the spin lock use the ldrex and strex commands, but also uses the ldrex and strex commands to implement the semaphore.

4. Implementation of _ raw_spin_lock on x86 Processors

/***** Include/asm-i386/spinlock_types.h ***/

Typedef struct {
Unsigned int slock;
} Raw_spinlock_t;
# DEFINE _ raw_spin_lock_unlocked {1}

/***** Include/asm-i386/spinlock. h ***/

Static inline void _ raw_spin_lock (raw_spinlock_t * Lock)
{
ASM volatile ("/N1:/t"
Lock_prefix "; decb % 0/n/t"
// Lock-> slock minus 1
"JNS 3f/N"
// If it is not negative, jump to 3f. 3f without any instructions, that is, exit
"2:/t"
"Rep; NOP/n/t"
// Repeated execution of Nop. Nop is a small latency function of X86.
"Cmpb $0, % 0/n/t"
"Jle 2B/n/t"
// If the lock-> slock value is not greater than 0, jump to the label 2, that is, continue to execute NOP again
"JMP 1B/N"
// If the lock-> slock is greater than 0, jump to the label 1 and re-judge the slock Member of the lock.
"3:/n/t"
: "+ M" (lock-> slock): "Memory ");
}

In a multi-processor environment, lock_prefix is actually defined as the "Lock" prefix. The x86 processor uses the "Lock" prefix to provide a means to lock the bus during command execution. There is a lead lock on the chip. If a assembly command (ADD, ADC, And, BTC, BTR, BTS, cmpxchg, cmpxch8b, Dec, Inc, neg, not, or, SBB, sub, XOR, xadd, xchg) prefix with "Lock". The compiled machine code lowers the potential of lead lock when the processor executes this command, thus, the bus is locked, so that other processors or peripherals using DMA are temporarily unable to access the memory through the same bus.

The JNS Assembly command checks the SF (Symbol) Bit Of The eflags register. If it is 0, it indicates that the original slock value is 1, the thread gets the lock, then, jump to the position of tag 3 to end the function call. If the SF bit is 1, the original slock value is 0 or negative, and the lock is occupied. The thread continuously tests the relationship between slock and 0 at tag 2. If slock is smaller than or equal to 0, it jumps to tag 2 and continues to wait. If slock is greater than 0, if the lock has been released, the lock will be applied again at the position of tag 1.

II. Implementation of spin_unlock (LOCK)

/*** Include/Linux/spinlock. h ***/

# If defined (config_debug_spinlock) | defined (config_preempt) |/
! Defined (config_smp)
# Define spin_unlock (LOCK) _ spin_unlock (LOCK)
......
# Else
# Define spin_unlock (LOCK )/
Do {__ raw_spin_unlock (& (LOCK)-> raw_lock); _ release (Lock);} while (0)

1. For a single processor

/***** Include/Linux/spinlock_api_up.h ****/

# DEFINE _ spin_unlock (LOCK) _ unlock (LOCK)
# DEFINE _ unlock (LOCK )/
Do {preempt_enable (); _ release (Lock); (void) (Lock);} while (0)

Complete the previous process of getting the lock.

2. If SMP is configured

# Define spin_unlock (LOCK )/
Do {__ raw_spin_unlock (& (LOCK)-> raw_lock); _ release (Lock);} while (0)

3. Implementation of _ raw_spin_unlock on arm Processors

/****** Include/ASM-arm/spinlock. h ***/

Static inline void _ raw_spin_unlock (raw_spinlock_t * Lock)
{
Smp_mb ();
_ ASM _ volatile __(
"Str % 1, [% 0]/n" // write 0 to lock-> lock to unlock
# Ifdef config_cpu_32v6k
"MCR P15, 0, % 1, C7, C10, 4/N"/* DSB */
"Sev"
# Endif
:
: "R" (& lock-> lock), "R" (0) // % 0 get & lock-> lock put in any register, % 1: Put any register into 0
: "Cc ");
}

_ Raw_spin_unlock simply writes 0 to lock-> lock.

4. Implementation of _ raw_spin_unlock on x86 Processors

/*** Include/asm-i386/spinlock. h ***/

Static inline void _ raw_spin_unlock (raw_spinlock_t * Lock)
{
ASM volatile ("movb $1, % 0": "+ M" (lock-> slock): "Memory ");
}

The _ raw_spin_unlock function executes only one assembly command: Set lock-> slock to 1.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.