← Architecture 비교DUJINLABS.COM

Linux 6.18.37 LTS · Architecture comparison 06/21

Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE

compiler barrier, SMP barrier, DMA barrier와 instruction-cache synchronization을 서로 다른 계약으로 나눕니다.

비교 대상
arm64 / x86-64 / RISC-V
실제 원본
3 files · 238 annotated lines
기준 tag
Linux v6.18.37
분석 축
state · ordering · lifetime · latency

01 · QUESTION

무엇을 확인할 것인가

한 CPU의 store가 다른 CPU나 DMA device에 보인다는 말은 어느 observer와 어느 순서를 기준으로 하는가?

Linux memory model은 acquire/release와 full barrier API를 제공하지만 ISA의 기본 ordering, shareability domain, device memory와 instruction fetch coherence가 구현을 결정한다.

producer store, barrier, publication flag와 consumer acquire load를 한 litmus test로 읽는다. cache clean/invalidate는 ordering barrier와 같은 연산이 아니다.

지연 시간 관점barrier 비용은 outstanding memory transaction, domain과 microarchitecture에 따라 달라진다. DSB처럼 completion까지 기다리는 명령과 ordering만 강제하는 DMB를 분리한다.

02 · CONTRACT

공통 계약과 architecture 구현

architecture핵심 mechanism실패 형태확인할 상태
arm64DMB/DSB/ISB와 shareability·load/store optiondmb ish만으로 device MMIO completion이나 instruction-cache 갱신까지 됐다고 오해한다.barrier 종류, domain, cache line, memory attribute와 observer CPU/device를 기록한다.
x86-64TSO 기본 ordering과 LFENCE/SFENCE/MFENCE·locked opx86은 strong ordering이라는 이유로 DMA/MMIO와 speculation barrier를 모두 빈 연산으로 처리한다.memory type, locked op, fence, store buffer와 device posted-write completion을 확인한다.
RISC-VFENCE predecessor/successor set과 aq/rl bitdata fence 뒤 새 code가 실행될 것으로 가정하거나 I/O bit가 빠진 fence로 MMIO를 publish한다.FENCE pred/succ, aq/rl, I/O vs memory, hart mask와 icache sync를 기록한다.

03 · DIAGRAMS

세 그림으로 먼저 읽기

그림 1. 같은 목적, 서로 다른 mechanism각 ISA에서 실제로 추적할 state와 checkpoint를 한 줄에 맞췄습니다.

arm64

mechanism
DMB/DSB/ISB와 shareability·load/store option
state
weakly ordered ISA라 dmb ish, ishld, ishst가 SMP order를 표현한다. device/DMA에는 outer-shareable domain이 필요하고 code patch 뒤에는 D-cache clean, I-cache invalidate와 ISB가 이어진다.
checkpoint
barrier 종류, domain, cache line, memory attribute와 observer CPU/device를 기록한다.

x86-64

mechanism
TSO 기본 ordering과 LFENCE/SFENCE/MFENCE·locked op
state
WB memory의 store-store/load-load는 강하지만 store buffer, non-temporal store, WC memory와 speculation 때문에 barrier가 완전히 사라지지는 않는다. SMP full barrier는 locked instruction으로 구현될 수 있다.
checkpoint
memory type, locked op, fence, store buffer와 device posted-write completion을 확인한다.

RISC-V

mechanism
FENCE predecessor/successor set과 aq/rl bit
state
RVWMO는 fence rw,rw, r,r, w,w로 observer order를 지정한다. AMO의 aq/rl은 해당 atomic 전후 ordering을 포함하고 instruction fetch에는 별도 fence.i가 필요하다.
checkpoint
FENCE pred/succ, aq/rl, I/O vs memory, hart mask와 icache sync를 기록한다.
그림 2. 공통 kernel과 architecture hook의 소유권공통 정책이 hardware state를 직접 소유하지 않는 경계를 표시합니다.
Linux common contractLinux memory model은 acquire/release와 full barrier API를 제공하지만 ISA의 기본 ordering, shareability domain, device memory와 instruction fetch coherence가 구현을 결정한다.
arm64DMB/DSB/ISB와 shareability·load/store optionrelease/acquire instruction과 barrier scope가 observer domain을 포함해야 한다.
x86-64TSO 기본 ordering과 LFENCE/SFENCE/MFENCE·locked opcompiler reorder와 CPU reorder를 구분하고 MMIO/WC에는 별도 API를 사용한다.
RISC-VFENCE predecessor/successor set과 aq/rl bitremote hart의 instruction cache는 local fence.i만으로 갱신되지 않을 수 있어 IPI/SBI 기반 sync가 필요하다.
lifetime boundarylock-free object는 publication 전 완성되어야 하고 consumer가 reference를 놓기 전 free되면 안 된다. DMA buffer는 CPU/device ownership 전환과 cache maintenance가 맞아야 한다.
그림 3. publication과 관찰 순서state를 준비한 뒤 architecture ordering을 거쳐 관찰 가능한 checkpoint가 됩니다.
arm64state 준비release/acquire instruction과 barrier scope가 observer domain을 포함해야 한다.관찰: barrier 종류, domain, cache line, memory attribute와 observer CPU/device를 기록한다.
x86-64state 준비compiler reorder와 CPU reorder를 구분하고 MMIO/WC에는 별도 API를 사용한다.관찰: memory type, locked op, fence, store buffer와 device posted-write completion을 확인한다.
RISC-Vstate 준비remote hart의 instruction cache는 local fence.i만으로 갱신되지 않을 수 있어 IPI/SBI 기반 sync가 필요하다.관찰: FENCE pred/succ, aq/rl, I/O vs memory, hart mask와 icache sync를 기록한다.

