Commit 578152da authored by SeongJae Park's avatar SeongJae Park Committed by Jonathan Corbet
Browse files

kokr/memory-barriers/txt: Replace uses of "transitive"

This commit applies two upstream change, commit f1ab25a3
("memory-barriers: Replace uses of "transitive"") and commit 0902b1f4


("memory-barriers: Rework multicopy-atomicity section") to the Korean
translation.  Those two changes are applied with this signle commit
because the second change is improvement of the first one.
Signed-off-by: default avatarSeongJae Park <sj38.park@gmail.com>
Signed-off-by: default avatarJonathan Corbet <corbet@lwn.net>
parent 8ee25f6f
......@@ -82,7 +82,7 @@ Documentation/memory-barriers.txt
- SMP 배리어 짝맞추기.
- 메모리 배리어 시퀀스의 예.
- 읽기 메모리 배리어 vs 로드 예측.
- 이행성
- Multicopy 원자성.
(*) 명시적 커널 배리어.
......@@ -656,6 +656,11 @@ Documentation/RCU/rcu_dereference.txt 파일을 주의 깊게 읽어 주시기
해줍니다.
데이터 의존성에 의해 제공되는 이 순서규칙은 이를 포함하고 있는 CPU 에
지역적임을 알아두시기 바랍니다. 더 많은 정보를 위해선 "Multicopy 원자성"
섹션을 참고하세요.
데이터 의존성 배리어는 매우 중요한데, 예를 들어 RCU 시스템에서 그렇습니다.
include/linux/rcupdate.h 의 rcu_assign_pointer() 와 rcu_dereference() 를
참고하세요. 여기서 데이터 의존성 배리어는 RCU 로 관리되는 포인터의 타겟을 현재
......@@ -864,38 +869,10 @@ CPU 는 b 로부터의 로드 오퍼레이션이 a 로부터의 로드 오퍼레
주어진 if 문의 then 절과 else 절에게만 (그리고 이 두 절 내에서 호출되는
함수들에게까지) 적용되지, 이 if 문을 뒤따르는 코드에는 적용되지 않습니다.
마지막으로, 컨트롤 의존성은 이행성 (transitivity) 을 제공하지 -않습니다-. 이건
'x' 와 'y' 가 둘 다 0 이라는 초기값을 가졌다는 가정 하의 두개의 예제로
보이겠습니다:
CPU 0 CPU 1
======================= =======================
r1 = READ_ONCE(x); r2 = READ_ONCE(y);
if (r1 > 0) if (r2 > 0)
WRITE_ONCE(y, 1); WRITE_ONCE(x, 1);
assert(!(r1 == 1 && r2 == 1));
이 두 CPU 예제에서 assert() 의 조건은 항상 참일 것입니다. 그리고, 만약 컨트롤
의존성이 이행성을 (실제로는 그러지 않지만) 보장한다면, 다음의 CPU 가 추가되어도
아래의 assert() 조건은 참이 될것입니다:
CPU 2
=====================
WRITE_ONCE(x, 2);
컨트롤 의존성에 의해 제공되는 이 순서규칙은 이를 포함하고 있는 CPU 에
지역적입니다. 더 많은 정보를 위해선 "Multicopy 원자성" 섹션을 참고하세요.
assert(!(r1 == 2 && r2 == 1 && x == 2)); /* FAILS!!! */
하지만 컨트롤 의존성은 이행성을 제공하지 -않기- 때문에, 세개의 CPU 예제가 실행
완료된 후에 위의 assert() 의 조건은 거짓으로 평가될 수 있습니다. 세개의 CPU
예제가 순서를 지키길 원한다면, CPU 0 와 CPU 1 코드의 로드와 스토어 사이, "if"
문 바로 다음에 smp_mb()를 넣어야 합니다. 더 나아가서, 최초의 두 CPU 예제는
매우 위험하므로 사용되지 않아야 합니다.
이 두개의 예제는 다음 논문:
http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf 와
이 사이트: https://www.cl.cam.ac.uk/~pes20/ppcmem/index.html 에 나온 LB 와 WWC
리트머스 테스트입니다.
요약하자면:
......@@ -930,8 +907,8 @@ http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf 와
(*) 컨트롤 의존성은 보통 다른 타입의 배리어들과 짝을 맞춰 사용됩니다.
(*) 컨트롤 의존성은 이행성을 제공하지 -않습니다-. 이행성이 필요하다면,
smp_mb() 를 사용하세요.
(*) 컨트롤 의존성은 multicopy 원자성을 제공하지 -않습니다-. 모든 CPU 들이
특정 스토어를 동시에 보길 원한다면, smp_mb() 를 사용하세요.
(*) 컴파일러는 컨트롤 의존성을 이해하고 있지 않습니다. 따라서 컴파일러가
여러분의 코드를 망가뜨리지 않도록 하는건 여러분이 해야 하는 일입니다.
......@@ -943,13 +920,14 @@ SMP 배리어 짝맞추기
CPU 간 상호작용을 다룰 때에 일부 타입의 메모리 배리어는 항상 짝을 맞춰
사용되어야 합니다. 적절하게 짝을 맞추지 않은 코드는 사실상 에러에 가깝습니다.
범용 배리어들은 범용 배리어끼리도 짝을 맞추지만 이행성이 없는 대부분의 다른
타입의 배리어들과도 짝을 맞춥니다. ACQUIRE 배리어는 RELEASE 배리어와 짝을
맞춥니다만, 둘 다 범용 배리어를 포함해 다른 배리어들과도 짝을 맞출 수 있습니다.
쓰기 배리어는 데이터 의존성 배리어나 컨트롤 의존성, ACQUIRE 배리어, RELEASE
배리어, 읽기 배리어, 또는 범용 배리어와 짝을 맞춥니다. 비슷하게 읽기 배리어나
컨트롤 의존성, 또는 데이터 의존성 배리어는 쓰기 배리어나 ACQUIRE 배리어,
RELEASE 배리어, 또는 범용 배리어와 짝을 맞추는데, 다음과 같습니다:
범용 배리어들은 범용 배리어끼리도 짝을 맞추지만 multicopy 원자성이 없는
대부분의 다른 타입의 배리어들과도 짝을 맞춥니다. ACQUIRE 배리어는 RELEASE
배리어와 짝을 맞춥니다만, 둘 다 범용 배리어를 포함해 다른 배리어들과도 짝을
맞출 수 있습니다. 쓰기 배리어는 데이터 의존성 배리어나 컨트롤 의존성, ACQUIRE
배리어, RELEASE 배리어, 읽기 배리어, 또는 범용 배리어와 짝을 맞춥니다.
비슷하게 읽기 배리어나 컨트롤 의존성, 또는 데이터 의존성 배리어는 쓰기 배리어나
ACQUIRE 배리어, RELEASE 배리어, 또는 범용 배리어와 짝을 맞추는데, 다음과
같습니다:
CPU 1 CPU 2
=============== ===============
......@@ -1361,57 +1339,74 @@ A 의 로드 두개가 모두 B 의 로드 뒤에 있지만, 서로 다른 값
: : +-------+
이행
------
MULTICOPY 원자
----------------
이행성(transitivity)은 실제의 컴퓨터 시스템에서 항상 제공되지는 않는, 순서
맞추기에 대한 상당히 직관적인 개념입니다. 다음의 예가 이행성을 보여줍니다:
Multicopy 원자성은 실제의 컴퓨터 시스템에서 항상 제공되지는 않는, 순서 맞추기에
대한 상당히 직관적인 개념으로, 특정 스토어가 모든 CPU 들에게 동시에 보여지게
됨을, 달리 말하자면 모든 CPU 들이 모든 스토어들이 보여지는 순서를 동의하게 되는
것입니다. 하지만, 완전한 multicopy 원자성의 사용은 가치있는 하드웨어
최적화들을 무능하게 만들어버릴 수 있어서, 보다 완화된 형태의 ``다른 multicopy
원자성'' 라는 이름의, 특정 스토어가 모든 -다른- CPU 들에게는 동시에 보여지게
하는 보장을 대신 제공합니다. 이 문서의 뒷부분들은 이 완화된 형태에 대해 논하게
됩니다만, 단순히 ``multicopy 원자성'' 이라고 부르겠습니다.
다음의 예가 multicopy 원자성을 보입니다:
CPU 1 CPU 2 CPU 3
======================= ======================= =======================
{ X = 0, Y = 0 }
STORE X=1 LOAD X STORE Y=1
<범용 배리어> <범용 배리어>
LOAD Y LOAD X
CPU 2 의 X 로드가 1을 리턴했고 Y 로드가 0을 리턴했다고 해봅시다. 이는 CPU 2 의
X 로드가 CPU 1 의 X 스토어 뒤에 이루어졌고 CPU 2 의 Y 로드는 CPU 3 의 Y 스토어
전에 이루어졌음을 의미합니다. 그럼 "CPU 3 의 X 로드는 0을 리턴할 수 있나요?"
CPU 2 의 X 로드는 CPU 1 의 스토어 후에 이루어졌으니, CPU 3 의 X 로드는 1을
리턴하는게 자연스럽습니다. 이런 생각이 이행성의 한 예입니다: CPU A 에서 실행된
로드가 CPU B 에서의 같은 변수에 대한 로드를 뒤따른다면, CPU A 의 로드는 CPU B
의 로드가 내놓은 값과 같거나 그 후의 값을 내놓아야 합니다.
리눅스 커널에서 범용 배리어의 사용은 이행성을 보장합니다. 따라서, 앞의 예에서
CPU 2 의 X 로드가 1을, Y 로드는 0을 리턴했다면, CPU 3 의 X 로드는 반드시 1을
리턴합니다.
하지만, 읽기나 쓰기 배리어에 대해서는 이행성이 보장되지 -않습니다-. 예를 들어,
앞의 예에서 CPU 2 의 범용 배리어가 아래처럼 읽기 배리어로 바뀐 경우를 생각해
봅시다:
STORE X=1 r1=LOAD X (reads 1) LOAD Y (reads 1)
<범용 배리어> <읽기 배리어>
STORE Y=r1 LOAD X
CPU 2 의 Y 로의 스토어에 사용되는 X 로드의 결과가 1 이었고 CPU 3 의 Y 로드가
1을 리턴했다고 해봅시다. 이는 CPU 1 의 X 로의 스토어가 CPU 2 의 X 로부터의
로드를 앞서고 CPU 2 의 Y 로의 스토어가 CPU 3 의 Y 로부터의 로드를 앞섬을
의미합니다. 또한, 여기서의 메모리 배리어들은 CPU 2 가 자신의 로드를 자신의
스토어 전에 수행하고, CPU 3 가 Y 로부터의 로드를 X 로부터의 로드 전에 수행함을
보장합니다. 그럼 "CPU 3 의 X 로부터의 로드는 0 을 리턴할 수 있을까요?"
CPU 3 의 X 로드가 CPU 2 의 로드보다 뒤에 이루어졌으므로, CPU 3 의 X 로부터의
로드는 1 을 리턴한다고 예상하는게 당연합니다. 이런 예상은 multicopy
원자성으로부터 나옵니다: CPU B 에서 수행된 로드가 CPU A 의 같은 변수로부터의
로드를 뒤따른다면 (그리고 CPU A 가 자신이 읽은 값으로 먼저 해당 변수에 스토어
하지 않았다면) multicopy 원자성을 제공하는 시스템에서는, CPU B 의 로드가 CPU A
의 로드와 같은 값 또는 그 나중 값을 리턴해야만 합니다. 하지만, 리눅스 커널은
시스템들이 multicopy 원자성을 제공할 것을 요구하지 않습니다.
앞의 범용 메모리 배리어의 사용은 모든 multicopy 원자성의 부족을 보상해줍니다.
앞의 예에서, CPU 2 의 X 로부터의 로드가 1 을 리턴했고 CPU 3 의 Y 로부터의
로드가 1 을 리턴했다면, CPU 3 의 X 로부터의 로드는 1을 리턴해야만 합니다.
하지만, 의존성, 읽기 배리어, 쓰기 배리어는 항상 non-multicopy 원자성을 보상해
주지는 않습니다. 예를 들어, CPU 2 의 범용 배리어가 앞의 예에서 사라져서
아래처럼 데이터 의존성만 남게 되었다고 해봅시다:
CPU 1 CPU 2 CPU 3
======================= ======================= =======================
{ X = 0, Y = 0 }
STORE X=1 LOAD X STORE Y=1
<읽기 배리어> <범용 배리어>
LOAD Y LOAD X
이 코드는 이행성을 갖지 않습니다: 이 예에서는, CPU 2 의 X 로드가 1을
리턴하고, Y 로드는 0을 리턴하지만 CPU 3 의 X 로드가 0을 리턴하는 것도 완전히
합법적입니다.
CPU 2 의 읽기 배리어가 자신의 읽기는 순서를 맞춰줘도, CPU 1 의 스토어와의
순서를 맞춰준다고는 보장할 수 없다는게 핵심입니다. 따라서, CPU 1 과 CPU 2 가
버퍼나 캐시를 공유하는 시스템에서 이 예제 코드가 실행된다면, CPU 2 는 CPU 1 이
쓴 값에 좀 빨리 접근할 수 있을 것입니다. 따라서 CPU 1 과 CPU 2 의 접근으로
조합된 순서를 모든 CPU 가 동의할 수 있도록 하기 위해 범용 배리어가 필요합니다.
범용 배리어는 "글로벌 이행성"을 제공해서, 모든 CPU 들이 오퍼레이션들의 순서에
동의하게 할 것입니다. 반면, release-acquire 조합은 "로컬 이행성" 만을
제공해서, 해당 조합이 사용된 CPU 들만이 해당 액세스들의 조합된 순서에 동의함이
보장됩니다. 예를 들어, 존경스런 Herman Hollerith 의 C 코드로 보면:
STORE X=1 r1=LOAD X (reads 1) LOAD Y (reads 1)
<데이터 의존성> <읽기 배리어>
STORE Y=r1 LOAD X (reads 0)
이 변화는 non-multicopy 원자성이 만연하게 합니다: 이 예에서, CPU 2 의 X
로부터의 로드가 1을 리턴하고, CPU 3 의 Y 로부터의 로드가 1 을 리턴하는데, CPU 3
의 X 로부터의 로드가 0 을 리턴하는게 완전히 합법적입니다.
핵심은, CPU 2 의 데이터 의존성이 자신의 로드와 스토어를 순서짓지만, CPU 1 의
스토어에 대한 순서는 보장하지 않는다는 것입니다. 따라서, 이 예제가 CPU 1 과
CPU 2 가 스토어 버퍼나 한 수준의 캐시를 공유하는, multicopy 원자성을 제공하지
않는 시스템에서 수행된다면 CPU 2 는 CPU 1 의 쓰기에 이른 접근을 할 수도
있습니다. 따라서, 모든 CPU 들이 여러 접근들의 조합된 순서에 대해서 동의하게
하기 위해서는 범용 배리어가 필요합니다.
범용 배리어는 non-multicopy 원자성만 보상할 수 있는게 아니라, -모든- CPU 들이
-모든- 오퍼레이션들의 순서를 동일하게 인식하게 하는 추가적인 순서 보장을
만들어냅니다. 반대로, release-acquire 짝의 연결은 이런 추가적인 순서는
제공하지 않는데, 해당 연결에 들어있는 CPU 들만이 메모리 접근의 조합된 순서에
대해 동의할 것으로 보장됨을 의미합니다. 예를 들어, 존경스런 Herman Hollerith
의 코드를 C 코드로 변환하면:
int u, v, x, y, z;
......@@ -1444,8 +1439,7 @@ CPU 2 의 읽기 배리어가 자신의 읽기는 순서를 맞춰줘도, CPU 1
}
cpu0(), cpu1(), 그리고 cpu2() 는 smp_store_release()/smp_load_acquire() 쌍의
연결을 통한 로컬 이행성에 동참하고 있으므로, 다음과 같은 결과는 나오지 않을
겁니다:
연결에 참여되어 있으므로, 다음과 같은 결과는 나오지 않을 겁니다:
r0 == 1 && r1 == 1 && r2 == 1
......@@ -1454,8 +1448,9 @@ cpu0() 의 쓰기를 봐야만 하므로, 다음과 같은 결과도 없을 겁
r1 == 1 && r5 == 0
하지만, release-acquire 타동성은 동참한 CPU 들에만 적용되므로 cpu3() 에는
적용되지 않습니다. 따라서, 다음과 같은 결과가 가능합니다:
하지만, release-acquire 에 의해 제공되는 순서는 해당 연결에 동참한 CPU 들에만
적용되므로 cpu3() 에, 적어도 스토어들 외에는 적용되지 않습니다. 따라서, 다음과
같은 결과가 가능합니다:
r0 == 0 && r1 == 1 && r2 == 1 && r3 == 0 && r4 == 0
......@@ -1482,8 +1477,8 @@ u 로의 스토어를 cpu1() 의 v 로부터의 로드 뒤에 일어난 것으
이런 결과는 어떤 것도 재배치 되지 않는, 순차적 일관성을 가진 가상의
시스템에서도 일어날 수 있음을 기억해 두시기 바랍니다.
다시 말하지만, 당신의 코드가 글로벌 이행성을 필요로 한다면, 범용 배리어를
사용하십시오.
다시 말하지만, 당신의 코드가 모든 오퍼레이션들의 완전한 순서를 필요로 한다면,
범용 배리어를 사용하십시오.
==================
......@@ -3058,6 +3053,9 @@ AMD64 Architecture Programmer's Manual Volume 2: System Programming
Chapter 7.1: Memory-Access Ordering
Chapter 7.4: Buffering and Combining Memory Writes
ARM Architecture Reference Manual (ARMv8, for ARMv8-A architecture profile)
Chapter B2: The AArch64 Application Level Memory Model
IA-32 Intel Architecture Software Developer's Manual, Volume 3:
System Programming Guide
Chapter 7.1: Locked Atomic Operations
......@@ -3069,6 +3067,8 @@ The SPARC Architecture Manual, Version 9
Appendix D: Formal Specification of the Memory Models
Appendix J: Programming with the Memory Models
Storage in the PowerPC (Stone and Fitzgerald)
UltraSPARC Programmer Reference Manual
Chapter 5: Memory Accesses and Cacheability
Chapter 15: Sparc-V9 Memory Models
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment