DMA -1- (Basic)

<kernel v5.0>

DMA(Direct Memory Access) -1- (Basic)

디바이스로 부터 데이터를  메모리에 읽어들일 때 DMA를 사용하지 않는 방법과 DMA를 사용하는 방법을 보여준다.

  • non-DMA
    • CPU가 디바이스로부터 직접 데이터를 읽거나 기록하는 것을 반복하여 cpu load가 상승한다.
  • DMA
    • CPU는 디바이스에게 DMA 시작 신호와 DMA 완료 인터럽트만 수신하고, 직접적인 전송에는 참여하지 않으므로 cpu load를 최소화 시킨다. 이 과정에서 디바이스가 직접 메모리에 접근하여 읽거나 기록한다.
    • 버스 아키텍처에 DMA 컨트롤러 h/w가 구성되어 있어야 한다.
      • 예) PCI, PCIe, AXI, AHB, …

 

DMA 주소 체계

디바이스가 DMA 방식으로 접근할 메모리(Buffer)와 관련된 세 가지 주소 공간 유형을 알아본다.

  • CPU 가상 주소 공간
    • CPU가 사용하는 주소 공간
  • CPU 물리 주소 공간
    • CPU 가상 주소가 MMU에 의해 물리 주소로 변환하여 사용하는 주소 공간
  • 버스 주소 공간
    • 디바이스가 사용하는 주소 공간

 

다음 그림과 같이 CPU와 디바이스가 바라보는 주소 공간이 다름을 알 수 있다.

  • 64비트 시스템이더라도 전력의 소모를 줄이기 위해 물리 주소 공간의 크기를 보통 줄여 설계한다.
  • 버스 주소 공간의 크기도 여러 가지 버스(AXI, AMBA, ISA, PCI,…)가 존재하므로 사용하는 비트 크기는 각각 다르다.
  • 특정 디바이스들은 버스 주소의 일부 주소 공간에만 접근하게 제약하기도 한다.

 

아래 그림은 호스트가 소유한 메모리와 디바이스가 소유한 메모리의 관계를 3 가지 주소 공간을 통해 보여준다.

  • Case A) 디바이스 메모리
    • 디바이스가 소유한 메모리, 레지스터 또는 PCI BAR 영역이 호스트 브리지에 의해 매핑되어 사용된다.
    • 이 영역은 ioremap() 등의 API를 통해 매핑하여 사용한다.
    • 대부분 /proc/iomem을 통해 영역을 확인할 수 있다.
      • 별도로 영역 정보를 등록하지 않는 경우도 있다.
  • Case B) 호스트 CPU 메모리 – 디바이스가 IOMMU를 통해 접근
    • 호스트 CPU의 메모리 영역에 DMA 버퍼 메모리를 제공한다.
    • 디바이스가 사용하는 버스 주소 공간은 IOMMU를 통해 호스트 CPU의 물리 주소 공간에 접근할 수 있다.
  • Case C) 호스트 CPU 메모리 – 디바이스가 Direct 접근
    • 호스트 CPU의 메모리 영역에 DMA 버퍼 메모리를 제공한다.
    • 디바이스가 사용하는 버스 주소 공간과 호스트 CPU의 물리 주소 공간이 같은 경우 디바이스는 주소 변환을 위해 IOMMU를 사용할 필요가 없다. 따라서 디바이스는 Direct로 호스트 CPU의 물리 주소 공간에 위치한 DMA 버퍼에 접근할 수 있다.

 

DMA 주소 변환

IOMMU 매핑 vs Direct 매핑

디바이스는 다음과 같이 두 가지 매핑 방법으로 DMA 영역에 접근한다.

  • IOMMU 매핑
    • 디바이스가 사용하는 버스 주소를 IOMMU 장치를 통해 CPU의 물리 주소로 변환하는 경우 사용된다.
    • 참고로 IOVA(IO Virtualization)를 지원하는 IOMMU 장치도 있다.
  • Direct 매핑
    • 디바이스가 사용하는 버스 주소와 CPU의 물리 주소가 일치하여 변환 없이 사용할 수 있는 경우 사용된다.
    • legacy x86 시스템에서 디바이스들은 하위 16M 이하의 DMA 영역에 Direct 접근하였다.

 

