Linux Kernel Atomic解析
Atomic
- 1. Atomic
- 1.1 SMP read / write
- 1.1.1 Read
- 1.1.2 Write
- 1.1.3 相關(guān)接口
- 1.1.4 READ_ONCE / WRITE_ONCE
- 1.2 SMP add / sub
- 1.2.1 atomic_add
- 1.2.1.1 atomic_add_return
- 1.2.1.2 arch_atomic_add_return_relaxed
- 1.2.1.3 arch_atomic_add_return
- 小結(jié)
- 1.2.2 atomic_sub
1. Atomic
1.1 SMP read / write
1.1.1 Read
include/asm-generic/atomic.h#ifndef atomic_read #define atomic_read(v) READ_ONCE((v)->counter) #endif1.1.2 Write
include/asm-generic/atomic.h#define atomic_set(v, i) WRITE_ONCE(((v)->counter), (i))??atomic_read和atomic_write函數(shù),通過(guò)READ_ONCE和WRITE_ONCE直接讀寫??梢灾苯幼x寫,而不需要考慮原子性的原因是:同一時(shí)刻只允許一個(gè)cpu訪問(wèn)外設(shè)。
??如上圖所示,當(dāng)cpu0與cpu1同時(shí)請(qǐng)求訪問(wèn)ddr時(shí),同一時(shí)刻總線只會(huì)響應(yīng)一個(gè)請(qǐng)求,例如cpu0的request,此時(shí)bus是鎖定狀態(tài);當(dāng)cpu0訪問(wèn)結(jié)束,ddr將鎖放開(kāi)后,cpu1的請(qǐng)求才會(huì)得到ddr響應(yīng)。
1.1.3 相關(guān)接口
atomic_read(v) atomic_set(v, i)1.1.4 READ_ONCE / WRITE_ONCE
??將讀取的變量使用volatile宏修飾,讓cpu每次都從內(nèi)存中讀取數(shù)據(jù)。
#define READ_ONCE(x) __READ_ONCE(x, 1)#define __READ_ONCE(x, check) \ ({ \union { typeof(x) __val; char __c[1]; } __u; \if (check) \__read_once_size(&(x), __u.__c, sizeof(x)); \else \__read_once_size_nocheck(&(x), __u.__c, sizeof(x)); \smp_read_barrier_depends(); /* Enforce dependency ordering from x */ \__u.__val; \ })static __always_inline void __read_once_size(const volatile void *p, void *res, int size) {__READ_ONCE_SIZE; }#define __READ_ONCE_SIZE \ ({ \switch (size) { \case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \case 4: *(__u32 *)res = *(volatile __u32 *)p; break; \case 8: *(__u64 *)res = *(volatile __u64 *)p; break; \default: \barrier(); \__builtin_memcpy((void *)res, (const void *)p, size); \barrier(); \} \ })??傳入的第二個(gè)參數(shù)check為1,調(diào)用__read_once_size。
??smp_read_barrier_depends是提供給架構(gòu)實(shí)現(xiàn)內(nèi)存隔離的函數(shù),只有alpha和blackfin定義了,其余架構(gòu)為空函數(shù)。
??返回值為_(kāi)_u.__val,首先__u是一個(gè)union類型,在賦值時(shí)使用的是char __c,在返回時(shí)使用的是typeof(x) __val,它定義了一個(gè)與x變量類型相同的__val變量。
??針對(duì)u8 / u16 / u32 / u64大小的數(shù)據(jù),使用volatile修飾,保證編譯器不優(yōu)化,每次都從內(nèi)存中讀取。對(duì)于其它大小的數(shù)據(jù),在讀的前后調(diào)用barrier,barrier的功能為內(nèi)存屏障,執(zhí)行后,cpu會(huì)從內(nèi)存中讀取數(shù)據(jù)。
??與讀類似,WRITE_ONCE的實(shí)現(xiàn)如下:
#define WRITE_ONCE(x, val) \ ({ \union { typeof(x) __val; char __c[1]; } __u = \{ .__val = (__force typeof(x)) (val) }; \__write_once_size(&(x), __u.__c, sizeof(x)); \__u.__val; \ })static __always_inline void __write_once_size(volatile void *p, void *res, int size) {switch (size) {case 1: *(volatile __u8 *)p = *(__u8 *)res; break;case 2: *(volatile __u16 *)p = *(__u16 *)res; break;case 4: *(volatile __u32 *)p = *(__u32 *)res; break;case 8: *(volatile __u64 *)p = *(__u64 *)res; break;default:barrier();__builtin_memcpy((void *)p, (const void *)res, size);barrier();} }1.2 SMP add / sub
??再考慮另外一個(gè)問(wèn)題,atomic的加減是否需要特殊保護(hù)?
??如下圖所示,這種情況下就會(huì)發(fā)生讀寫的非原子性操作。
??變量a在cpu0和cpu1分別++后,a應(yīng)該變?yōu)?,但由于同時(shí)對(duì)a的非原子操作,導(dǎo)致重復(fù)的將a++的結(jié)果2寫入內(nèi)存。
??為了保證atomic變量加減的原子性操作,atomic提供的相關(guān)函數(shù)。
1.2.1 atomic_add
??首先,kernel提供了通用性定義,位于include/asm-generic/atomic.h中,實(shí)現(xiàn)如下:
include/asm-generic/atomic.hstatic inline void atomic_add(int i, atomic_t *v) {atomic_add_return(i, v); }??繼續(xù)查看atomic_add_return的實(shí)現(xiàn)
1.2.1.1 atomic_add_return
??這個(gè)函數(shù)的實(shí)現(xiàn)有兩個(gè)地方,一處為atomic-instrumented.h,一處為atomic-fallback.h。
??在arch_atomic_add_return_relaxed未定義或arch_atomic_add_return已定義時(shí),使用instrumented.h中的定義。
??先來(lái)看一下atomic-instrumented.h中的定義:
include/asm-generic/atomic-instrumented.h#if !defined(arch_atomic_add_return_relaxed) || defined(arch_atomic_add_return) static inline int atomic_add_return(int i, atomic_t *v) {kasan_check_write(v, sizeof(*v));return arch_atomic_add_return(i, v); } #define atomic_add_return atomic_add_return #endif??在atomic-instrumented.h中條件不滿足,未定義時(shí)。使用atomic-fallback.h中的定義:
include/linux/atomic-fallback.h#ifndef atomic_add_return static inline int atomic_add_return(int i, atomic_t *v) {int ret;__atomic_pre_full_fence();ret = atomic_add_return_relaxed(i, v);__atomic_post_full_fence();return ret; } #define atomic_add_return atomic_add_return #endif1.2.1.2 arch_atomic_add_return_relaxed
??只有一處定義位置,在arm64中:
arch/arm64/include/asm/atomic.h#define arch_atomic_add_return_relaxed arch_atomic_add_return_relaxed1.2.1.3 arch_atomic_add_return
??該函數(shù)只在arm64和x86中有定義:
1 110 arch/arm64/include/asm/atomic.h <<arch_atomic_add_return>>#define arch_atomic_add_return arch_atomic_add_return2 165 arch/x86/include/asm/atomic.h <<arch_atomic_add_return>>static __always_inline int arch_atomic_add_return(int i, atomic_t *v)??主要調(diào)查arm64的,該系列函數(shù)采用的是拼接定義的方法,如下:
arch/arm64/include/asm/atomic.hATOMIC_FETCH_OPS(atomic_add_return)#define ATOMIC_FETCH_OPS(op) \ATOMIC_FETCH_OP(_relaxed, op) \ATOMIC_FETCH_OP(_acquire, op) \ATOMIC_FETCH_OP(_release, op) \ATOMIC_FETCH_OP( , op)#define ATOMIC_FETCH_OP(name, op) \ static __always_inline int arch_##op##name(int i, atomic_t *v) \ { \return __lse_ll_sc_body(op##name, i, v); \ }??以arch_atomic_add_return為例,宏展開(kāi)后,依次調(diào)用
__lse_ll_sc_body(atomic_add_return_relaxed, i, v); //i為將要累加的數(shù)值,v為atomic類型變量 __lse_ll_sc_body(atomic_add_return_acquire, i, v); __lse_ll_sc_body(atomic_add_return_release, i, v); __lse_ll_sc_body(atomic_add_return, i, v);??__lse_ll_sc_body只定義在arm64下,其余架構(gòu)不支持,它的作用是給armv8.1的新feature:LSE(Large System Extensions) 提供支持,實(shí)現(xiàn)如下:
arch/arm64/include/asm/lse.h#if defined(CONFIG_AS_LSE) && defined(CONFIG_ARM64_LSE_ATOMICS) #define __lse_ll_sc_body(op, ...) \ ({ \system_uses_lse_atomics() ? \__lse_##op(__VA_ARGS__) : \__ll_sc_##op(__VA_ARGS__); \ }) #else /* CONFIG_AS_LSE && CONFIG_ARM64_LSE_ATOMICS */ #define __lse_ll_sc_body(op, ...) __ll_sc_##op(__VA_ARGS__) #endif /* CONFIG_AS_LSE && CONFIG_ARM64_LSE_ATOMICS */??在配置了CONFIG_AS_LSE & CONFIG_ARM64_LSE_ATOMICS時(shí),通過(guò)調(diào)用system_uses_lse_atomics判斷cpu是否具備LSE能力。
static inline bool system_uses_lse_atomics(void) {return (static_branch_likely(&arm64_const_caps_ready)) &&static_branch_likely(&cpu_hwcap_keys[ARM64_HAS_LSE_ATOMICS]); }??如果cpu具備LSE能力的話,就調(diào)用:
__lse_atomic_add_return_relaxed(i, v); __lse_atomic_add_return_acquire(i, v); __lse_atomic_add_return_release(i, v); __lse_atomic_add_return(i, v);??否則調(diào)用:
__ll_sc_atomic_add_return_relaxed(i, v); __ll_sc_atomic_add_return_acquire(i, v); __ll_sc_atomic_add_return_release(i, v); __ll_sc_atomic_add_return(i, v);??繼續(xù)查看__lse_atomic_add_return的定義,同樣采用的是拼接定義的方法,如下:
arch/arm64/include/asm/atomic_lse.h#define ATOMIC_OP_ADD_RETURN(name, mb, cl...) \ static inline int __lse_atomic_add_return##name(int i, atomic_t *v) \ { \u32 tmp; \\asm volatile( \__LSE_PREAMBLE \" ldadd" #mb " %w[i], %w[tmp], %[v]\n" \" add %w[i], %w[i], %w[tmp]" \: [i] "+r" (i), [v] "+Q" (v->counter), [tmp] "=&r" (tmp) \: "r" (v) \: cl); \\return i; \ }??__lse_atomic_add_return##name是宏定義的展開(kāi)形式, 宏名稱為ATOMIC_OP_ADD_RETURN,定義_relaxed / _acquire / _release / ’ ’ 的位置如下:
ATOMIC_OP_ADD_RETURN(_relaxed, ) ATOMIC_OP_ADD_RETURN(_acquire, a, "memory") ATOMIC_OP_ADD_RETURN(_release, l, "memory") ATOMIC_OP_ADD_RETURN( , al, "memory")??回過(guò)頭來(lái)看它的實(shí)現(xiàn)
??1) __LSE_PREAMBLE
????作用是告訴編譯器,下面的是lse指令。
??2) ldadd
????通過(guò)一條指令完成,從內(nèi)存中取值,計(jì)算,存儲(chǔ)。
有關(guān)LSE參考另外一篇文章
小結(jié)
??atomic_add在arm64或x86平臺(tái)中使用的是atomic-instrumented.h中的定義,其它平臺(tái)使用的是atomic-fallback.h中的定義。
1.2.2 atomic_sub
static inline void atomic_sub(int i, atomic_t *v) {atomic_sub_return(i, v); }總結(jié)
以上是生活随笔為你收集整理的Linux Kernel Atomic解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CDH5 安装需求和相关软件支持的版本信
- 下一篇: Linux 服务器乱码问题解决