04 · SOURCE

Linux 6.18.37 원본 코드와 줄별 설명

소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 아래 코드와 각 줄의 설명은 1:1로 대응합니다.

arm64 · Linux 6.18.37

DMB/DSB/ISB와 shareability·load/store option

weakly ordered ISA라 dmb ish, ishld, ishst가 SMP order를 표현한다. device/DMA에는 outer-shareable domain이 필요하고 code patch 뒤에는 D-cache clean, I-cache invalidate와 ISB가 이어진다.

원본 코드: arch/arm64/include/asm/barrier.h:45-141

45						 ARM64_HAS_SB))
46 
47#define gsb_ack()	asm volatile(GSB_ACK_BARRIER_INSN : : : "memory")
48#define gsb_sys()	asm volatile(GSB_SYS_BARRIER_INSN : : : "memory")
49 
50#ifdef CONFIG_ARM64_PSEUDO_NMI
51#define pmr_sync()						\
52	do {							\
53		asm volatile(					\
54		ALTERNATIVE_CB("dsb sy",			\
55			       ARM64_HAS_GIC_PRIO_RELAXED_SYNC,	\
56			       alt_cb_patch_nops)		\
57		);						\
58	} while(0)
59#else
60#define pmr_sync()	do {} while (0)
61#endif
62 
63#define __mb()		dsb(sy)
64#define __rmb()		dsb(ld)
65#define __wmb()		dsb(st)
66 
67#define __dma_mb()	dmb(osh)
68#define __dma_rmb()	dmb(oshld)
69#define __dma_wmb()	dmb(oshst)
70 
71#define io_stop_wc()	dgh()
72 
73#define tsb_csync()								\
74	do {									\
75		/*								\
76		 * CPUs affected by Arm Erratum 2054223 or 2067961 needs	\
77		 * another TSB to ensure the trace is flushed. The barriers	\
78		 * don't have to be strictly back to back, as long as the	\
79		 * CPU is in trace prohibited state.				\
80		 */								\
81		if (cpus_have_final_cap(ARM64_WORKAROUND_TSB_FLUSH_FAILURE))	\
82			__tsb_csync();						\
83		__tsb_csync();							\
84	} while (0)
85 
86/*
87 * Generate a mask for array_index__nospec() that is ~0UL when 0 <= idx < sz
88 * and 0 otherwise.
89 */
90#define array_index_mask_nospec array_index_mask_nospec
91static inline unsigned long array_index_mask_nospec(unsigned long idx,
92						    unsigned long sz)
93{
94	unsigned long mask;
95 
96	asm volatile(
97	"	cmp	%1, %2\n"
98	"	sbc	%0, xzr, xzr\n"
99	: "=r" (mask)
100	: "r" (idx), "Ir" (sz)
101	: "cc");
102 
103	csdb();
104	return mask;
105}
106 
107/*
108 * Ensure that reads of the counter are treated the same as memory reads
109 * for the purposes of ordering by subsequent memory barriers.
110 *
111 * This insanity brought to you by speculative system register reads,
112 * out-of-order memory accesses, sequence locks and Thomas Gleixner.
113 *
114 * https://lore.kernel.org/r/alpine.DEB.2.21.1902081950260.1662@nanos.tec.linutronix.de/
115 */
116#define arch_counter_enforce_ordering(val) do {				\
117	u64 tmp, _val = (val);						\
118									\
119	asm volatile(							\
120	"	eor	%0, %1, %1\n"					\
121	"	add	%0, sp, %0\n"					\
122	"	ldr	xzr, [%0]"					\
123	: "=r" (tmp) : "r" (_val));					\
124} while (0)
125 
126#define __smp_mb()	dmb(ish)
127#define __smp_rmb()	dmb(ishld)
128#define __smp_wmb()	dmb(ishst)
129 
130#define __smp_store_release(p, v)					\
131do {									\
132	typeof(p) __p = (p);						\
133	union { __unqual_scalar_typeof(*p) __val; char __c[1]; } __u =	\
134		{ .__val = (__force __unqual_scalar_typeof(*p)) (v) };	\
135	compiletime_assert_atomic_type(*p);				\
136	kasan_check_write(__p, sizeof(*p));				\
137	switch (sizeof(*p)) {						\
138	case 1:								\
139		asm volatile ("stlrb %w1, %0"				\
140				: "=Q" (*__p)				\
141				: "rZ" (*(__u8 *)__u.__c)		\

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 97개 줄에 각각 설명을 붙였습니다.

L45 ARM64_HAS_SB))

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L46(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L47#define gsb_ack() asm volatile(GSB_ACK_BARRIER_INSN : : : "memory")

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L48#define gsb_sys() asm volatile(GSB_SYS_BARRIER_INSN : : : "memory")

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L49(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L50#ifdef CONFIG_ARM64_PSEUDO_NMI

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L51#define pmr_sync() \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L52 do { \

최소 한 번 실행되는 retry 또는 순회 구간이다. 탈출 조건이 다른 CPU나 hardware의 진행에 의존하는지 확인한다.

L53 asm volatile( \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L54 ALTERNATIVE_CB("dsb sy", \

memory, translation 또는 instruction-fetch 관찰 순서를 확정하는 architecture 명령이다. 앞에서 publish한 상태와 뒤에서 재사용하는 상태의 경계를 이 줄에 둔다.

L55 ARM64_HAS_GIC_PRIO_RELAXED_SYNC, \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L56 alt_cb_patch_nops) \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L57 ); \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L58 } while(0)

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L59#else

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L60#define pmr_sync() do {} while (0)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L61#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L62(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L63#define __mb() dsb(sy)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L64#define __rmb() dsb(ld)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L65#define __wmb() dsb(st)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L66(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L67#define __dma_mb() dmb(osh)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L68#define __dma_rmb() dmb(oshld)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L69#define __dma_wmb() dmb(oshst)

outer-shareable device observer에 대한 DMA store ordering을 나타낸다.

L70(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L71#define io_stop_wc() dgh()

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L72(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L73#define tsb_csync() \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L74 do { \

최소 한 번 실행되는 retry 또는 순회 구간이다. 탈출 조건이 다른 CPU나 hardware의 진행에 의존하는지 확인한다.

L75 /* \

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L76 * CPUs affected by Arm Erratum 2054223 or 2067961 needs \

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L77 * another TSB to ensure the trace is flushed. The barriers \

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L78 * don't have to be strictly back to back, as long as the \

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L79 * CPU is in trace prohibited state. \

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L80 */ \

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L81 if (cpus_have_final_cap(ARM64_WORKAROUND_TSB_FLUSH_FAILURE)) \

이 조건이 arm64 fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L82 __tsb_csync(); \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L83 __tsb_csync(); \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L84 } while (0)

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L85(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L86/*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L87 * Generate a mask for array_index__nospec() that is ~0UL when 0 <= idx < sz

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L88 * and 0 otherwise.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L89 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L90#define array_index_mask_nospec array_index_mask_nospec

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L91static inline unsigned long array_index_mask_nospec(unsigned long idx,

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L92 unsigned long sz)

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L93{

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L94 unsigned long mask;

선언 또는 macro 확장 일부다. type의 폭과 signedness, per-CPU/task/object 중 어느 수명을 따르는 값인지 확인한다.

L95(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L96 asm volatile(

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L97 " cmp %1, %2\n"

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L98 " sbc %0, xzr, xzr\n"

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L99 : "=r" (mask)

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L100 : "r" (idx), "Ir" (sz)

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L101 : "cc");

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L102(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L103 csdb();

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L104 return mask;

이 함수가 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 단계의 결과 또는 오류를 상위 계층에 전달한다. 반환 전에 lock, interrupt state, reference와 hardware active state가 정리됐는지 확인한다.

L105}

C block의 시작 또는 끝이다. lock, RCU, preemption과 interrupt-disabled 범위를 이 중괄호 바깥 호출까지 넘겨 추정하지 않는다.

L106(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L107/*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L108 * Ensure that reads of the counter are treated the same as memory reads

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L109 * for the purposes of ordering by subsequent memory barriers.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L110 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L111 * This insanity brought to you by speculative system register reads,

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L112 * out-of-order memory accesses, sequence locks and Thomas Gleixner.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L113 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L114 * https://lore.kernel.org/r/alpine.DEB.2.21.1902081950260.1662@nanos.tec.linutronix.de/

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L115 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L116#define arch_counter_enforce_ordering(val) do { \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L117 u64 tmp, _val = (val); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L118 \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L119 asm volatile( \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L120 " eor %0, %1, %1\n" \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L121 " add %0, sp, %0\n" \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L122 " ldr xzr, [%0]" \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L123 : "=r" (tmp) : "r" (_val)); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L124} while (0)

helper 또는 architecture operation을 실행한다. arm64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L125(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L126#define __smp_mb() dmb(ish)

inner-shareable CPU observer 사이의 read/write ordering을 full DMB로 강제한다.

L127#define __smp_rmb() dmb(ishld)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L128#define __smp_wmb() dmb(ishst)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L129(blank)

빈 줄은 arm64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L130#define __smp_store_release(p, v) \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L131do { \

최소 한 번 실행되는 retry 또는 순회 구간이다. 탈출 조건이 다른 CPU나 hardware의 진행에 의존하는지 확인한다.

L132 typeof(p) __p = (p); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L133 union { __unqual_scalar_typeof(*p) __val; char __c[1]; } __u = \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L134 { .__val = (__force __unqual_scalar_typeof(*p)) (v) }; \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L135 compiletime_assert_atomic_type(*p); \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L136 kasan_check_write(__p, sizeof(*p)); \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L137 switch (sizeof(*p)) { \

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L138 case 1: \

hardware reason, flag 또는 operation 종류를 개별 처리 경로로 분기한다. 각 case가 공통 cleanup과 completion 지점에 도달하는지 확인한다.

L139 asm volatile ("stlrb %w1, %0" \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L140 : "=Q" (*__p) \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L141 : "rZ" (*(__u8 *)__u.__c) \

이 줄이 arm64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

x86-64 · Linux 6.18.37

TSO 기본 ordering과 LFENCE/SFENCE/MFENCE·locked op

WB memory의 store-store/load-load는 강하지만 store buffer, non-temporal store, WC memory와 speculation 때문에 barrier가 완전히 사라지지는 않는다. SMP full barrier는 locked instruction으로 구현될 수 있다.

원본 코드: arch/x86/include/asm/barrier.h:9-84

9 * Force strict CPU ordering.
10 * And yes, this might be required on UP too when we're talking
11 * to devices.
12 */
13 
14#ifdef CONFIG_X86_32
15#define mb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "mfence", \
16				      X86_FEATURE_XMM2) ::: "memory", "cc")
17#define rmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \
18				       X86_FEATURE_XMM2) ::: "memory", "cc")
19#define wmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \
20				       X86_FEATURE_XMM2) ::: "memory", "cc")
21#else
22#define __mb()	asm volatile("mfence":::"memory")
23#define __rmb()	asm volatile("lfence":::"memory")
24#define __wmb()	asm volatile("sfence" ::: "memory")
25#endif
26 
27/**
28 * array_index_mask_nospec() - generate a mask that is ~0UL when the
29 * 	bounds check succeeds and 0 otherwise
30 * @index: array element index
31 * @size: number of elements in array
32 *
33 * Returns:
34 *     0 - (index < size)
35 */
36#define array_index_mask_nospec(idx,sz) ({	\
37	typeof((idx)+(sz)) __idx = (idx);	\
38	typeof(__idx) __sz = (sz);		\
39	unsigned long __mask;			\
40	asm volatile ("cmp %1,%2; sbb %0,%0"	\
41			:"=r" (__mask)		\
42			:ASM_INPUT_G (__sz),	\
43			 "r" (__idx)		\
44			:"cc");			\
45	__mask; })
46 
47/* Prevent speculative execution past this barrier. */
48#define barrier_nospec() alternative("", "lfence", X86_FEATURE_LFENCE_RDTSC)
49 
50#define __dma_rmb()	barrier()
51#define __dma_wmb()	barrier()
52 
53#define __smp_mb()	asm volatile("lock addl $0,-4(%%" _ASM_SP ")" ::: "memory", "cc")
54 
55#define __smp_rmb()	dma_rmb()
56#define __smp_wmb()	barrier()
57#define __smp_store_mb(var, value) do { (void)xchg(&var, value); } while (0)
58 
59#define __smp_store_release(p, v)					\
60do {									\
61	compiletime_assert_atomic_type(*p);				\
62	barrier();							\
63	WRITE_ONCE(*p, v);						\
64} while (0)
65 
66#define __smp_load_acquire(p)						\
67({									\
68	typeof(*p) ___p1 = READ_ONCE(*p);				\
69	compiletime_assert_atomic_type(*p);				\
70	barrier();							\
71	___p1;								\
72})
73 
74/* Atomic operations are already serializing on x86 */
75#define __smp_mb__before_atomic()	do { } while (0)
76#define __smp_mb__after_atomic()	do { } while (0)
77 
78/* Writing to CR3 provides a full memory barrier in switch_mm(). */
79#define smp_mb__after_switch_mm()	do { } while (0)
80 
81#include <asm-generic/barrier.h>
82 
83#endif /* _ASM_X86_BARRIER_H */
84 

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 76개 줄에 각각 설명을 붙였습니다.