다음 그림은 IOMMU 매핑과 Direct 매핑과의 구성 차이를 보여준다.

  • MMU
    • cpu 관점에서 cpu 가상 주소와 cpu 물리 주소의 변환을 수행한다.
  • IOMMU 또는 IOTLB
    • 디바이스 io 관점에서 버스 주소와 cpu 물리 주소의 변환을 수행한다.

 

DMA 주소 제한(Limitations)

물리 주소나 버스 주소가 64비트 주소로 표현 가능한 영역을 가진다고 할지라도,  다음과 같은 예와 같이 디바이스의 DMA 주소 지정이 제한되는 경우 가 있다.

  • 64bit PCIX 버스에 32bit PCI 디바이스를 사용하는 경우
  • legacy x86 시스템에서 ISA 버스를 통해 연결된 디바이스인 경우
    • 20bit(1M) 또는 24bit(16M) 주소만을 사용하여 DMA 접근 가능하다.

 

DMA 주소 제한을 설정할 때 대표적으로 다음 3 가지 API를 사용한다.

  • dma_set_mask()
    • 스트리밍 매핑을 사용하는 경우에 사용한다.
    • 예) 32bit(4G) 제한이 필요한 경우
      • mask=0xffff_ffff
      • 또는 DMA_BIT_MASK(32)
  • dma_set_coherent_mask()
    • consistent 매핑에서 사용한다.
    • coherent mask는 dma_set_mask()에 사용하는 제한과 같은 mask 값 또는 작은 mask 값을 사용할 수 있다.
  • dma_set_mask_and_coherent()
    • 위의 두 가지를 같은 mask 값으로 사용하는 구성이다.

 

SWIOTLB(SoftWare I/O TransLation Lookaside Buffer) & Bounce Buffer

  • hardware IOMMU(IOTLB)를 사용하지 못하는 플랫폼에서 fall-back 용도로 운영한다.
    • IOMMU(IOTLB)는 버스 주소와 cpu 물리 주소간의 주소 변환 매핑을 수행한다.
    • SWIOTLB는 io 가상 주소와 io 물리 주소간의 주소 변환 매핑이 되는 것처럼 보이도록 software 기법을 사용하여 두 영역간의 데이터를 복사하는 방법을 사용한다.
  • 디바이스가 좁은 범위의 주소 영역에만 접근할 수 있는 경우 사용된다.
  • 커널 부트업 과정중에 디바이스가 접근할 수 있는 범위안의 일정량의 Bounce Buffer를 할당받아 운용한다.
    • 커널 파라메터 예) “swiotlb=64K”
  • 디바이스가 DMA를 통해 bounce 버퍼에 기록하면 이를 swiotlb 드라이버가 디바이스의 버퍼에 복사하여 운영된다.
    • 바운스 버퍼를 DMA window로 사용한다.
  • software 방법으로 운영하기 때문에 성능이 저하되는 단점이 있다.
  • 이러한 Bounce 버퍼 운영을 통해 디바이스가 접근하지 못하는 highmem에 있는 유저 매핑 메모리에 복사하여 사용하는 사례도 있다.

 

다음 그림은 bounce buffer로 64K를 사용하여 디바이스가 DMA 전송을 수행하는 모습을 보여준다.

 

PCI DAC(Dual Address Cycle)

x86 PCI-X 디바이스는 64비트 주소를 사용한다. 특정 플랫폼에서 IOMMU가 32비트 이하의 주소만을 지원할 때 PCI-X 디바이스에 32비트 주소를 두 번 보내는 방법이 있다.

 

DMA용 버퍼 메모리

리눅스 커널은 여러 가지 메모리 할당자를 제공한다. DMA 버퍼 메모리를 위해서는 커널에서 제공하는 일부 할당자들만 사용이 가능하다.

 

DMA 메모리 할당 가능

  • __get_free_page()
    • 페이지 할당자(버디 시스템)으로 부터 2의 제곱승 단위의 페이지 할당
  • kmalloc()
    • 슬랩 할당자를 사용하여 작은 사이즈부터 할당 가능
  • kmem_cache_alloc()
    • 많은 양의 반복되는 슬랩 할당이 예상되는 경우 슬랩 캐시 할당

 

