.. | .. |
---|
24 | 24 | ========================= |
---|
25 | 25 | |
---|
26 | 26 | 저자: David Howells <dhowells@redhat.com> |
---|
27 | | - Paul E. McKenney <paulmck@linux.vnet.ibm.com> |
---|
| 27 | + Paul E. McKenney <paulmck@linux.ibm.com> |
---|
28 | 28 | Will Deacon <will.deacon@arm.com> |
---|
29 | 29 | Peter Zijlstra <peterz@infradead.org> |
---|
30 | 30 | |
---|
.. | .. |
---|
91 | 91 | |
---|
92 | 92 | - 컴파일러 배리어. |
---|
93 | 93 | - CPU 메모리 배리어. |
---|
94 | | - - MMIO 쓰기 배리어. |
---|
95 | 94 | |
---|
96 | 95 | (*) 암묵적 커널 메모리 배리어. |
---|
97 | 96 | |
---|
.. | .. |
---|
103 | 102 | (*) CPU 간 ACQUIRING 배리어의 효과. |
---|
104 | 103 | |
---|
105 | 104 | - Acquire vs 메모리 액세스. |
---|
106 | | - - Acquire vs I/O 액세스. |
---|
107 | 105 | |
---|
108 | 106 | (*) 메모리 배리어가 필요한 곳 |
---|
109 | 107 | |
---|
.. | .. |
---|
493 | 491 | 이 타입의 오퍼레이션은 단방향의 투과성 배리어처럼 동작합니다. ACQUIRE |
---|
494 | 492 | 오퍼레이션 뒤의 모든 메모리 오퍼레이션들이 ACQUIRE 오퍼레이션 후에 |
---|
495 | 493 | 일어난 것으로 시스템의 나머지 컴포넌트들에 보이게 될 것이 보장됩니다. |
---|
496 | | - LOCK 오퍼레이션과 smp_load_acquire(), smp_cond_acquire() 오퍼레이션도 |
---|
497 | | - ACQUIRE 오퍼레이션에 포함됩니다. smp_cond_acquire() 오퍼레이션은 컨트롤 |
---|
498 | | - 의존성과 smp_rmb() 를 사용해서 ACQUIRE 의 의미적 요구사항(semantic)을 |
---|
499 | | - 충족시킵니다. |
---|
| 494 | + LOCK 오퍼레이션과 smp_load_acquire(), smp_cond_load_acquire() 오퍼레이션도 |
---|
| 495 | + ACQUIRE 오퍼레이션에 포함됩니다. |
---|
500 | 496 | |
---|
501 | 497 | ACQUIRE 오퍼레이션 앞의 메모리 오퍼레이션들은 ACQUIRE 오퍼레이션 완료 후에 |
---|
502 | 498 | 수행된 것처럼 보일 수 있습니다. |
---|
.. | .. |
---|
517 | 513 | 완료되기 전에 행해진 것처럼 보일 수 있습니다. |
---|
518 | 514 | |
---|
519 | 515 | ACQUIRE 와 RELEASE 오퍼레이션의 사용은 일반적으로 다른 메모리 배리어의 |
---|
520 | | - 필요성을 없앱니다 (하지만 "MMIO 쓰기 배리어" 서브섹션에서 설명되는 예외를 |
---|
521 | | - 알아두세요). 또한, RELEASE+ACQUIRE 조합은 범용 메모리 배리어처럼 동작할 |
---|
522 | | - 것을 보장하지 -않습니다-. 하지만, 어떤 변수에 대한 RELEASE 오퍼레이션을 |
---|
523 | | - 앞서는 메모리 액세스들의 수행 결과는 이 RELEASE 오퍼레이션을 뒤이어 같은 |
---|
524 | | - 변수에 대해 수행된 ACQUIRE 오퍼레이션을 뒤따르는 메모리 액세스에는 보여질 |
---|
525 | | - 것이 보장됩니다. 다르게 말하자면, 주어진 변수의 크리티컬 섹션에서는, 해당 |
---|
526 | | - 변수에 대한 앞의 크리티컬 섹션에서의 모든 액세스들이 완료되었을 것을 |
---|
527 | | - 보장합니다. |
---|
| 516 | + 필요성을 없앱니다. 또한, RELEASE+ACQUIRE 조합은 범용 메모리 배리어처럼 |
---|
| 517 | + 동작할 것을 보장하지 -않습니다-. 하지만, 어떤 변수에 대한 RELEASE |
---|
| 518 | + 오퍼레이션을 앞서는 메모리 액세스들의 수행 결과는 이 RELEASE 오퍼레이션을 |
---|
| 519 | + 뒤이어 같은 변수에 대해 수행된 ACQUIRE 오퍼레이션을 뒤따르는 메모리 |
---|
| 520 | + 액세스에는 보여질 것이 보장됩니다. 다르게 말하자면, 주어진 변수의 |
---|
| 521 | + 크리티컬 섹션에서는, 해당 변수에 대한 앞의 크리티컬 섹션에서의 모든 |
---|
| 522 | + 액세스들이 완료되었을 것을 보장합니다. |
---|
528 | 523 | |
---|
529 | 524 | 즉, ACQUIRE 는 최소한의 "취득" 동작처럼, 그리고 RELEASE 는 최소한의 "공개" |
---|
530 | 525 | 처럼 동작한다는 의미입니다. |
---|
.. | .. |
---|
571 | 566 | |
---|
572 | 567 | [*] 버스 마스터링 DMA 와 일관성에 대해서는 다음을 참고하시기 바랍니다: |
---|
573 | 568 | |
---|
574 | | - Documentation/PCI/pci.txt |
---|
575 | | - Documentation/DMA-API-HOWTO.txt |
---|
576 | | - Documentation/DMA-API.txt |
---|
| 569 | + Documentation/driver-api/pci/pci.rst |
---|
| 570 | + Documentation/core-api/dma-api-howto.rst |
---|
| 571 | + Documentation/core-api/dma-api.rst |
---|
577 | 572 | |
---|
578 | 573 | |
---|
579 | 574 | 데이터 의존성 배리어 (역사적) |
---|
580 | 575 | ----------------------------- |
---|
581 | 576 | |
---|
582 | | -리눅스 커널 v4.15 기준으로, smp_read_barrier_depends() 가 READ_ONCE() 에 |
---|
| 577 | +리눅스 커널 v4.15 기준으로, smp_mb() 가 DEC Alpha 용 READ_ONCE() 코드에 |
---|
583 | 578 | 추가되었는데, 이는 이 섹션에 주의를 기울여야 하는 사람들은 DEC Alpha 아키텍쳐 |
---|
584 | 579 | 전용 코드를 만드는 사람들과 READ_ONCE() 자체를 만드는 사람들 뿐임을 의미합니다. |
---|
585 | 580 | 그런 분들을 위해, 그리고 역사에 관심 있는 분들을 위해, 여기 데이터 의존성 |
---|
.. | .. |
---|
643 | 638 | 리눅스 커널이 지원하는 CPU 들은 (1) 쓰기가 정말로 일어날지, (2) 쓰기가 어디에 |
---|
644 | 639 | 이루어질지, 그리고 (3) 쓰여질 값을 확실히 알기 전까지는 쓰기를 수행하지 않기 |
---|
645 | 640 | 때문입니다. 하지만 "컨트롤 의존성" 섹션과 |
---|
646 | | -Documentation/RCU/rcu_dereference.txt 파일을 주의 깊게 읽어 주시기 바랍니다: |
---|
| 641 | +Documentation/RCU/rcu_dereference.rst 파일을 주의 깊게 읽어 주시기 바랍니다: |
---|
647 | 642 | 컴파일러는 매우 창의적인 많은 방법으로 종속성을 깰 수 있습니다. |
---|
648 | 643 | |
---|
649 | 644 | CPU 1 CPU 2 |
---|
.. | .. |
---|
1503 | 1498 | |
---|
1504 | 1499 | (*) CPU 메모리 배리어. |
---|
1505 | 1500 | |
---|
1506 | | - (*) MMIO 쓰기 배리어. |
---|
1507 | | - |
---|
1508 | 1501 | |
---|
1509 | 1502 | 컴파일러 배리어 |
---|
1510 | 1503 | --------------- |
---|
.. | .. |
---|
1844 | 1837 | (*) smp_mb__before_atomic(); |
---|
1845 | 1838 | (*) smp_mb__after_atomic(); |
---|
1846 | 1839 | |
---|
1847 | | - 이것들은 값을 리턴하지 않는 (더하기, 빼기, 증가, 감소와 같은) 어토믹 |
---|
1848 | | - 함수들을 위한, 특히 그것들이 레퍼런스 카운팅에 사용될 때를 위한 |
---|
1849 | | - 함수들입니다. 이 함수들은 메모리 배리어를 내포하고 있지는 않습니다. |
---|
| 1840 | + 이것들은 메모리 배리어를 내포하지 않는 어토믹 RMW 함수를 사용하지만 코드에 |
---|
| 1841 | + 메모리 배리어가 필요한 경우를 위한 것들입니다. 메모리 배리어를 내포하지 |
---|
| 1842 | + 않는 어토믹 RMW 함수들의 예로는 더하기, 빼기, (실패한) 조건적 |
---|
| 1843 | + 오퍼레이션들, _relaxed 함수들이 있으며, atomic_read 나 atomic_set 은 이에 |
---|
| 1844 | + 해당되지 않습니다. 메모리 배리어가 필요해지는 흔한 예로는 어토믹 |
---|
| 1845 | + 오퍼레이션을 사용해 레퍼런스 카운트를 수정하는 경우를 들 수 있습니다. |
---|
1850 | 1846 | |
---|
1851 | | - 이것들은 값을 리턴하지 않으며 어토믹한 (set_bit 과 clear_bit 같은) 비트 |
---|
1852 | | - 연산에도 사용될 수 있습니다. |
---|
| 1847 | + 이것들은 또한 (set_bit 과 clear_bit 같은) 메모리 배리어를 내포하지 않는 |
---|
| 1848 | + 어토믹 RMW bitop 함수들을 위해서도 사용될 수 있습니다. |
---|
1853 | 1849 | |
---|
1854 | 1850 | 한 예로, 객체 하나를 무효한 것으로 표시하고 그 객체의 레퍼런스 카운트를 |
---|
1855 | 1851 | 감소시키는 다음 코드를 보세요: |
---|
.. | .. |
---|
1906 | 1902 | |
---|
1907 | 1903 | writel_relaxed() 와 같은 완화된 I/O 접근자들에 대한 자세한 내용을 위해서는 |
---|
1908 | 1904 | "커널 I/O 배리어의 효과" 섹션을, consistent memory 에 대한 자세한 내용을 |
---|
1909 | | - 위해선 Documentation/DMA-API.txt 문서를 참고하세요. |
---|
| 1905 | + 위해선 Documentation/core-api/dma-api.rst 문서를 참고하세요. |
---|
1910 | 1906 | |
---|
| 1907 | + (*) pmem_wmb(); |
---|
1911 | 1908 | |
---|
1912 | | -MMIO 쓰기 배리어 |
---|
1913 | | ----------------- |
---|
| 1909 | + 이것은 persistent memory 를 위한 것으로, persistent 저장소에 가해진 변경 |
---|
| 1910 | + 사항이 플랫폼 연속성 도메인에 도달했을 것을 보장하기 위한 것입니다. |
---|
1914 | 1911 | |
---|
1915 | | -리눅스 커널은 또한 memory-mapped I/O 쓰기를 위한 특별한 배리어도 가지고 |
---|
1916 | | -있습니다: |
---|
| 1912 | + 예를 들어, 임시적이지 않은 pmem 영역으로의 쓰기 후, 우리는 쓰기가 플랫폼 |
---|
| 1913 | + 연속성 도메인에 도달했을 것을 보장하기 위해 pmem_wmb() 를 사용합니다. |
---|
| 1914 | + 이는 쓰기가 뒤따르는 instruction 들이 유발하는 어떠한 데이터 액세스나 |
---|
| 1915 | + 데이터 전송의 시작 전에 persistent 저장소를 업데이트 했을 것을 보장합니다. |
---|
| 1916 | + 이는 wmb() 에 의해 이뤄지는 순서 규칙을 포함합니다. |
---|
1917 | 1917 | |
---|
1918 | | - mmiowb(); |
---|
1919 | | - |
---|
1920 | | -이것은 mandatory 쓰기 배리어의 변종으로, 완화된 순서 규칙의 I/O 영역에으로의 |
---|
1921 | | -쓰기가 부분적으로 순서를 맞추도록 해줍니다. 이 함수는 CPU->하드웨어 사이를 |
---|
1922 | | -넘어서 실제 하드웨어에까지 일부 수준의 영향을 끼칩니다. |
---|
1923 | | - |
---|
1924 | | -더 많은 정보를 위해선 "Acquire vs I/O 액세스" 서브섹션을 참고하세요. |
---|
1925 | | - |
---|
| 1918 | + Persistent memory 에서의 로드를 위해선 현재의 읽기 메모리 배리어로도 읽기 |
---|
| 1919 | + 순서를 보장하는데 충분합니다. |
---|
1926 | 1920 | |
---|
1927 | 1921 | ========================= |
---|
1928 | 1922 | 암묵적 커널 메모리 배리어 |
---|
.. | .. |
---|
2146 | 2140 | event_indicated = 1; |
---|
2147 | 2141 | wake_up_process(event_daemon); |
---|
2148 | 2142 | |
---|
2149 | | -wake_up() 류에 의해 쓰기 메모리 배리어가 내포됩니다. 만약 그것들이 뭔가를 |
---|
2150 | | -깨운다면요. 이 배리어는 태스크 상태가 지워지기 전에 수행되므로, 이벤트를 |
---|
2151 | | -알리기 위한 STORE 와 태스크 상태를 TASK_RUNNING 으로 설정하는 STORE 사이에 |
---|
2152 | | -위치하게 됩니다. |
---|
| 2143 | +wake_up() 이 무언가를 깨우게 되면, 이 함수는 범용 메모리 배리어를 수행합니다. |
---|
| 2144 | +이 함수가 아무것도 깨우지 않는다면 메모리 배리어는 수행될 수도, 수행되지 않을 |
---|
| 2145 | +수도 있습니다; 이 경우에 메모리 배리어를 수행할 거라 오해해선 안됩니다. 이 |
---|
| 2146 | +배리어는 태스크 상태가 접근되기 전에 수행되는데, 자세히 말하면 이 이벤트를 |
---|
| 2147 | +알리기 위한 STORE 와 TASK_RUNNING 으로 상태를 쓰는 STORE 사이에 수행됩니다: |
---|
2153 | 2148 | |
---|
2154 | | - CPU 1 CPU 2 |
---|
| 2149 | + CPU 1 (Sleeper) CPU 2 (Waker) |
---|
2155 | 2150 | =============================== =============================== |
---|
2156 | 2151 | set_current_state(); STORE event_indicated |
---|
2157 | 2152 | smp_store_mb(); wake_up(); |
---|
2158 | | - STORE current->state <쓰기 배리어> |
---|
2159 | | - <범용 배리어> STORE current->state |
---|
2160 | | - LOAD event_indicated |
---|
| 2153 | + STORE current->state ... |
---|
| 2154 | + <범용 배리어> <범용 배리어> |
---|
| 2155 | + LOAD event_indicated if ((LOAD task->state) & TASK_NORMAL) |
---|
| 2156 | + STORE task->state |
---|
2161 | 2157 | |
---|
2162 | | -한번더 말합니다만, 이 쓰기 메모리 배리어는 이 코드가 정말로 뭔가를 깨울 때에만 |
---|
2163 | | -실행됩니다. 이걸 설명하기 위해, X 와 Y 는 모두 0 으로 초기화 되어 있다는 가정 |
---|
2164 | | -하에 아래의 이벤트 시퀀스를 생각해 봅시다: |
---|
| 2158 | +여기서 "task" 는 깨어나지는 쓰레드이고 CPU 1 의 "current" 와 같습니다. |
---|
| 2159 | + |
---|
| 2160 | +반복하지만, wake_up() 이 무언가를 정말 깨운다면 범용 메모리 배리어가 수행될 |
---|
| 2161 | +것이 보장되지만, 그렇지 않다면 그런 보장이 없습니다. 이걸 이해하기 위해, X 와 |
---|
| 2162 | +Y 는 모두 0 으로 초기화 되어 있다는 가정 하에 아래의 이벤트 시퀀스를 생각해 |
---|
| 2163 | +봅시다: |
---|
2165 | 2164 | |
---|
2166 | 2165 | CPU 1 CPU 2 |
---|
2167 | 2166 | =============================== =============================== |
---|
2168 | | - X = 1; STORE event_indicated |
---|
| 2167 | + X = 1; Y = 1; |
---|
2169 | 2168 | smp_mb(); wake_up(); |
---|
2170 | | - Y = 1; wait_event(wq, Y == 1); |
---|
2171 | | - wake_up(); load from Y sees 1, no memory barrier |
---|
2172 | | - load from X might see 0 |
---|
| 2169 | + LOAD Y LOAD X |
---|
2173 | 2170 | |
---|
2174 | | -위 예제에서의 경우와 달리 깨우기가 정말로 행해졌다면, CPU 2 의 X 로드는 1 을 |
---|
2175 | | -본다고 보장될 수 있을 겁니다. |
---|
| 2171 | +정말로 깨우기가 행해졌다면, 두 로드 중 (최소한) 하나는 1 을 보게 됩니다. |
---|
| 2172 | +반면에, 실제 깨우기가 행해지지 않았다면, 두 로드 모두 0을 볼 수도 있습니다. |
---|
| 2173 | + |
---|
| 2174 | +wake_up_process() 는 항상 범용 메모리 배리어를 수행합니다. 이 배리어 역시 |
---|
| 2175 | +태스크 상태가 접근되기 전에 수행됩니다. 특히, 앞의 예제 코드에서 wake_up() 이 |
---|
| 2176 | +wake_up_process() 로 대체된다면 두 로드 중 하나는 1을 볼 것이 보장됩니다. |
---|
2176 | 2177 | |
---|
2177 | 2178 | 사용 가능한 깨우기류 함수들로 다음과 같은 것들이 있습니다: |
---|
2178 | 2179 | |
---|
.. | .. |
---|
2192 | 2193 | wake_up_poll(); |
---|
2193 | 2194 | wake_up_process(); |
---|
2194 | 2195 | |
---|
| 2196 | +메모리 순서규칙 관점에서, 이 함수들은 모두 wake_up() 과 같거나 보다 강한 순서 |
---|
| 2197 | +보장을 제공합니다. |
---|
2195 | 2198 | |
---|
2196 | 2199 | [!] 잠재우는 코드와 깨우는 코드에 내포되는 메모리 배리어들은 깨우기 전에 |
---|
2197 | 2200 | 이루어진 스토어를 잠재우는 코드가 set_current_state() 를 호출한 후에 행하는 |
---|
.. | .. |
---|
2274 | 2277 | *A, *B or *C following RELEASE M |
---|
2275 | 2278 | *F, *G or *H preceding ACQUIRE Q |
---|
2276 | 2279 | *E, *F or *G following RELEASE Q |
---|
2277 | | - |
---|
2278 | | - |
---|
2279 | | - |
---|
2280 | | -ACQUIRE VS I/O 액세스 |
---|
2281 | | ----------------------- |
---|
2282 | | - |
---|
2283 | | -특정한 (특히 NUMA 가 관련된) 환경 하에서 두개의 CPU 에서 동일한 스핀락으로 |
---|
2284 | | -보호되는 두개의 크리티컬 섹션 안의 I/O 액세스는 PCI 브릿지에 겹쳐진 I/O |
---|
2285 | | -액세스로 보일 수 있는데, PCI 브릿지는 캐시 일관성 프로토콜과 합을 맞춰야 할 |
---|
2286 | | -의무가 없으므로, 필요한 읽기 메모리 배리어가 요청되지 않기 때문입니다. |
---|
2287 | | - |
---|
2288 | | -예를 들어서: |
---|
2289 | | - |
---|
2290 | | - CPU 1 CPU 2 |
---|
2291 | | - =============================== =============================== |
---|
2292 | | - spin_lock(Q) |
---|
2293 | | - writel(0, ADDR) |
---|
2294 | | - writel(1, DATA); |
---|
2295 | | - spin_unlock(Q); |
---|
2296 | | - spin_lock(Q); |
---|
2297 | | - writel(4, ADDR); |
---|
2298 | | - writel(5, DATA); |
---|
2299 | | - spin_unlock(Q); |
---|
2300 | | - |
---|
2301 | | -는 PCI 브릿지에 다음과 같이 보일 수 있습니다: |
---|
2302 | | - |
---|
2303 | | - STORE *ADDR = 0, STORE *ADDR = 4, STORE *DATA = 1, STORE *DATA = 5 |
---|
2304 | | - |
---|
2305 | | -이렇게 되면 하드웨어의 오동작을 일으킬 수 있습니다. |
---|
2306 | | - |
---|
2307 | | - |
---|
2308 | | -이런 경우엔 잡아둔 스핀락을 내려놓기 전에 mmiowb() 를 수행해야 하는데, 예를 |
---|
2309 | | -들면 다음과 같습니다: |
---|
2310 | | - |
---|
2311 | | - CPU 1 CPU 2 |
---|
2312 | | - =============================== =============================== |
---|
2313 | | - spin_lock(Q) |
---|
2314 | | - writel(0, ADDR) |
---|
2315 | | - writel(1, DATA); |
---|
2316 | | - mmiowb(); |
---|
2317 | | - spin_unlock(Q); |
---|
2318 | | - spin_lock(Q); |
---|
2319 | | - writel(4, ADDR); |
---|
2320 | | - writel(5, DATA); |
---|
2321 | | - mmiowb(); |
---|
2322 | | - spin_unlock(Q); |
---|
2323 | | - |
---|
2324 | | -이 코드는 CPU 1 에서 요청된 두개의 스토어가 PCI 브릿지에 CPU 2 에서 요청된 |
---|
2325 | | -스토어들보다 먼저 보여짐을 보장합니다. |
---|
2326 | | - |
---|
2327 | | - |
---|
2328 | | -또한, 같은 디바이스에서 스토어를 이어 로드가 수행되면 이 로드는 로드가 수행되기 |
---|
2329 | | -전에 스토어가 완료되기를 강제하므로 mmiowb() 의 필요가 없어집니다: |
---|
2330 | | - |
---|
2331 | | - CPU 1 CPU 2 |
---|
2332 | | - =============================== =============================== |
---|
2333 | | - spin_lock(Q) |
---|
2334 | | - writel(0, ADDR) |
---|
2335 | | - a = readl(DATA); |
---|
2336 | | - spin_unlock(Q); |
---|
2337 | | - spin_lock(Q); |
---|
2338 | | - writel(4, ADDR); |
---|
2339 | | - b = readl(DATA); |
---|
2340 | | - spin_unlock(Q); |
---|
2341 | | - |
---|
2342 | | - |
---|
2343 | | -더 많은 정보를 위해선 Documentation/driver-api/device-io.rst 를 참고하세요. |
---|
2344 | 2280 | |
---|
2345 | 2281 | |
---|
2346 | 2282 | ========================= |
---|
.. | .. |
---|
2487 | 2423 | 리눅스 커널 내부에서, I/O 는 어떻게 액세스들을 적절히 순차적이게 만들 수 있는지 |
---|
2488 | 2424 | 알고 있는, - inb() 나 writel() 과 같은 - 적절한 액세스 루틴을 통해 이루어져야만 |
---|
2489 | 2425 | 합니다. 이것들은 대부분의 경우에는 명시적 메모리 배리어 와 함께 사용될 필요가 |
---|
2490 | | -없습니다만, 다음의 두가지 상황에서는 명시적 메모리 배리어가 필요할 수 있습니다: |
---|
2491 | | - |
---|
2492 | | - (1) 일부 시스템에서 I/O 스토어는 모든 CPU 에 일관되게 순서 맞춰지지 않는데, |
---|
2493 | | - 따라서 _모든_ 일반적인 드라이버들에 락이 사용되어야만 하고 이 크리티컬 |
---|
2494 | | - 섹션을 빠져나오기 전에 mmiowb() 가 꼭 호출되어야 합니다. |
---|
2495 | | - |
---|
2496 | | - (2) 만약 액세스 함수들이 완화된 메모리 액세스 속성을 갖는 I/O 메모리 윈도우를 |
---|
2497 | | - 사용한다면, 순서를 강제하기 위해선 _mandatory_ 메모리 배리어가 필요합니다. |
---|
| 2426 | +없습니다만, 완화된 메모리 액세스 속성으로 I/O 메모리 윈도우로의 참조를 위해 |
---|
| 2427 | +액세스 함수가 사용된다면 순서를 강제하기 위해 _mandatory_ 메모리 배리어가 |
---|
| 2428 | +필요합니다. |
---|
2498 | 2429 | |
---|
2499 | 2430 | 더 많은 정보를 위해선 Documentation/driver-api/device-io.rst 를 참고하십시오. |
---|
2500 | 2431 | |
---|
.. | .. |
---|
2538 | 2469 | 인터럽트 내에서 일어난 액세스와 섞일 수 있다고 - 그리고 그 반대도 - 가정해야만 |
---|
2539 | 2470 | 합니다. |
---|
2540 | 2471 | |
---|
2541 | | -그런 영역 안에서 일어나는 I/O 액세스들은 엄격한 순서 규칙의 I/O 레지스터에 |
---|
2542 | | -묵시적 I/O 배리어를 형성하는 동기적 (synchronous) 로드 오퍼레이션을 포함하기 |
---|
2543 | | -때문에 일반적으로는 이런게 문제가 되지 않습니다. 만약 이걸로는 충분치 않다면 |
---|
2544 | | -mmiowb() 가 명시적으로 사용될 필요가 있습니다. |
---|
| 2472 | +그런 영역 안에서 일어나는 I/O 액세스는 묵시적 I/O 배리어를 형성하는, 엄격한 |
---|
| 2473 | +순서 규칙의 I/O 레지스터로의 로드 오퍼레이션을 포함하기 때문에 일반적으로는 |
---|
| 2474 | +문제가 되지 않습니다. |
---|
2545 | 2475 | |
---|
2546 | 2476 | |
---|
2547 | 2477 | 하나의 인터럽트 루틴과 별도의 CPU 에서 수행중이며 서로 통신을 하는 두 루틴 |
---|
.. | .. |
---|
2553 | 2483 | 커널 I/O 배리어의 효과 |
---|
2554 | 2484 | ====================== |
---|
2555 | 2485 | |
---|
2556 | | -I/O 메모리에 액세스할 때, 드라이버는 적절한 액세스 함수를 사용해야 합니다: |
---|
2557 | | - |
---|
2558 | | - (*) inX(), outX(): |
---|
2559 | | - |
---|
2560 | | - 이것들은 메모리 공간보다는 I/O 공간에 이야기를 하려는 의도로 |
---|
2561 | | - 만들어졌습니다만, 그건 기본적으로 CPU 마다 다른 컨셉입니다. i386 과 |
---|
2562 | | - x86_64 프로세서들은 특별한 I/O 공간 액세스 사이클과 명령어를 실제로 가지고 |
---|
2563 | | - 있지만, 다른 많은 CPU 들에는 그런 컨셉이 존재하지 않습니다. |
---|
2564 | | - |
---|
2565 | | - 다른 것들 중에서도 PCI 버스가 I/O 공간 컨셉을 정의하는데, 이는 - i386 과 |
---|
2566 | | - x86_64 같은 CPU 에서 - CPU 의 I/O 공간 컨셉으로 쉽게 매치됩니다. 하지만, |
---|
2567 | | - 대체할 I/O 공간이 없는 CPU 에서는 CPU 의 메모리 맵의 가상 I/O 공간으로 |
---|
2568 | | - 매핑될 수도 있습니다. |
---|
2569 | | - |
---|
2570 | | - 이 공간으로의 액세스는 (i386 등에서는) 완전하게 동기화 됩니다만, 중간의 |
---|
2571 | | - (PCI 호스트 브리지와 같은) 브리지들은 이를 완전히 보장하진 않을수도 |
---|
2572 | | - 있습니다. |
---|
2573 | | - |
---|
2574 | | - 이것들의 상호간의 순서는 완전하게 보장됩니다. |
---|
2575 | | - |
---|
2576 | | - 다른 타입의 메모리 오퍼레이션, I/O 오퍼레이션에 대한 순서는 완전하게 |
---|
2577 | | - 보장되지는 않습니다. |
---|
| 2486 | +I/O 액세스를 통한 주변장치와의 통신은 아키텍쳐와 기기에 매우 종속적입니다. |
---|
| 2487 | +따라서, 본질적으로 이식성이 없는 드라이버는 가능한 가장 적은 오버헤드로 |
---|
| 2488 | +동기화를 하기 위해 각자의 타겟 시스템의 특정 동작에 의존할 겁니다. 다양한 |
---|
| 2489 | +아키텍쳐와 버스 구현에 이식성을 가지려 하는 드라이버를 위해, 커널은 다양한 |
---|
| 2490 | +정도의 순서 보장을 제공하는 일련의 액세스 함수를 제공합니다. |
---|
2578 | 2491 | |
---|
2579 | 2492 | (*) readX(), writeX(): |
---|
2580 | 2493 | |
---|
2581 | | - 이것들이 수행 요청되는 CPU 에서 서로에게 완전히 순서가 맞춰지고 독립적으로 |
---|
2582 | | - 수행되는지에 대한 보장 여부는 이들이 액세스 하는 메모리 윈도우에 정의된 |
---|
2583 | | - 특성에 의해 결정됩니다. 예를 들어, 최신의 i386 아키텍쳐 머신에서는 MTRR |
---|
2584 | | - 레지스터로 이 특성이 조정됩니다. |
---|
| 2494 | + readX() 와 writeX() MMIO 액세스 함수는 접근되는 주변장치로의 포인터를 |
---|
| 2495 | + __iomem * 패러미터로 받습니다. 디폴트 I/O 기능으로 매핑되는 포인터 |
---|
| 2496 | + (예: ioremap() 으로 반환되는 것) 의 순서 보장은 다음과 같습니다: |
---|
2585 | 2497 | |
---|
2586 | | - 일반적으로는, 프리페치 (prefetch) 가능한 디바이스를 액세스 하는게 |
---|
2587 | | - 아니라면, 이것들은 완전히 순서가 맞춰지고 결합되지 않게 보장될 겁니다. |
---|
| 2498 | + 1. 같은 주변장치로의 모든 readX() 와 writeX() 액세스는 각자에 대해 |
---|
| 2499 | + 순서지어집니다. 이는 같은 CPU 쓰레드에 의한 특정 디바이스로의 MMIO |
---|
| 2500 | + 레지스터 액세스가 프로그램 순서대로 도착할 것을 보장합니다. |
---|
2588 | 2501 | |
---|
2589 | | - 하지만, (PCI 브리지와 같은) 중간의 하드웨어는 자신이 원한다면 집행을 |
---|
2590 | | - 연기시킬 수 있습니다; 스토어 명령을 실제로 하드웨어로 내려보내기(flush) |
---|
2591 | | - 위해서는 같은 위치로부터 로드를 하는 방법이 있습니다만[*], PCI 의 경우는 |
---|
2592 | | - 같은 디바이스나 환경 구성 영역에서의 로드만으로도 충분할 겁니다. |
---|
| 2502 | + 2. 한 스핀락을 잡은 CPU 쓰레드에 의한 writeX() 는 같은 스핀락을 나중에 |
---|
| 2503 | + 잡은 다른 CPU 쓰레드에 의해 같은 주변장치를 향해 호출된 writeX() |
---|
| 2504 | + 앞으로 순서지어집니다. 이는 스핀락을 잡은 채 특정 디바이스를 향해 |
---|
| 2505 | + 호출된 MMIO 레지스터 쓰기는 해당 락의 획득에 일관적인 순서로 도달할 |
---|
| 2506 | + 것을 보장합니다. |
---|
2593 | 2507 | |
---|
2594 | | - [*] 주의! 쓰여진 것과 같은 위치로부터의 로드를 시도하는 것은 오동작을 |
---|
2595 | | - 일으킬 수도 있습니다 - 예로 16650 Rx/Tx 시리얼 레지스터를 생각해 |
---|
2596 | | - 보세요. |
---|
| 2508 | + 3. 특정 주변장치를 향한 특정 CPU 쓰레드의 writeX() 는 먼저 해당 |
---|
| 2509 | + 쓰레드로 전파되는, 또는 해당 쓰레드에 의해 요청된 모든 앞선 메모리 |
---|
| 2510 | + 쓰기가 완료되기 전까지 먼저 기다립니다. 이는 dma_alloc_coherent() |
---|
| 2511 | + 를 통해 할당된 전송용 DMA 버퍼로의 해당 CPU 의 쓰기가 이 CPU 가 이 |
---|
| 2512 | + 전송을 시작시키기 위해 MMIO 컨트롤 레지스터에 쓰기를 할 때 DMA |
---|
| 2513 | + 엔진에 보여질 것을 보장합니다. |
---|
2597 | 2514 | |
---|
2598 | | - 프리페치 가능한 I/O 메모리가 사용되면, 스토어 명령들이 순서를 지키도록 |
---|
2599 | | - 하기 위해 mmiowb() 배리어가 필요할 수 있습니다. |
---|
| 2515 | + 4. 특정 CPU 쓰레드에 의한 주변장치로의 readX() 는 같은 쓰레드에 의한 |
---|
| 2516 | + 모든 뒤따르는 메모리 읽기가 시작되기 전에 완료됩니다. 이는 |
---|
| 2517 | + dma_alloc_coherent() 를 통해 할당된 수신용 DMA 버퍼로부터의 CPU 의 |
---|
| 2518 | + 읽기는 이 DMA 수신의 완료를 표시하는 DMA 엔진의 MMIO 상태 레지스터 |
---|
| 2519 | + 읽기 후에는 오염된 데이터를 읽지 않을 것을 보장합니다. |
---|
2600 | 2520 | |
---|
2601 | | - PCI 트랜잭션 사이의 상호작용에 대해 더 많은 정보를 위해선 PCI 명세서를 |
---|
2602 | | - 참고하시기 바랍니다. |
---|
| 2521 | + 5. CPU 에 의한 주변장치로의 readX() 는 모든 뒤따르는 delay() 루프가 |
---|
| 2522 | + 수행을 시작하기 전에 완료됩니다. 이는 CPU 의 특정 |
---|
| 2523 | + 주변장치로의 두개의 MMIO 레지스터 쓰기가 행해지는데 첫번째 쓰기가 |
---|
| 2524 | + readX() 를 통해 곧바로 읽어졌고 이어 두번째 writeX() 전에 udelay(1) |
---|
| 2525 | + 이 호출되었다면 이 두개의 쓰기는 최소 1us 의 간격을 두고 행해질 것을 |
---|
| 2526 | + 보장합니다: |
---|
| 2527 | + |
---|
| 2528 | + writel(42, DEVICE_REGISTER_0); // 디바이스에 도착함... |
---|
| 2529 | + readl(DEVICE_REGISTER_0); |
---|
| 2530 | + udelay(1); |
---|
| 2531 | + writel(42, DEVICE_REGISTER_1); // ...이것보다 최소 1us 전에. |
---|
| 2532 | + |
---|
| 2533 | + 디폴트가 아닌 기능을 통해 얻어지는 __iomem 포인터 (예: ioremap_wc() 를 |
---|
| 2534 | + 통해 리턴되는 것) 의 순서 속성은 실제 아키텍쳐에 의존적이어서 이런 |
---|
| 2535 | + 종류의 매핑으로의 액세스는 앞서 설명된 보장사항에 의존할 수 없습니다. |
---|
2603 | 2536 | |
---|
2604 | 2537 | (*) readX_relaxed(), writeX_relaxed() |
---|
2605 | 2538 | |
---|
2606 | | - 이것들은 readX() 와 writeX() 랑 비슷하지만, 더 완화된 메모리 순서 보장을 |
---|
2607 | | - 제공합니다. 구체적으로, 이것들은 일반적 메모리 액세스 (예: DMA 버퍼) 에도 |
---|
2608 | | - LOCK 이나 UNLOCK 오퍼레이션들에도 순서를 보장하지 않습니다. LOCK 이나 |
---|
2609 | | - UNLOCK 오퍼레이션들에 맞춰지는 순서가 필요하다면, mmiowb() 배리어가 사용될 |
---|
2610 | | - 수 있습니다. 같은 주변 장치에의 완화된 액세스끼리는 순서가 지켜짐을 알아 |
---|
2611 | | - 두시기 바랍니다. |
---|
| 2539 | + 이것들은 readX() 와 writeX() 랑 비슷하지만, 더 완화된 메모리 순서 |
---|
| 2540 | + 보장을 제공합니다. 구체적으로, 이것들은 일반적 메모리 액세스나 delay() |
---|
| 2541 | + 루프 (예:앞의 2-5 항목) 에 대해 순서를 보장하지 않습니다만 디폴트 I/O |
---|
| 2542 | + 기능으로 매핑된 __iomem 포인터에 대해 동작할 때, 같은 CPU 쓰레드에 의한 |
---|
| 2543 | + 같은 주변장치로의 액세스에는 순서가 맞춰질 것이 보장됩니다. |
---|
| 2544 | + |
---|
| 2545 | + (*) readsX(), writesX(): |
---|
| 2546 | + |
---|
| 2547 | + readsX() 와 writesX() MMIO 액세스 함수는 DMA 를 수행하는데 적절치 않은, |
---|
| 2548 | + 주변장치 내의 메모리 매핑된 레지스터 기반 FIFO 로의 액세스를 위해 |
---|
| 2549 | + 설계되었습니다. 따라서, 이 기능들은 앞서 설명된 readX_relaxed() 와 |
---|
| 2550 | + writeX_relaxed() 의 순서 보장만을 제공합니다. |
---|
| 2551 | + |
---|
| 2552 | + (*) inX(), outX(): |
---|
| 2553 | + |
---|
| 2554 | + inX() 와 outX() 액세스 함수는 일부 아키텍쳐 (특히 x86) 에서는 특수한 |
---|
| 2555 | + 명령어를 필요로 하며 포트에 매핑되는, 과거의 유산인 I/O 주변장치로의 |
---|
| 2556 | + 접근을 위해 만들어졌습니다. |
---|
| 2557 | + |
---|
| 2558 | + 많은 CPU 아키텍쳐가 결국은 이런 주변장치를 내부의 가상 메모리 매핑을 |
---|
| 2559 | + 통해 접근하기 때문에, inX() 와 outX() 가 제공하는 이식성 있는 순서 |
---|
| 2560 | + 보장은 디폴트 I/O 기능을 통한 매핑을 접근할 때의 readX() 와 writeX() 에 |
---|
| 2561 | + 의해 제공되는 것과 각각 동일합니다. |
---|
| 2562 | + |
---|
| 2563 | + 디바이스 드라이버는 outX() 가 리턴하기 전에 해당 I/O 주변장치로부터의 |
---|
| 2564 | + 완료 응답을 기다리는 쓰기 트랜잭션을 만들어 낸다고 기대할 수도 |
---|
| 2565 | + 있습니다. 이는 모든 아키텍쳐에서 보장되지는 않고, 따라서 이식성 있는 |
---|
| 2566 | + 순서 규칙의 일부분이 아닙니다. |
---|
| 2567 | + |
---|
| 2568 | + (*) insX(), outsX(): |
---|
| 2569 | + |
---|
| 2570 | + 앞에서와 같이, insX() 와 outsX() 액세스 함수는 디폴트 I/O 기능을 통한 |
---|
| 2571 | + 매핑을 접근할 때 각각 readX() 와 writeX() 와 같은 순서 보장을 |
---|
| 2572 | + 제공합니다. |
---|
2612 | 2573 | |
---|
2613 | 2574 | (*) ioreadX(), iowriteX() |
---|
2614 | 2575 | |
---|
2615 | | - 이것들은 inX()/outX() 나 readX()/writeX() 처럼 실제로 수행하는 액세스의 |
---|
2616 | | - 종류에 따라 적절하게 수행될 것입니다. |
---|
| 2576 | + 이것들은 inX()/outX() 나 readX()/writeX() 처럼 실제로 수행하는 액세스의 |
---|
| 2577 | + 종류에 따라 적절하게 수행될 것입니다. |
---|
| 2578 | + |
---|
| 2579 | +String 액세스 함수 (insX(), outsX(), readsX() 그리고 writesX()) 의 예외를 |
---|
| 2580 | +제외하고는, 앞의 모든 것이 아랫단의 주변장치가 little-endian 이라 가정하며, |
---|
| 2581 | +따라서 big-endian 아키텍쳐에서는 byte-swapping 오퍼레이션을 수행합니다. |
---|
2617 | 2582 | |
---|
2618 | 2583 | |
---|
2619 | 2584 | =================================== |
---|
.. | .. |
---|
2705 | 2670 | 여부는 디바이스가 액세스 되는 메모리 윈도우의 특성에 의해 결정될 수도 있고, CPU |
---|
2706 | 2671 | 가 가지고 있을 수 있는 특수한 디바이스 통신 인스트럭션의 사용에 의해서 결정될 |
---|
2707 | 2672 | 수도 있습니다. |
---|
2708 | | - |
---|
2709 | | - |
---|
2710 | | -캐시 일관성 |
---|
2711 | | ------------ |
---|
2712 | | - |
---|
2713 | | -하지만 삶은 앞에서 이야기한 것처럼 단순하지 않습니다: 캐시들은 일관적일 것으로 |
---|
2714 | | -기대되지만, 그 일관성이 순서에도 적용될 거라는 보장은 없습니다. 한 CPU 에서 |
---|
2715 | | -만들어진 변경 사항은 최종적으로는 시스템의 모든 CPU 에게 보여지게 되지만, 다른 |
---|
2716 | | -CPU 들에게도 같은 순서로 보이게 될 거라는 보장은 없다는 뜻입니다. |
---|
2717 | | - |
---|
2718 | | - |
---|
2719 | | -두개의 CPU (1 & 2) 가 달려 있고, 각 CPU 에 두개의 데이터 캐시(CPU 1 은 A/B 를, |
---|
2720 | | -CPU 2 는 C/D 를 갖습니다)가 병렬로 연결되어 있는 시스템을 다룬다고 생각해 |
---|
2721 | | -봅시다: |
---|
2722 | | - |
---|
2723 | | - : |
---|
2724 | | - : +--------+ |
---|
2725 | | - : +---------+ | | |
---|
2726 | | - +--------+ : +--->| Cache A |<------->| | |
---|
2727 | | - | | : | +---------+ | | |
---|
2728 | | - | CPU 1 |<---+ | | |
---|
2729 | | - | | : | +---------+ | | |
---|
2730 | | - +--------+ : +--->| Cache B |<------->| | |
---|
2731 | | - : +---------+ | | |
---|
2732 | | - : | Memory | |
---|
2733 | | - : +---------+ | System | |
---|
2734 | | - +--------+ : +--->| Cache C |<------->| | |
---|
2735 | | - | | : | +---------+ | | |
---|
2736 | | - | CPU 2 |<---+ | | |
---|
2737 | | - | | : | +---------+ | | |
---|
2738 | | - +--------+ : +--->| Cache D |<------->| | |
---|
2739 | | - : +---------+ | | |
---|
2740 | | - : +--------+ |
---|
2741 | | - : |
---|
2742 | | - |
---|
2743 | | -이 시스템이 다음과 같은 특성을 갖는다 생각해 봅시다: |
---|
2744 | | - |
---|
2745 | | - (*) 홀수번 캐시라인은 캐시 A, 캐시 C 또는 메모리에 위치할 수 있음; |
---|
2746 | | - |
---|
2747 | | - (*) 짝수번 캐시라인은 캐시 B, 캐시 D 또는 메모리에 위치할 수 있음; |
---|
2748 | | - |
---|
2749 | | - (*) CPU 코어가 한개의 캐시에 접근하는 동안, 다른 캐시는 - 더티 캐시라인을 |
---|
2750 | | - 메모리에 내리거나 추측성 로드를 하거나 하기 위해 - 시스템의 다른 부분에 |
---|
2751 | | - 액세스 하기 위해 버스를 사용할 수 있음; |
---|
2752 | | - |
---|
2753 | | - (*) 각 캐시는 시스템의 나머지 부분들과 일관성을 맞추기 위해 해당 캐시에 |
---|
2754 | | - 적용되어야 할 오퍼레이션들의 큐를 가짐; |
---|
2755 | | - |
---|
2756 | | - (*) 이 일관성 큐는 캐시에 이미 존재하는 라인에 가해지는 평범한 로드에 의해서는 |
---|
2757 | | - 비워지지 않는데, 큐의 오퍼레이션들이 이 로드의 결과에 영향을 끼칠 수 있다 |
---|
2758 | | - 할지라도 그러함. |
---|
2759 | | - |
---|
2760 | | -이제, 첫번째 CPU 에서 두개의 쓰기 오퍼레이션을 만드는데, 해당 CPU 의 캐시에 |
---|
2761 | | -요청된 순서로 오퍼레이션이 도달됨을 보장하기 위해 두 오퍼레이션 사이에 쓰기 |
---|
2762 | | -배리어를 사용하는 상황을 상상해 봅시다: |
---|
2763 | | - |
---|
2764 | | - CPU 1 CPU 2 COMMENT |
---|
2765 | | - =============== =============== ======================================= |
---|
2766 | | - u == 0, v == 1 and p == &u, q == &u |
---|
2767 | | - v = 2; |
---|
2768 | | - smp_wmb(); v 의 변경이 p 의 변경 전에 보일 것을 |
---|
2769 | | - 분명히 함 |
---|
2770 | | - <A:modify v=2> v 는 이제 캐시 A 에 독점적으로 존재함 |
---|
2771 | | - p = &v; |
---|
2772 | | - <B:modify p=&v> p 는 이제 캐시 B 에 독점적으로 존재함 |
---|
2773 | | - |
---|
2774 | | -여기서의 쓰기 메모리 배리어는 CPU 1 의 캐시가 올바른 순서로 업데이트 된 것으로 |
---|
2775 | | -시스템의 다른 CPU 들이 인지하게 만듭니다. 하지만, 이제 두번째 CPU 가 그 값들을 |
---|
2776 | | -읽으려 하는 상황을 생각해 봅시다: |
---|
2777 | | - |
---|
2778 | | - CPU 1 CPU 2 COMMENT |
---|
2779 | | - =============== =============== ======================================= |
---|
2780 | | - ... |
---|
2781 | | - q = p; |
---|
2782 | | - x = *q; |
---|
2783 | | - |
---|
2784 | | -위의 두개의 읽기 오퍼레이션은 예상된 순서로 일어나지 못할 수 있는데, 두번째 CPU |
---|
2785 | | -의 한 캐시에 다른 캐시 이벤트가 발생해 v 를 담고 있는 캐시라인의 해당 캐시에의 |
---|
2786 | | -업데이트가 지연되는 사이, p 를 담고 있는 캐시라인은 두번째 CPU 의 다른 캐시에 |
---|
2787 | | -업데이트 되어버렸을 수 있기 때문입니다. |
---|
2788 | | - |
---|
2789 | | - CPU 1 CPU 2 COMMENT |
---|
2790 | | - =============== =============== ======================================= |
---|
2791 | | - u == 0, v == 1 and p == &u, q == &u |
---|
2792 | | - v = 2; |
---|
2793 | | - smp_wmb(); |
---|
2794 | | - <A:modify v=2> <C:busy> |
---|
2795 | | - <C:queue v=2> |
---|
2796 | | - p = &v; q = p; |
---|
2797 | | - <D:request p> |
---|
2798 | | - <B:modify p=&v> <D:commit p=&v> |
---|
2799 | | - <D:read p> |
---|
2800 | | - x = *q; |
---|
2801 | | - <C:read *q> 캐시에 업데이트 되기 전의 v 를 읽음 |
---|
2802 | | - <C:unbusy> |
---|
2803 | | - <C:commit v=2> |
---|
2804 | | - |
---|
2805 | | -기본적으로, 두개의 캐시라인 모두 CPU 2 에 최종적으로는 업데이트 될 것이지만, |
---|
2806 | | -별도의 개입 없이는, 업데이트의 순서가 CPU 1 에서 만들어진 순서와 동일할 |
---|
2807 | | -것이라는 보장이 없습니다. |
---|
2808 | | - |
---|
2809 | | - |
---|
2810 | | -여기에 개입하기 위해선, 데이터 의존성 배리어나 읽기 배리어를 로드 오퍼레이션들 |
---|
2811 | | -사이에 넣어야 합니다 (v4.15 부터는 READ_ONCE() 매크로에 의해 무조건적으로 |
---|
2812 | | -그렇게 됩니다). 이렇게 함으로써 캐시가 다음 요청을 처리하기 전에 일관성 큐를 |
---|
2813 | | -처리하도록 강제하게 됩니다. |
---|
2814 | | - |
---|
2815 | | - CPU 1 CPU 2 COMMENT |
---|
2816 | | - =============== =============== ======================================= |
---|
2817 | | - u == 0, v == 1 and p == &u, q == &u |
---|
2818 | | - v = 2; |
---|
2819 | | - smp_wmb(); |
---|
2820 | | - <A:modify v=2> <C:busy> |
---|
2821 | | - <C:queue v=2> |
---|
2822 | | - p = &v; q = p; |
---|
2823 | | - <D:request p> |
---|
2824 | | - <B:modify p=&v> <D:commit p=&v> |
---|
2825 | | - <D:read p> |
---|
2826 | | - smp_read_barrier_depends() |
---|
2827 | | - <C:unbusy> |
---|
2828 | | - <C:commit v=2> |
---|
2829 | | - x = *q; |
---|
2830 | | - <C:read *q> 캐시에 업데이트 된 v 를 읽음 |
---|
2831 | | - |
---|
2832 | | - |
---|
2833 | | -이런 부류의 문제는 DEC Alpha 계열 프로세서들에서 발견될 수 있는데, 이들은 |
---|
2834 | | -데이터 버스를 좀 더 잘 사용해 성능을 개선할 수 있는, 분할된 캐시를 가지고 있기 |
---|
2835 | | -때문입니다. 대부분의 CPU 는 하나의 읽기 오퍼레이션의 메모리 액세스가 다른 읽기 |
---|
2836 | | -오퍼레이션에 의존적이라면 데이터 의존성 배리어를 내포시킵니다만, 모두가 그런건 |
---|
2837 | | -아니기 때문에 이점에 의존해선 안됩니다. |
---|
2838 | | - |
---|
2839 | | -다른 CPU 들도 분할된 캐시를 가지고 있을 수 있지만, 그런 CPU 들은 평범한 메모리 |
---|
2840 | | -액세스를 위해서도 이 분할된 캐시들 사이의 조정을 해야만 합니다. Alpha 는 가장 |
---|
2841 | | -약한 메모리 순서 시맨틱 (semantic) 을 선택함으로써 메모리 배리어가 명시적으로 |
---|
2842 | | -사용되지 않았을 때에는 그런 조정이 필요하지 않게 했으며, 이는 Alpha 가 당시에 |
---|
2843 | | -더 높은 CPU 클락 속도를 가질 수 있게 했습니다. 하지만, (다시 말하건대, v4.15 |
---|
2844 | | -이후부터는) Alpha 아키텍쳐 전용 코드와 READ_ONCE() 매크로 내부에서를 제외하고는 |
---|
2845 | | -smp_read_barrier_depends() 가 사용되지 않아야 함을 알아두시기 바랍니다. |
---|
2846 | 2673 | |
---|
2847 | 2674 | |
---|
2848 | 2675 | 캐시 일관성 VS DMA |
---|
.. | .. |
---|
3005 | 2832 | 데이터의 발견을 올바른 순서로 일어나게 하기 때문입니다. |
---|
3006 | 2833 | |
---|
3007 | 2834 | 리눅스 커널의 메모리 배리어 모델은 Alpha 에 기초해서 정의되었습니다만, v4.15 |
---|
3008 | | -부터는 리눅스 커널이 READ_ONCE() 내에 smp_read_barrier_depends() 를 추가해서 |
---|
3009 | | -Alpha 의 메모리 모델로의 영향력이 크게 줄어들긴 했습니다. |
---|
3010 | | - |
---|
3011 | | -위의 "캐시 일관성" 서브섹션을 참고하세요. |
---|
| 2835 | +부터는 Alpha 용 READ_ONCE() 코드 내에 smp_mb() 가 추가되어서 메모리 모델로의 |
---|
| 2836 | +Alpha 의 영향력이 크게 줄어들었습니다. |
---|
3012 | 2837 | |
---|
3013 | 2838 | |
---|
3014 | 2839 | 가상 머신 게스트 |
---|