Linux Kernel Atomic解析
Atomic
- 1. Atomic
- 1.1 SMP read / write
- 1.1.1 Read
- 1.1.2 Write
- 1.1.3 相關接口
- 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
- 小結
- 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函數,通過READ_ONCE和WRITE_ONCE直接讀寫。可以直接讀寫,而不需要考慮原子性的原因是:同一時刻只允許一個cpu訪問外設。
??如上圖所示,當cpu0與cpu1同時請求訪問ddr時,同一時刻總線只會響應一個請求,例如cpu0的request,此時bus是鎖定狀態;當cpu0訪問結束,ddr將鎖放開后,cpu1的請求才會得到ddr響應。
1.1.3 相關接口
atomic_read(v) atomic_set(v, i)1.1.4 READ_ONCE / WRITE_ONCE
??將讀取的變量使用volatile宏修飾,讓cpu每次都從內存中讀取數據。
#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(); \} \ })??傳入的第二個參數check為1,調用__read_once_size。
??smp_read_barrier_depends是提供給架構實現內存隔離的函數,只有alpha和blackfin定義了,其余架構為空函數。
??返回值為__u.__val,首先__u是一個union類型,在賦值時使用的是char __c,在返回時使用的是typeof(x) __val,它定義了一個與x變量類型相同的__val變量。
??針對u8 / u16 / u32 / u64大小的數據,使用volatile修飾,保證編譯器不優化,每次都從內存中讀取。對于其它大小的數據,在讀的前后調用barrier,barrier的功能為內存屏障,執行后,cpu會從內存中讀取數據。
??與讀類似,WRITE_ONCE的實現如下:
#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
??再考慮另外一個問題,atomic的加減是否需要特殊保護?
??如下圖所示,這種情況下就會發生讀寫的非原子性操作。
??變量a在cpu0和cpu1分別++后,a應該變為3,但由于同時對a的非原子操作,導致重復的將a++的結果2寫入內存。
??為了保證atomic變量加減的原子性操作,atomic提供的相關函數。
1.2.1 atomic_add
??首先,kernel提供了通用性定義,位于include/asm-generic/atomic.h中,實現如下:
include/asm-generic/atomic.hstatic inline void atomic_add(int i, atomic_t *v) {atomic_add_return(i, v); }??繼續查看atomic_add_return的實現
1.2.1.1 atomic_add_return
??這個函數的實現有兩個地方,一處為atomic-instrumented.h,一處為atomic-fallback.h。
??在arch_atomic_add_return_relaxed未定義或arch_atomic_add_return已定義時,使用instrumented.h中的定義。
??先來看一下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中條件不滿足,未定義時。使用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
??該函數只在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)??主要調查arm64的,該系列函數采用的是拼接定義的方法,如下:
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為例,宏展開后,依次調用
__lse_ll_sc_body(atomic_add_return_relaxed, i, v); //i為將要累加的數值,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下,其余架構不支持,它的作用是給armv8.1的新feature:LSE(Large System Extensions) 提供支持,實現如下:
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時,通過調用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能力的話,就調用:
__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);??否則調用:
__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);??繼續查看__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是宏定義的展開形式, 宏名稱為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")??回過頭來看它的實現
??1) __LSE_PREAMBLE
????作用是告訴編譯器,下面的是lse指令。
??2) ldadd
????通過一條指令完成,從內存中取值,計算,存儲。
有關LSE參考另外一篇文章
小結
??atomic_add在arm64或x86平臺中使用的是atomic-instrumented.h中的定義,其它平臺使用的是atomic-fallback.h中的定義。
1.2.2 atomic_sub
static inline void atomic_sub(int i, atomic_t *v) {atomic_sub_return(i, v); }總結
以上是生活随笔為你收集整理的Linux Kernel Atomic解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CDH5 安装需求和相关软件支持的版本信
- 下一篇: Unity Kinect体感跑酷互动游戏