DMA 메모리 할당 불가능

  • 커널 이미지 주소
    • 컴파일 타임에 static하게 할당
    • 이 영역들은 ARM, ARM64의 경우 섹션 페이지 매핑되어 있다.
  • 모듈 이미지 주소
    • 모듈의 로드 타임에 할당
    • 이 영역들은 ARM, ARM64의 경우 섹션 페이지 매핑되어 있다.
  • 스택 주소
    • 현재 스택의 일부를 할당
  • vmalloc()
    • vmalloc(연속된 가상 주소 공간이지만 연속되지 않은 물리 주소 공간) 영역의 할당

 

Cache Coherent vs Non-Coherent

디바이스가 액세스할 수 있도록 물리적으로 연속된 메모리이다.

 

Cache Coherent
  • 캐시 효과에 대해 걱정할 필요없이 장치 또는 프로세서에서 기록한 내용이 또 다른 프로세서 또는 디바이스에 의해 언제든 읽을 수 있다.
  • 캐시를 사용하는 메모리 시스템에서 cpu, 캐시, 메모리(DRAM) 및 디바이스가 Cache Coherent Inter-connect 장치를 통해 연동된다.
    • 디바이스가 DMA를 수행하기 전/후로 hardware가 알아서 캐시의 invalidate 및 clean 등을 수행한다.
    • 예) ARM, ARM64에서 ACP(Accelerator Coherency Port)를 사용하는 디바이스의 경우 L1/L2 캐시와 h/w coherent 작용을 한다.
  • 캐시를 사용하지 않아도 될 정도로 빠른 메모리(SRAM)의 경우 캐시 없이 cpu, 메모리 및 디바이스가 버스와 연결된다.
    • SRAM 등의 고속 메모리는 DRAM에 비해 매우 cost가 높기 때문에 고속 네트웍 디바이스 전용의 버퍼 메모리로 종종 사용된다.
    • 리눅스 커널에서는 캐시를 사용하지 않도록 write-combine 매핑을 활용한다.
Non-Coherent
  • 디바이스 또는 프로세서가 기록한 데이터가 캐시 효과로 인해 양쪽에 동시에 업데이트된 데이터가 sync되지 않는다. 이의 sync 보정를 위해 software적으로 추가적인 조작이 필요한다.
  • cpu, 캐시, 메모리 및 디바이스 중 하나라도 Cache Coherent Inter-connect 장치에 연동하지 못한 경우이다.
    • 디바이스가 DMA를 수행하기 전/후로 software가 캐시의 invalidate 및 clean 등을 수행해야 한다. (그 외의 버퍼등)
    • 예) ARM에서 디바이스가 Cache Coherent Inter-connect 장치에 연동되지 않는 사례들이 많이 있다.

 

Consistent(coherent) DMA 매핑 vs Streaming DMA 매핑

리눅스 커널의 DMA 버퍼 메모리를 사용하기 전에 매핑하는 두 가지 방법에 대해 알아본다.

 

Consistent(coherent) DMA 매핑

DMA 요청 시 마다 매핑하지 않고, 보통 드라이버 초기화 시 한 번만 consistent(coherent) DMA 매핑을 수행한다. cpu와 디바이스가 동시에 접근이 가능한 상태이고, 명시적인 software flush 없이 서로 갱신된 값을 볼 수 있다. consistent(coherent) DMA 매핑을 위해 다음 두 가지의 메모리 할당 기법을 사용한다.

  • coherent 메모리 할당
  • contiguous 메모리 할당

 

coherent 메모리 할당

dma_alloc_coherent() API를 사용하여 아래 3 가지 중의 한 가지 방법으로 coherent 메모리를 할당한다.

 

1) generic coherent per-device memory
  • 시스템 메모리 영역의 DRAM은 사용할 수 없고, Reserved DMA 영역의으로 지정된 디바이스 전용 메모리를 사용한다.이들은 캐시를 사용하지 않고 write 버퍼만 사용가능한 write-combine 매핑을 사용한다.
    • Reserved DMA 영역 지정은 디바이스에 장착된 작은 사이즈 SRAM 등의 고속 메모리에 주로 사용된다.
      • 디바이스 트리의 경우 compatible = “shared-dma-pool”
      • 커널 cmdline 파라메터의 경우 “memmap=nn[KMG]$ss[KMG]”
  • 디바이스별로 비트맵을 통해 관리된다.
    • 비트맵의 1 비트는 1 페이지의 할당 여부를 관리한다.

 