L9 * Force strict CPU ordering.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L10 * And yes, this might be required on UP too when we're talking

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L11 * to devices.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L12 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L13(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L14#ifdef CONFIG_X86_32

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L15#define mb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "mfence", \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L16 X86_FEATURE_XMM2) ::: "memory", "cc")

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L17#define rmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L18 X86_FEATURE_XMM2) ::: "memory", "cc")

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L19#define wmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L20 X86_FEATURE_XMM2) ::: "memory", "cc")

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L21#else

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L22#define __mb() asm volatile("mfence":::"memory")

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L23#define __rmb() asm volatile("lfence":::"memory")

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L24#define __wmb() asm volatile("sfence" ::: "memory")

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L25#endif

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L26(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L27/**

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L28 * array_index_mask_nospec() - generate a mask that is ~0UL when the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L29 * bounds check succeeds and 0 otherwise

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L30 * @index: array element index

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L31 * @size: number of elements in array

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L32 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L33 * Returns:

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L34 * 0 - (index < size)

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L35 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L36#define array_index_mask_nospec(idx,sz) ({ \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L37 typeof((idx)+(sz)) __idx = (idx); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L38 typeof(__idx) __sz = (sz); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L39 unsigned long __mask; \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L40 asm volatile ("cmp %1,%2; sbb %0,%0" \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L41 :"=r" (__mask) \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L42 :ASM_INPUT_G (__sz), \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L43 "r" (__idx) \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L44 :"cc"); \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L45 __mask; })

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L46(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L47/* Prevent speculative execution past this barrier. */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L48#define barrier_nospec() alternative("", "lfence", X86_FEATURE_LFENCE_RDTSC)

일반 data ordering이 아니라 speculative execution을 제한하는 alternative다.

L49(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L50#define __dma_rmb() barrier()

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L51#define __dma_wmb() barrier()

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L52(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L53#define __smp_mb() asm volatile("lock addl $0,-4(%%" _ASM_SP ")" ::: "memory", "cc")

locked RMW가 full memory ordering point를 제공한다.

L54(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L55#define __smp_rmb() dma_rmb()

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L56#define __smp_wmb() barrier()

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L57#define __smp_store_mb(var, value) do { (void)xchg(&var, value); } while (0)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L58(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L59#define __smp_store_release(p, v) \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L60do { \

최소 한 번 실행되는 retry 또는 순회 구간이다. 탈출 조건이 다른 CPU나 hardware의 진행에 의존하는지 확인한다.

L61 compiletime_assert_atomic_type(*p); \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L62 barrier(); \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L63 WRITE_ONCE(*p, v); \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L64} while (0)

helper 또는 architecture operation을 실행한다. x86-64에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L65(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L66#define __smp_load_acquire(p) \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L67({ \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L68 typeof(*p) ___p1 = READ_ONCE(*p); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L69 compiletime_assert_atomic_type(*p); \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L70 barrier(); \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L71 ___p1; \

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L72})

이 줄이 x86-64의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L73(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L74/* Atomic operations are already serializing on x86 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L75#define __smp_mb__before_atomic() do { } while (0)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L76#define __smp_mb__after_atomic() do { } while (0)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L77(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L78/* Writing to CR3 provides a full memory barrier in switch_mm(). */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L79#define smp_mb__after_switch_mm() do { } while (0)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L80(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L81#include <asm-generic/barrier.h>

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L82(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L83#endif /* _ASM_X86_BARRIER_H */

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L84(blank)

빈 줄은 x86-64 Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

RISC-V · Linux 6.18.37

FENCE predecessor/successor set과 aq/rl bit

RVWMO는 fence rw,rw, r,r, w,w로 observer order를 지정한다. AMO의 aq/rl은 해당 atomic 전후 ordering을 포함하고 instruction fetch에는 별도 fence.i가 필요하다.

원본 코드: arch/riscv/include/asm/barrier.h:12-76

12 
13#ifndef __ASSEMBLER__
14#include <asm/cmpxchg.h>
15#include <asm/fence.h>
16 
17/* These barriers need to enforce ordering on both devices or memory. */
18#define __mb()		RISCV_FENCE(iorw, iorw)
19#define __rmb()		RISCV_FENCE(ir, ir)
20#define __wmb()		RISCV_FENCE(ow, ow)
21 
22/* These barriers do not need to enforce ordering on devices, just memory. */
23#define __smp_mb()	RISCV_FENCE(rw, rw)
24#define __smp_rmb()	RISCV_FENCE(r, r)
25#define __smp_wmb()	RISCV_FENCE(w, w)
26 
27/*
28 * This is a very specific barrier: it's currently only used in two places in
29 * the kernel, both in the scheduler.  See include/linux/spinlock.h for the two
30 * orderings it guarantees, but the "critical section is RCsc" guarantee
31 * mandates a barrier on RISC-V.  The sequence looks like:
32 *
33 *    lr.aq lock
34 *    sc    lock <= LOCKED
35 *    smp_mb__after_spinlock()
36 *    // critical section
37 *    lr    lock
38 *    sc.rl lock <= UNLOCKED
39 *
40 * The AQ/RL pair provides a RCpc critical section, but there's not really any
41 * way we can take advantage of that here because the ordering is only enforced
42 * on that one lock.  Thus, we're just doing a full fence.
43 *
44 * Since we allow writeX to be called from preemptive regions we need at least
45 * an "o" in the predecessor set to ensure device writes are visible before the
46 * task is marked as available for scheduling on a new hart.  While I don't see
47 * any concrete reason we need a full IO fence, it seems safer to just upgrade
48 * this in order to avoid any IO crossing a scheduling boundary.  In both
49 * instances the scheduler pairs this with an mb(), so nothing is necessary on
50 * the new hart.
51 */
52#define smp_mb__after_spinlock()	RISCV_FENCE(iorw, iorw)
53 
54#define __smp_store_release(p, v)					\
55do {									\
56	compiletime_assert_atomic_type(*p);				\
57	RISCV_FENCE(rw, w);						\
58	WRITE_ONCE(*p, v);						\
59} while (0)
60 
61#define __smp_load_acquire(p)						\
62({									\
63	typeof(*p) ___p1 = READ_ONCE(*p);				\
64	compiletime_assert_atomic_type(*p);				\
65	RISCV_FENCE(r, rw);						\
66	___p1;								\
67})
68 
69#ifdef CONFIG_RISCV_ISA_ZAWRS
70#define smp_cond_load_relaxed(ptr, cond_expr) ({			\
71	typeof(ptr) __PTR = (ptr);					\
72	__unqual_scalar_typeof(*ptr) VAL;				\
73	for (;;) {							\
74		VAL = READ_ONCE(*__PTR);				\
75		if (cond_expr)						\
76			break;						\

라인 바이 라인 주석

빈 줄과 전처리 경계도 생략하지 않았습니다. 원본의 65개 줄에 각각 설명을 붙였습니다.

L12(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L13#ifndef __ASSEMBLER__

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L14#include <asm/cmpxchg.h>

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L15#include <asm/fence.h>

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L16(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L17/* These barriers need to enforce ordering on both devices or memory. */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L18#define __mb() RISCV_FENCE(iorw, iorw)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L19#define __rmb() RISCV_FENCE(ir, ir)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L20#define __wmb() RISCV_FENCE(ow, ow)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L21(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L22/* These barriers do not need to enforce ordering on devices, just memory. */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L23#define __smp_mb() RISCV_FENCE(rw, rw)

memory read/write 사이의 full RVWMO ordering을 만든다.

L24#define __smp_rmb() RISCV_FENCE(r, r)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L25#define __smp_wmb() RISCV_FENCE(w, w)

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L26(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L27/*

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L28 * This is a very specific barrier: it's currently only used in two places in

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L29 * the kernel, both in the scheduler. See include/linux/spinlock.h for the two

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L30 * orderings it guarantees, but the "critical section is RCsc" guarantee

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L31 * mandates a barrier on RISC-V. The sequence looks like:

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L32 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L33 * lr.aq lock

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L34 * sc lock <= LOCKED

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L35 * smp_mb__after_spinlock()

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L36 * // critical section

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L37 * lr lock

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L38 * sc.rl lock <= UNLOCKED

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L39 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L40 * The AQ/RL pair provides a RCpc critical section, but there's not really any

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L41 * way we can take advantage of that here because the ordering is only enforced

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L42 * on that one lock. Thus, we're just doing a full fence.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L43 *

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L44 * Since we allow writeX to be called from preemptive regions we need at least

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L45 * an "o" in the predecessor set to ensure device writes are visible before the

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L46 * task is marked as available for scheduling on a new hart. While I don't see

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L47 * any concrete reason we need a full IO fence, it seems safer to just upgrade

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L48 * this in order to avoid any IO crossing a scheduling boundary. In both

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L49 * instances the scheduler pairs this with an mb(), so nothing is necessary on

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L50 * the new hart.

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L51 */

Linux 원본 주석이다. 바로 아래 코드의 호출 조건, hardware 제약 또는 예외 처리를 설명하므로 실행 줄과 함께 읽는다.

L52#define smp_mb__after_spinlock() RISCV_FENCE(iorw, iorw)

Linux lock ordering requirement를 맞추기 위해 I/O까지 포함한 강한 fence를 사용한다.

L53(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L54#define __smp_store_release(p, v) \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L55do { \

최소 한 번 실행되는 retry 또는 순회 구간이다. 탈출 조건이 다른 CPU나 hardware의 진행에 의존하는지 확인한다.

L56 compiletime_assert_atomic_type(*p); \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L57 RISCV_FENCE(rw, w); \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L58 WRITE_ONCE(*p, v); \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L59} while (0)

helper 또는 architecture operation을 실행한다. RISC-V에서 이 호출이 register write, cache/TLB operation, callback 또는 object lifetime 중 무엇을 바꾸는지 call site와 callee를 연결해 본다.

L60(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L61#define __smp_load_acquire(p) \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L62({ \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L63 typeof(*p) ___p1 = READ_ONCE(*p); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L64 compiletime_assert_atomic_type(*p); \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L65 RISCV_FENCE(r, rw); \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L66 ___p1; \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L67})

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L68(blank)

빈 줄은 RISC-V Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE 경로에서 한 상태 묶음이 끝나는 위치다. 위쪽에서 만든 값이 아래쪽에서 소비되는지 구간을 나눠 읽는다.

L69#ifdef CONFIG_RISCV_ISA_ZAWRS

Kconfig와 compiler feature에 따라 최종 object에 남는 경로가 달라지는 전처리 경계다. 대상 .config와 disassembly로 실제 선택을 확인한다.

L70#define smp_cond_load_relaxed(ptr, cond_expr) ({ \

compile-time 이름, constant 또는 architecture helper를 가져오는 줄이다. macro라면 최종 instruction과 memory-order 의미까지 펼쳐서 확인한다.

L71 typeof(ptr) __PTR = (ptr); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L72 __unqual_scalar_typeof(*ptr) VAL; \

이 줄이 RISC-V의 현재 상태에서 읽는 register와 memory, 그리고 다음 줄에 남기는 값을 적는다. Cache coherency와 memory barrier: DMB/DSB, x86 ordering, FENCE의 공통 kernel 계약과 architecture 전용 side effect를 분리해 해석한다.

L73 for (;;) { \

range, CPU mask, relocation 또는 descriptor를 반복 처리한다. 반복 상한과 중간 실패 때 이미 처리한 항목을 되돌리는 경로를 함께 본다.

L74 VAL = READ_ONCE(*__PTR); \

계산한 pointer, flag, register image 또는 generation을 다음 단계가 읽을 위치에 저장한다. 값의 단위, address space와 publication ordering을 확인한다.

L75 if (cond_expr) \

이 조건이 RISC-V fast path와 fallback/error path를 가른다. 조건에 쓰인 flag가 어느 CPU 또는 object의 상태인지, 동시에 바뀔 수 있는지 확인한다.

L76 break; \

정상 직선 경로를 벗어나 cleanup, retry 또는 다음 항목으로 이동한다. 이동 대상에서 해제하는 resource와 현재까지 획득한 ownership을 맞춘다.

05 · WORKED EXAMPLE

숫자로 검산하기

01

message-passing litmus를 architecture instruction으로 내린다

CPU0이 data=0x55를 쓰고 ready=1을 publish하며 CPU1이 ready를 본 뒤 data를 읽는 상황이다.

  1. CPU0data store 뒤 release store 또는 write barrier를 두고 ready를 쓴다.
  2. CPU1ready acquire load가 1이면 이후 data load가 0x55보다 먼저 관찰될 수 없어야 한다.
  3. ISAarm64는 STLR/LDAR 또는 DMB, x86은 TSO와 compiler constraint, RISC-V는 aq/rl 또는 FENCE로 계약을 만든다.
  4. DMAconsumer가 device라면 SMP barrier 대신 dma_wmb와 cache ownership API가 필요한지 확인한다.

결론'barrier를 넣었다'가 아니라 어떤 observer의 어떤 두 access 순서를 만들었는지 문장으로 증명한다.

06 · DEEP DIVE

경계별 상세 분석

01

공통 kernel core와 architecture hook의 경계

Linux memory model은 acquire/release와 full barrier API를 제공하지만 ISA의 기본 ordering, shareability domain, device memory와 instruction fetch coherence가 구현을 결정한다.

producer store, barrier, publication flag와 consumer acquire load를 한 litmus test로 읽는다. cache clean/invalidate는 ordering barrier와 같은 연산이 아니다.

02

arm64: DMB/DSB/ISB와 shareability·load/store option

weakly ordered ISA라 dmb ish, ishld, ishst가 SMP order를 표현한다. device/DMA에는 outer-shareable domain이 필요하고 code patch 뒤에는 D-cache clean, I-cache invalidate와 ISB가 이어진다.

release/acquire instruction과 barrier scope가 observer domain을 포함해야 한다. 디버깅할 때는 barrier 종류, domain, cache line, memory attribute와 observer CPU/device를 기록한다.

03

x86-64: TSO 기본 ordering과 LFENCE/SFENCE/MFENCE·locked op

WB memory의 store-store/load-load는 강하지만 store buffer, non-temporal store, WC memory와 speculation 때문에 barrier가 완전히 사라지지는 않는다. SMP full barrier는 locked instruction으로 구현될 수 있다.

compiler reorder와 CPU reorder를 구분하고 MMIO/WC에는 별도 API를 사용한다. 디버깅할 때는 memory type, locked op, fence, store buffer와 device posted-write completion을 확인한다.

04

RISC-V: FENCE predecessor/successor set과 aq/rl bit

RVWMO는 fence rw,rw, r,r, w,w로 observer order를 지정한다. AMO의 aq/rl은 해당 atomic 전후 ordering을 포함하고 instruction fetch에는 별도 fence.i가 필요하다.

remote hart의 instruction cache는 local fence.i만으로 갱신되지 않을 수 있어 IPI/SBI 기반 sync가 필요하다. 디버깅할 때는 FENCE pred/succ, aq/rl, I/O vs memory, hart mask와 icache sync를 기록한다.

05

객체 수명과 소유권을 먼저 고정한다

lock-free object는 publication 전 완성되어야 하고 consumer가 reference를 놓기 전 free되면 안 된다. DMA buffer는 CPU/device ownership 전환과 cache maintenance가 맞아야 한다.

주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.

06

latency upper bound는 hardware instruction 하나가 아니다

barrier 비용은 outstanding memory transaction, domain과 microarchitecture에 따라 달라진다. DSB처럼 completion까지 기다리는 명령과 ordering만 강제하는 DMB를 분리한다.

평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.

07 · FAILURE

실패를 어떤 증거로 나눌 것인가

분류관찰되는 결과첫 확인값
arm64dmb ish만으로 device MMIO completion이나 instruction-cache 갱신까지 됐다고 오해한다.barrier 종류, domain, cache line, memory attribute와 observer CPU/device를 기록한다.
x86-64x86은 strong ordering이라는 이유로 DMA/MMIO와 speculation barrier를 모두 빈 연산으로 처리한다.memory type, locked op, fence, store buffer와 device posted-write completion을 확인한다.
RISC-Vdata fence 뒤 새 code가 실행될 것으로 가정하거나 I/O bit가 빠진 fence로 MMIO를 publish한다.FENCE pred/succ, aq/rl, I/O vs memory, hart mask와 icache sync를 기록한다.

08 · LAB

재현과 계측 절차

  1. herd7 litmus와 실제 stress test를 함께 사용해 허용 outcome을 비교한다.
  2. WB/WC/MMIO memory type을 바꿔 x86에서도 barrier 결과가 달라지는 지점을 확인한다.
  3. 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
  4. 소스만 읽고 끝내지 않고 최종 vmlinuxobjdump -dr, readelf -SW 결과로 선택된 alternative와 section 배치를 확인한다.

09 · REFERENCES

원문 좌표

Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.