요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
==============================================
Ordering I/O writes to memory-mapped addresses
==============================================
On some platforms, so-called memory-mapped I/O is weakly ordered. On such
platforms, driver writers are responsible for ensuring that I/O writes to
memory-mapped addresses on their device arrive in the order intended. This is
typically done by reading a 'safe' device or bridge register, causing the I/O
chipset to flush pending writes to the device before any reads are posted. A
driver would usually use this technique immediately prior to the exit of a
critical section of code protected by spinlocks. This would ensure that
subsequent writes to I/O space arrived only after all prior writes (much like a
memory barrier op, mb(), only with respect to I/O).
A more concrete example from a hypothetical device driver::
...
CPU A: spin_lock_irqsave(&dev_lock, flags)
CPU A: val = readl(my_status);
CPU A: ...
CPU A: writel(newval, ring_ptr);
CPU A: spin_unlock_irqrestore(&dev_lock, flags)
...
CPU B: spin_lock_irqsave(&dev_lock, flags)
CPU B: val = readl(my_status);
CPU B: ...
CPU B: writel(newval2, ring_ptr);
CPU B: spin_unlock_irqrestore(&dev_lock, flags)
...
In the case above, the device may receive newval2 before it receives newval,
which could cause problems. Fixing it is easy enough though::
...
CPU A: spin_lock_irqsave(&dev_lock, flags)
CPU A: val = readl(my_status);
CPU A: ...
CPU A: writel(newval, ring_ptr);
CPU A: (void)readl(safe_register); /* maybe a config register? */
CPU A: spin_unlock_irqrestore(&dev_lock, flags)
...
CPU B: spin_lock_irqsave(&dev_lock, flags)
CPU B: val = readl(my_status);
CPU B: ...
CPU B: writel(newval2, ring_ptr);
CPU B: (void)readl(safe_register); /* maybe a config register? */
CPU B: spin_unlock_irqrestore(&dev_lock, flags)
Here, the reads from safe_register will cause the I/O chipset to flush any
pending writes before actually posting the read to the chipset, preventing
possible data corruption.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Memory-mapped I/O write ordering
1-14문서 제목은 `Ordering I/O writes to memory-mapped addresses`입니다. 일부 platform에서 memory-mapped I/O는 weakly ordered이므로 driver 작성자가 device의 memory-mapped address로 보낸 I/O write가 의도한 순서로 도착하도록 보장해야 합니다.
일반적인 방법은 안전한 device 또는 bridge register를 읽어, read가 post되기 전에 I/O chipset이 device로 향한 pending write를 flush하게 하는 것입니다. Driver는 보통 spinlock으로 보호한 critical section을 빠져나가기 직전에 이 기법을 사용합니다. 그러면 이후 I/O write는 앞선 write가 모두 도착한 뒤 도착하며, I/O에 한정한 `mb()` memory barrier와 비슷한 효과를 냅니다.
Critical section 끝에서 pending I/O write 순서를 확정합니다.
Write reordering 문제 예
15-30가상 driver에서 CPU A와 CPU B는 같은 `dev_lock`을 차례로 잡고 `my_status`를 읽은 뒤 `ring_ptr`에 `newval`과 `newval2`를 씁니다. Spinlock은 CPU의 critical section 순서를 정하지만 weakly ordered I/O fabric의 pending write 도착 순서까지 자동으로 보장하지는 않습니다.
...
CPU A: spin_lock_irqsave(&dev_lock, flags)
CPU A: val = readl(my_status);
CPU A: ...
CPU A: writel(newval, ring_ptr);
CPU A: spin_unlock_irqrestore(&dev_lock, flags)
...
CPU B: spin_lock_irqsave(&dev_lock, flags)
CPU B: val = readl(my_status);
CPU B: ...
CPU B: writel(newval2, ring_ptr);
CPU B: spin_unlock_irqrestore(&dev_lock, flags)
...
CPU lock 순서와 device write 도착 순서가 달라질 수 있습니다.
Safe read로 ordering 보장
31-48위 경우 device가 `newval`보다 `newval2`를 먼저 받아 문제를 일으킬 수 있습니다. 수정은 각 `writel()` 뒤, spinlock을 풀기 전에 `safe_register`를 `readl()`하는 것입니다.
...
CPU A: spin_lock_irqsave(&dev_lock, flags)
CPU A: val = readl(my_status);
CPU A: ...
CPU A: writel(newval, ring_ptr);
CPU A: (void)readl(safe_register); /* maybe a config register? */
CPU A: spin_unlock_irqrestore(&dev_lock, flags)
...
CPU B: spin_lock_irqsave(&dev_lock, flags)
CPU B: val = readl(my_status);
CPU B: ...
CPU B: writel(newval2, ring_ptr);
CPU B: (void)readl(safe_register); /* maybe a config register? */
CPU B: spin_unlock_irqrestore(&dev_lock, flags)
Critical section의 마지막 I/O operation 차이입니다.
Data corruption 방지
49-51`safe_register` read는 실제 read를 chipset에 post하기 전에 모든 pending write를 flush하게 하므로 가능한 data corruption을 방지합니다.
CPU A와 B의 write가 device에 확정되는 순서입니다.
요약과 해설
io_ordering.rst:1-51Spinlock은 CPU critical section 순서를 보장하지만 weakly ordered I/O fabric의 write 도착 순서까지 보장하지 않습니다. Lock을 풀기 전 safe register를 읽어 chipset queue를 flush해야 device가 앞선 writel을 확정한 뒤 다음 CPU write를 받습니다.