2) direct DMA memory -fall-back
  • 디바이스가 IOMMU를 사용하지 않아 주소 변환 없이 시스템 메모리에 DMA 전송할 메모리이다.
  • CMA 영역에서 할당 받은 페이지 또는 일반 버디시스템에서 할당 받은 페이지를 사용한다.
    • CMA 영역 지정은 시스템 메모리 내에서 사용된다.
      • 디바이스 트리의 경우 compatible = “shared-dma-pool”을 지정하고 linux,cma-default 속성을 사용한다.
      • 커널 cmdline 파라메터의 경우 “cma=nn[MG]@[start[MG][-end[MG]]]”
  • 할당 받은 후 DMA coherent 디바이스가 아닌 경우 아키텍처가 지원하는 매핑 방법을 사용하여 다시 매핑한다.
    • ARM64의 경우 write-combine 매핑을 사용하고, 그 외의 경우 no-cache 매핑을 사용한다.

 

3) IOMMU memory – fall-back
  • 디바이스가 IOMMU를 사용하여 주소 변환 과정을 통해 시스템 메모리에 DMA 전송한다.
  • CMA 영역에서 할당 받은 페이지 또는 일반 버디시스템에서 할당 받은 페이지를 사용한다.
  • 할당 받은 후 IOMMU가 지원하는 매핑 방법을 사용하여 다시 매핑한다.

 

Contiguous 메모리 할당

  • fully coherent 하지는 않지만 Write Combine 매핑을 사용하여 시스템 메모리를 coherent 메모리를 구성한다.
    • Write Combine은 캐시는 사용하지는 않지만 write 버퍼는 사용할 수 있는 매핑이다.  따라서 디바이스가 메모리를 읽기 전에 write buffer를 비워야(clean) 한다.
  • CMA(Contiguous Memory Allocation) 영역이 지정된 시스템의 경우 interrupt context등에서 atomic 플래그 옵션으로 호출되는 경우가 아니면 우선 사용된다. 그 외의 경우 버디 시스템을 통해 일반 페이지 메모리를 사용한다.
  • 캐시를 사용하지 않는 시스템 메모리를 사용하므로 Fully Coherent 메모리에 비해 성능이 많이 저하되는 단점이 있다.
  • dma_alloc_wc() API를 통해 write-combine 매핑 타입의 메모리를 직접 할당 받을 수 있다.
  • 메모리 배리어가 필요한 경우도 있다.
    • week order를 수행하는 메모리인 경우 cpu가 디바이스에게 순차적인 접근을 사용해야 하는 경우 wmb()등의 배리어를 사용해야 한다.

 

다음 그림은 cpu가 write-combine 매핑된 coherent(contiguous) 메모리에 데이터를 기록하는 모습을 보여준다.

 

DMA 버퍼 할당 및 consistent(coherent) DMA 매핑
  • 큰 페이지 할당 및 매핑
    • dma_alloc_coherent() API를 사용하여 페이지 단위의 큰 DMA 버퍼를 할당하고 cosistent 매핑한다.
  • 작은 페이지 할당 및 매핑
    • dma_alloc_coherent() API를 사용하여 할당 받은 DMA 버퍼를 여러 개로 나누어 사용하거나,
    • 자주 반복되어 사용되는 작은 블럭 사이즈의 DMA 버퍼를 위해 dma_pool_create() API로 DMA pool을 생성하고, dma_pool_alloc() API로 블럭을 할당 받아 사용한다.

 

페이지 매핑 타입

다음은 페이지를 매핑할 때 사용하는 방법에 대해 잠시 기억해본다.

  • WC(Write Combine) 매핑
    • 캐시는 사용하지 않고 쓰기 버퍼만 사용 가능하다. 이 때 weekly(in) 오더 메모리도 가능하다.
  • WT(Write Through) 매핑
    • 캐시 hit되는 경우 캐시와 메모리를 동시에 기록한다. 캐시 hit하지 않는 경우는 곧바로 메모리에 기록하게 한다.
  • WB(Write Back) 매핑
    • 캐시를 사용하도록 매핑한다. (커널이 사용하는 일반 DRAM 영역)

 

2) Streaming DMA 매핑

디바이스가 DMA를 수행하기 전에 매핑(map)을 하고 DMA 수행한 후에 매핑 해제(unmap)를 한다. 이 매핑들은 interrupt context에서 사용될 수 있다.

  • constent DMA 매핑은 드라이버 로드 시 한 번만 수행하는 것과 다르게, Streaming DMA 매핑과 매핑 해제는 매번 DMA를 하기 전/후에 수행하여야 한다.

 

map & unmap

아키텍처 및 시스템 설계에 따라 cpu와 디바이스간에 공유된 메모리를 동시에 양쪽에서 업데이트를 하는 경우 서로 데이터가 싱크되지 않아 올바르게 보여지지 않을 수 있다. 따라서 cpu와 디바이스간의 데이터 일관성을 위해 DMA를 사용하기 전에 DMA 방향을 지정하고 DMA 전송 전/후로 다음 특별한 조작 등을 수행할 필요가 있다. 이러한 동작은 아키텍처마다 조금씩 다르다.

  • map
    • 디바이스가 메모리에 DMA 전송(read/write)을 하기 전에 처리해야 할 일을 수행한다.
    • 아키텍처 및 시스템에 따라 다양한 조작이 있을 수 있는데 그러한 조작은 다음과 같은 것들이 있다.
      • 캐시 조작(clean 또는 invalidate)
      • 버퍼 조작(flush)
      • IOMMU 사용 시 TLB 관련 매핑 레지스터 조작
      • SWIOTLB 사용 시 orginal DMA 버퍼와 디바이스가 실제 DMA 전송하는 Bounce 버퍼와의 데이터 복사(copy)
    • ARM64 캐시 조작 예)
      • coherent 메모리를 사용하는 경우 캐시 조작을 할 필요없다.
      • coherent 메모리를 사용하지 않는 경우 캐시 조작이 필요하다.
        • DMA_FROM_DEVICE 방향을 사용하는 경우 캐시를 invalidate 한다.
        • 그 외의 경우 캐시를 clean 한다.
  • unmap
    • 디바이스가 메모리에 DMA 전송(read/write)을 한 후에 처리해야 할 일을 수행한다.
    • ARM64 캐시 조작 예)
      • coherent 메모리를 사용하는 경우 캐시 조작을 할 필요없다.
      • coherent 메모리를 사용하지 않는 경우 DMA 방향에 따라 캐시 조작이 필요한 경우가 있다.
        • DMA_TO_DEVICE 방향을 사용하는 경우 아무런 동작을 하지 않는다.
        • 그 외의 경우 캐시를 invalidate 한다.

 

다음 그림은 ARM64 시스템에서 DMA 사용 전 후로 map/unmap 시 시스템 메모리의 캐시에 관여를 하는 모습을 보여준다.

 

DMA directions

다음과 같이 DMA 전송 시 데이터가 움직이는 방향을 지정할 수 있다.

  • DMA_BIDIRECTIONAL
    • 메인 메모리 및 디바이스 양방향
    • 정확한 방향을 모를 때 양방향 보낼 수 있도록 지정한다. 다만 단방향 지정보다 cost가 더 높다.
  • DMA_TO_DEVICE
    • 메인 메모리에서 디바이스 방향
  • DMA_FROM_DEVICE
    • 디바이스에서 메인 메모리 방향
  • DMA_NONE
    • 디버그 시 사용

 

single vs sg(scatter/gather)

스트리밍 매핑은 DMA 요청 시 마다(one-shot) 매핑과 매핑 해제를 수행하고, 다음과 같이 두 가지 버전을 지원한다.

  • single map/unmap
    • 물리적으로 연속된(contiguous) 하나의 영역을 대상으로 DMA 전송 시 사용
      • 예) large 영역으로 구성된 ring 버퍼
    • 제약: highmem 영역은 사용하지 못한다.
    • DMA 전송 시작 전에 dma_map_single()을 호출하고 DMA 전송이 완료되면 dma_unmap_single()을 호출한다.
  • scatter/gather map/unmap
    • 물리적으로 연속되지 않고 fragment된 여러 버퍼를 대상으로 한꺼번에 DMA 전송 시 사용
      • 물리적으로 연속된 대용량의 DMA 버퍼를 준비하지 않아도되는 장점이 있다.
    • scatterlist에서 지정한 개개의 메모리 영역은 물리적으로 연속된 메모리를 사용해야 하지만, 전체 DMA 버퍼가 연속되지 않고 fragment 되었다.
    • scatterlist 배열에 DMA 버퍼로 사용할 영역의 offset 및 길이 정보등을 담아 요청한다.
    • DMA 전송 시작 전에 dma_map_sg()를 호출하고 DMA 전송이 완료되면 dma_unmap_sg()를 호출한다.

 

다음 그림은 single DMA 스트리밍과 scatter/gather DMA 스트리밍의 모습을 보여준다.

 

다음 그림은 연속된(contiguous) single 메모리 영역을 대상으로 디바이스에서 메모리 방향의 스트리밍 DMA 매핑을 보여준다.

 

다음 그림은 fragment된 여러 개의 메모리 영역을 대상으로 디바이스에서 메모리 방향의 스트리밍 DMA 매핑을 보여준다.

 

DMA 관련 헤더 파일 구성

arm 아키텍처는 dma 영역 설정 및 dma 할당을 위해 arm 아키텍처 전용 코드가 많이 남아 있다. arm64 아키텍처는 일부에 전용 코드를 사용하고 대부분 디바이스 트리 및 generic 구현을 통해 구성된다. x86 아키텍처는 대부분 generic 코드로 구현되어 있다. 다음은 DMA 관련한 주요 구현 파일들이다.

  • 기본 파일
    • include/linux/dma-buf.h
    • include/linux/dma-contiguous.h
    • include/linux/dma-direct.h
    • include/linux/dma-fence.h
    • include/linux/dma-fence-array.h
    • include/linux/dma-iommu.h
    • include/linux/dma-noncoherent.h
    • include/linux/dmaengine.h
    • include/linux/dmapool.h
    • include/linux/dmar.h
    • include/linux/dma-mapping.h
    • include/linux/dma-direction.h
    • include/linux/scatterlist.h
    • mm/dmapool.c
    • kernel/dma/coherent.c
    • kernel/dma/contiguous.c
    • kernel/dma/direct.c
    • kernel/dma/mapping.c
    • kernel/dma/remap.c
  • Generic 헤더 파일
    • include/asm-generic/dma-mapping.h
    • include/asm-generic/dma-contiguous.h
    • include/asm-generic/dma.h
  • 아키텍처별 헤더 파일
    • arm
      • arch/arm/include/asm/dma-contiguous.h
      • arch/arm/include/asm/dma-iommu.h
      • arch/arm/include/asm/dma.h
      • arch/arm/include/asm/dma-direct.h
      • arch/arm/include/asm/dma-mapping.h
      • arch/arm/mm/dma-mapping.c
    • arm64
      • arch/arm/include/asm/dma-mapping.h
      • arch/arm/mm/dma-mapping.c
    • x86
      • arch/x86/include/asm/dma-mapping.h

 

DMA 메모리 할당/해제

DMA 메모리를 사용하기 위한 할당/해제 관리를 알아본다.

  • 디바이스 트리를 사용하면서 reserved-memory 영역을 대상으로 dma 또는 cma 영역으로 지정하여 관리할 수 있게 되었다.
    • default cma 영역을 의미하는 “linux,cma-default” 속성을 사용할 수 있다.
    • 잘 사용되지는 않지만 ARM 아키텍처에서는 default dma 영역을 의미하는 “linux,dma-default” 속성도 사용할 수 있다.
  • 디바이스 트리를 사용하지 않는 PC 서버의 경우도 ACPI를 통해 dma 또는 cma 영역을 지정하여 관리할 수 있다.
  • 그 외 디바이스 트리 및 ACPI를 사용하지 않는 경우 아키텍처 및 드라이버 specific한 custom 코드를 통해 수행될 수 있다.
  • 페이지 단위의 DMA coherent 메모리 할당을 원할때에는 dma_alloc_coherent()를 사용하고, 더 작은 단위의 할당을 반복하여 사용하고자 할 때에는 dma pool을 만든 후 dma_pool_alloc() 함수를 사용하여 할당 할 수 있다.
  • cma 영역을 dma 메모리로 사용하기 위해 내부에서 별도의 write-combine 매핑을 수행한다.

 

다음 그림은 전체적인 DMA 관리체계와 주요 API를 한 눈에 보여준다.

  • DMA가 버스와 연동하여 사용되지만 PCI 같은 버스 specific API를 사용하는 것보다 버스 독립적인 DMA-API를 사용해야 한다.
  • 예) pci_map_*() 보다 dma_map_*()을 사용한다.

 

Generic DMA Coherent per-device Memory 영역 선언

주로 성능이 빠른 SRAM 등의 디바이스 전용 메모리로 coherent 메모리 영역을 선언하는데 사용한다. 이렇게 선언된 메모리 영역은 디바이스가 dma 메모리의 할당/해제를 위해 비트맵으로 관리하며, 비트맵 1비트는 1개 페이지에 해당한다.

  • dma_declare_coherent_memory()
    • dmam_declare_coherent_memory() – Managed API
  • dma_release_declared_memory()
    • dmam_release_declared_memory() – Managed API
  • dma_mark_declared_memory_occupied()

 

메모리 페이지 할당/해제 API

DMA Coherent 메모리 페이지 할당/해제 API

위에서 선언한 coherent 메모리를 order 페이지 단위로 할당/해제한다. interrupt context에서 사용해야 하는 경우 GFP_ATOMIC gfp 플래그를 지정하여 사용할 수 있다.

  • dma_alloc_coherent()
    • dmam_alloc_coherent() – Managed API
  • dma_free_coherent()
    • dmam_free_coherent() – Managed API

 

DMA Contiguous 메모리 페이지 할당/해제 API

시스템 메모리(DRAM) 영역 중 CMA로 지정된 영역에서 order 페이지 단위로 할당/해제한다. CMA 영역이 없는 경우 일반 페이지 영역을 사용한다.

  • dma_alloc_wc()
  • dma_free_wc()

 

다양한 종류의 옵션 속성을 제공하는 DMA 메모리 페이지 할당/헤제 API

dma_alloc_coherent() 및 dma_alloc_wc() API를 사용할 수 없는 특수한 메모리 환경에서 옵션 속성을 주어 DMA 메모리 페이지 할당/해제를 수행할 수 있다.

  • dma_alloc_attrs()
    • dma_alloc_coherent()와 dma_alloc_wc()도 직접 호출한다.
  • dma_free_attrs()
    • dma_free_coherent()와 dma_free_wc()가 호출하는 함수이다.

 

DMA Attributes

메모리 할당 시 옵션 속성을 지정할 수 있다.

  • 무속성: fully coherent 메모리를 할당한다.
  • DMA_ATTR_WRITE_COMBINE
    • ARM과 ARM64 및 일부 mips(avr32) 아키텍처에서 제공된다.
    • dma_alloc_wc()에서 호출할 때 사용한다.
  • DMA_ATTR_WEAK_ORDERING
    • powerpc(cell), sparc 등의 아키텍처에서 read 및 write 들이 weakly order될 수 있다고 알려준다.
  • DMA_ATTR_NON_CONSISTENT
    • non-coherent 메모리를 사용해도 sync 포인트를 통해 적절히 DMA를 수행할 수 있게된다.
  • DMA_ATTR_WRITE_BARRIER
    • 지연된 DMA write가 완료될 때까지 기다린다.
    • 현재 ia64의 infiniband/core/umem.c에서 사용되고 있다.
  • DMA_ATTR_FORCE_CONTIGUOUS
    • ARM, ARM64의 특정 gpu 디바이스에서 반드시 물리 주소 및 가상 주소 모두 contiguous 메모리 매핑을 사용해야할 때 요청된다.
  • DMA_ATTR_ALLOC_SINGLE_PAGES
    • 1 페이지만 할당할 때 사용된다.
    • ARM에서만 지원하는 기능이다.

유저 매핑 속성.

  • DMA_ATTR_NO_KERNEL_MAPPING

기타

  • DMA_ATTR_SKIP_CPU_SYNC
    • DMA 전/후에 cpu에서 sync 작업을 할 필요가 없는 경우이다.
  • DMA_ATTR_NO_WARN
    • 메모리 할당 실패 경고를 출력하지 못하게 한다.
    • 현재 powerpc 에서만 적용되어 있다.
  • DMA_ATTR_PRIVILEGED
    • 유저 및 커널 레벨 양쪽에서 접근 가능하도록 요청한다.
    • arm의 pl330 DMA 컨트롤러 드라이버에서만 사용되고 있다.

 

DMA Coherent Memory Pool API

페이지 단위가 아닌 작은 용량의 dma coherent 메모리를 반복하여 사용하고자 할 때 DMA coherent 메모리 풀을 생성한 후 그 풀 내부에서 작은 메모리 할당을 할 필요성이 생겼다. 다음은 풀을 생성하고 해제하는데 사용하는 API이다.

  • dma_pool_create()
    • 이 함수 내부에서 dma_alloc_coherent() 함수를 호출하여 요청한 크기의 dma coherent pool을 생성한다.
    • dmam_pool_create() – Managed API
  • dma_pool_destroy()
    • dmam_pool_destroy() – Managed API

 

다음은 지정한 dma coherent pool에서 1개의 작은 블럭 메모리를 할당/해제하는 API 이다.

  • dma_pool_alloc()
  • dma_pool_free()

 

DMA Mask

DMA 영역 제한용 마스크를 설정하는 API 이다.

  • dma_set_mask()
  • dma_set_coherent_mask()
  • dma_set_mask_and_coherent()

 

DMA 유저스페이스 매핑

유저 스페이스에서 사용할 수 있는 매핑도 지원한다.

  • dma_mmap_attrs()
  • dma_mmap_coherent()
  • dma_mmap_writecombine()

 

DMA 스트리밍 매핑

map/unmap 매핑 API들은 내부에서 sync 함수를 동반한다.

 

single
  • single은 하나의 영역에 대한 요청이고,
page
  • page는 한 페이지에 대한 요청이다.
sg
  • sg는 fragment된 여러 영역에 대한 요청이다.

 

for_cpu

DMA 전에 sync가 필요한 경우

  • dma_sync_*_for_cpu() 함수를 사용할 수 있고,

 

for_device

DMA 후에 sync가 필요한 경우

  • dma_sync_*_for_device() 함수를 사용할 수 있다.

 

  • dma_map_single_attrs()
  • dma_unmap_single_attrs()
  • dma_map_single()
  • dma_unmap_single()
  • dma_map_page()
  • dma_unmap_page()
  • dma_map_sg_attrs()
  • dma_unmap_sg_attrs()
  • dma_map_sg()
  • dma_unmap_sg()
  • dma_mapping_error()
  • dma_sync_single_for_cpu()
  • dma_sync_single_for_device()
  • dma_sync_single_range_for_cpu()
  • dma_sync_single_range_for_device()
  • dma_sync_sg_for_cpu()
  • dma_sync_sg_for_device()

 

IOMMU

iommu 도메인을 할당하고 이를 이용하는 디바이스를 붙인 후 매핑을 지원하는 API이다.

  • iommu_domain_alloc()
  • iommu_domain_free()
  • iommu_attach_device()
  • iommu_detach_device()
  • iommu_map()
  • iommu_unmap()

 

참고

 

8 thoughts to “DMA -1- (Basic)”

  1. 임베디드 환경에서 DMA 사용시 캐쉬 컨트롤 관련 디버깅하는데 많은 도움이 되었습니다.
    감사합니다.

  2. 머릿속에 뭔가 정리가 안되어있을때 마다, 항상 감탄하여 글을 읽고 있습니다.
    정말 최곱니다!!

  3. 좋은 블로그덕분에 리눅스 이해도가 높아지네요 감사합니다.
    자주들어와서 좋은 정보 많이 얻어가고, 책도 덤으로 사겠습니다 🙂

    그리고 위에 SWIOTLB에서 Lookaside 가 빠진듯 합니다.
    IOMMU SWIOTLB : Input Output Memory Management Unit and the Software Input Output Translation Lookaside Buffer

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다