요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
USB DMA
~~~~~~~
In Linux 2.5 kernels (and later), USB device drivers have additional control
over how DMA may be used to perform I/O operations. The APIs are detailed
in the kernel usb programming guide (kerneldoc, from the source code).
API overview
============
The big picture is that USB drivers can continue to ignore most DMA issues,
though they still must provide DMA-ready buffers (see
Documentation/core-api/dma-api-howto.rst). That's how they've worked through
the 2.4 (and earlier) kernels, or they can now be DMA-aware.
DMA-aware usb drivers:
- New calls enable DMA-aware drivers, letting them allocate dma buffers and
manage dma mappings for existing dma-ready buffers (see below).
- URBs have an additional "transfer_dma" field, as well as a transfer_flags
bit saying if it's valid. (Control requests also have "setup_dma", but
drivers must not use it.)
- "usbcore" will map this DMA address, if a DMA-aware driver didn't do
it first and set ``URB_NO_TRANSFER_DMA_MAP``. HCDs
don't manage dma mappings for URBs.
- There's a new "generic DMA API", parts of which are usable by USB device
drivers. Never use dma_set_mask() on any USB interface or device; that
would potentially break all devices sharing that bus.
Eliminating copies
==================
It's good to avoid making CPUs copy data needlessly. The costs can add up,
and effects like cache-trashing can impose subtle penalties.
- If you're doing lots of small data transfers from the same buffer all
the time, that can really burn up resources on systems which use an
IOMMU to manage the DMA mappings. It can cost MUCH more to set up and
tear down the IOMMU mappings with each request than perform the I/O!
For those specific cases, USB has primitives to allocate less expensive
memory. They work like kmalloc and kfree versions that give you the right
kind of addresses to store in urb->transfer_buffer and urb->transfer_dma.
You'd also set ``URB_NO_TRANSFER_DMA_MAP`` in urb->transfer_flags::
void *usb_alloc_coherent (struct usb_device *dev, size_t size,
int mem_flags, dma_addr_t *dma);
void usb_free_coherent (struct usb_device *dev, size_t size,
void *addr, dma_addr_t dma);
Most drivers should **NOT** be using these primitives; they don't need
to use this type of memory ("dma-coherent"), and memory returned from
:c:func:`kmalloc` will work just fine.
The memory buffer returned is "dma-coherent"; sometimes you might need to
force a consistent memory access ordering by using memory barriers. It's
not using a streaming DMA mapping, so it's good for small transfers on
systems where the I/O would otherwise thrash an IOMMU mapping. (See
Documentation/core-api/dma-api-howto.rst for definitions of "coherent" and
"streaming" DMA mappings.)
Asking for 1/Nth of a page (as well as asking for N pages) is reasonably
space-efficient.
On most systems the memory returned will be uncached, because the
semantics of dma-coherent memory require either bypassing CPU caches
or using cache hardware with bus-snooping support. While x86 hardware
has such bus-snooping, many other systems use software to flush cache
lines to prevent DMA conflicts.
- Devices on some EHCI controllers could handle DMA to/from high memory.
Unfortunately, the current Linux DMA infrastructure doesn't have a sane
way to expose these capabilities ... and in any case, HIGHMEM is mostly a
design wart specific to x86_32. So your best bet is to ensure you never
pass a highmem buffer into a USB driver. That's easy; it's the default
behavior. Just don't override it; e.g. with ``NETIF_F_HIGHDMA``.
This may force your callers to do some bounce buffering, copying from
high memory to "normal" DMA memory. If you can come up with a good way
to fix this issue (for x86_32 machines with over 1 GByte of memory),
feel free to submit patches.
Working with existing buffers
=============================
Existing buffers aren't usable for DMA without first being mapped into the
DMA address space of the device. However, most buffers passed to your
driver can safely be used with such DMA mapping. (See the first section
of Documentation/core-api/dma-api-howto.rst, titled "What memory is DMA-able?")
- When you have the scatterlists which have been mapped for the USB controller,
you could use the new ``usb_sg_*()`` calls, which would turn scatterlist
into URBs::
int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
unsigned pipe, unsigned period, struct scatterlist *sg,
int nents, size_t length, gfp_t mem_flags);
void usb_sg_wait(struct usb_sg_request *io);
void usb_sg_cancel(struct usb_sg_request *io);
When the USB controller doesn't support DMA, the ``usb_sg_init()`` would try
to submit URBs in PIO way as long as the page in scatterlists is not in the
Highmem, which could be very rare in modern architectures.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
USB DMA API 개요
1-31Linux 2.5 이후 USB device driver는 I/O operation에 DMA를 사용하는 방식을 더 세밀하게 제어할 수 있습니다. 상세 API는 source code에서 생성되는 kernel USB programming guide의 kerneldoc에 설명되어 있습니다.
큰 틀에서는 USB driver가 대부분의 DMA 문제를 계속 usbcore에 맡길 수 있지만 `Documentation/core-api/dma-api-howto.rst`에 설명된 DMA-ready buffer는 제공해야 합니다. 이는 Linux 2.4 이전 방식이며, 필요하면 driver가 DMA-aware하게 동작할 수도 있습니다.
DMA-aware driver는 새 API로 DMA buffer를 할당하고 기존 DMA-ready buffer의 mapping을 관리합니다. URB에는 `transfer_dma` field와 이 값의 유효성을 나타내는 `transfer_flags` bit가 있습니다. Control request에도 `setup_dma`가 있지만 driver가 사용하면 안 됩니다.
DMA-aware driver가 먼저 mapping하고 `URB_NO_TRANSFER_DMA_MAP`을 설정하지 않았다면 usbcore가 DMA address를 mapping합니다. HCD는 URB의 DMA mapping을 관리하지 않습니다.
Generic DMA API 일부를 USB device driver에서 사용할 수 있지만 어떤 USB interface나 device에도 `dma_set_mask()`를 호출하면 안 됩니다. 같은 bus를 공유하는 모든 device를 망가뜨릴 수 있습니다.
USB DMA
~~~~~~~
In Linux 2.5 kernels (and later), USB device drivers have additional control
over how DMA may be used to perform I/O operations. The APIs are detailed
in the kernel usb programming guide (kerneldoc, from the source code).
API overview
============
The big picture is that USB drivers can continue to ignore most DMA issues,
though they still must provide DMA-ready buffers (see
Documentation/core-api/dma-api-howto.rst). That's how they've worked through
the 2.4 (and earlier) kernels, or they can now be DMA-aware.
DMA-aware usb drivers:
- New calls enable DMA-aware drivers, letting them allocate dma buffers and
manage dma mappings for existing dma-ready buffers (see below).
- URBs have an additional "transfer_dma" field, as well as a transfer_flags
bit saying if it's valid. (Control requests also have "setup_dma", but
drivers must not use it.)
- "usbcore" will map this DMA address, if a DMA-aware driver didn't do
it first and set ``URB_NO_TRANSFER_DMA_MAP``. HCDs
don't manage dma mappings for URBs.
- There's a new "generic DMA API", parts of which are usable by USB device
drivers. Never use dma_set_mask() on any USB interface or device; that
would potentially break all devices sharing that bus.
Copy 제거와 coherent buffer
32-67CPU가 불필요하게 data를 copy하지 않도록 하는 것이 좋습니다. 누적 비용뿐 아니라 cache thrashing 같은 미묘한 penalty도 생길 수 있습니다.
같은 buffer로 작은 transfer를 매우 자주 수행하면 IOMMU가 DMA mapping을 관리하는 system에서 resource 소모가 큽니다. 요청마다 IOMMU mapping을 설정·해제하는 비용이 실제 I/O보다 훨씬 클 수 있습니다.
이 경우 USB는 `usb_alloc_coherent()`와 `usb_free_coherent()`로 더 저렴한 memory를 할당하는 primitive를 제공합니다. `kmalloc`·`kfree`와 비슷하지만 `urb->transfer_buffer`와 `urb->transfer_dma`에 넣을 올바른 종류의 address를 제공하며 `urb->transfer_flags`에는 `URB_NO_TRANSFER_DMA_MAP`을 설정합니다.
대부분의 driver는 이 primitive를 사용하면 안 됩니다. 보통 dma-coherent memory가 필요 없고 `kmalloc()`이 반환한 memory로 충분합니다.
반환 buffer는 dma-coherent이며 일관된 memory access ordering을 강제하려면 memory barrier가 필요할 수 있습니다. Streaming DMA mapping을 사용하지 않으므로 IOMMU mapping을 반복적으로 바꿀 작은 transfer에 적합합니다. Coherent와 streaming mapping의 정의는 `Documentation/core-api/dma-api-howto.rst`를 참조합니다.
Page의 `1/N` 크기나 N page를 요청하는 방식 모두 공간 효율이 합리적입니다.
반복 mapping 비용이 실제 I/O보다 큰 특수한 작은 transfer에만 사용합니다.
Eliminating copies
==================
It's good to avoid making CPUs copy data needlessly. The costs can add up,
and effects like cache-trashing can impose subtle penalties.
- If you're doing lots of small data transfers from the same buffer all
the time, that can really burn up resources on systems which use an
IOMMU to manage the DMA mappings. It can cost MUCH more to set up and
tear down the IOMMU mappings with each request than perform the I/O!
For those specific cases, USB has primitives to allocate less expensive
memory. They work like kmalloc and kfree versions that give you the right
kind of addresses to store in urb->transfer_buffer and urb->transfer_dma.
You'd also set ``URB_NO_TRANSFER_DMA_MAP`` in urb->transfer_flags::
void *usb_alloc_coherent (struct usb_device *dev, size_t size,
int mem_flags, dma_addr_t *dma);
void usb_free_coherent (struct usb_device *dev, size_t size,
void *addr, dma_addr_t dma);
Most drivers should **NOT** be using these primitives; they don't need
to use this type of memory ("dma-coherent"), and memory returned from
:c:func:`kmalloc` will work just fine.
The memory buffer returned is "dma-coherent"; sometimes you might need to
force a consistent memory access ordering by using memory barriers. It's
not using a streaming DMA mapping, so it's good for small transfers on
systems where the I/O would otherwise thrash an IOMMU mapping. (See
Documentation/core-api/dma-api-howto.rst for definitions of "coherent" and
"streaming" DMA mappings.)
Asking for 1/Nth of a page (as well as asking for N pages) is reasonably
space-efficient.
Cache semantics와 HIGHMEM
68-87대부분의 system에서 dma-coherent memory는 uncached입니다. Coherent semantics를 위해 CPU cache를 bypass하거나 bus snooping을 지원하는 cache hardware가 필요하기 때문입니다. x86은 bus snooping을 지원하지만 많은 다른 system은 DMA conflict를 막기 위해 software로 cache line을 flush합니다.
일부 EHCI controller의 device는 high memory와 DMA를 주고받을 수 있지만 현재 Linux DMA infrastructure는 이 capability를 합리적으로 노출하지 못합니다. HIGHMEM은 주로 x86_32에 특화된 설계상 문제이므로 USB driver에 highmem buffer를 전달하지 않는 것이 가장 안전하며 이것이 기본 동작입니다. `NETIF_F_HIGHDMA` 등으로 기본값을 덮어쓰면 안 됩니다.
이 제한 때문에 caller가 high memory에서 일반 DMA memory로 copy하는 bounce buffering을 수행해야 할 수 있습니다. x86_32에서 1 GiB가 넘는 memory를 위한 해결책이 있다면 patch 제출을 권장합니다.
On most systems the memory returned will be uncached, because the
semantics of dma-coherent memory require either bypassing CPU caches
or using cache hardware with bus-snooping support. While x86 hardware
has such bus-snooping, many other systems use software to flush cache
lines to prevent DMA conflicts.
- Devices on some EHCI controllers could handle DMA to/from high memory.
Unfortunately, the current Linux DMA infrastructure doesn't have a sane
way to expose these capabilities ... and in any case, HIGHMEM is mostly a
design wart specific to x86_32. So your best bet is to ensure you never
pass a highmem buffer into a USB driver. That's easy; it's the default
behavior. Just don't override it; e.g. with ``NETIF_F_HIGHDMA``.
This may force your callers to do some bounce buffering, copying from
high memory to "normal" DMA memory. If you can come up with a good way
to fix this issue (for x86_32 machines with over 1 GByte of memory),
feel free to submit patches.
기존 buffer와 scatterlist
88-110기존 buffer는 먼저 device의 DMA address space에 mapping하지 않으면 DMA에 사용할 수 없습니다. 다만 driver에 전달되는 대부분의 buffer는 이런 DMA mapping에 안전하게 사용할 수 있습니다. DMA 가능한 memory 조건은 `Documentation/core-api/dma-api-howto.rst`의 `What memory is DMA-able?` 절을 참조합니다.
USB controller용으로 mapping된 scatterlist가 있으면 `usb_sg_init()`, `usb_sg_wait()`, `usb_sg_cancel()` 계열을 사용해 scatterlist를 URB로 변환할 수 있습니다.
USB controller가 DMA를 지원하지 않을 때도 scatterlist의 page가 Highmem이 아니면 `usb_sg_init()`은 PIO 방식으로 URB 제출을 시도합니다. 현대 architecture에서는 이런 상황이 드뭅니다.
Working with existing buffers
=============================
Existing buffers aren't usable for DMA without first being mapped into the
DMA address space of the device. However, most buffers passed to your
driver can safely be used with such DMA mapping. (See the first section
of Documentation/core-api/dma-api-howto.rst, titled "What memory is DMA-able?")
- When you have the scatterlists which have been mapped for the USB controller,
you could use the new ``usb_sg_*()`` calls, which would turn scatterlist
into URBs::
int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
unsigned pipe, unsigned period, struct scatterlist *sg,
int nents, size_t length, gfp_t mem_flags);
void usb_sg_wait(struct usb_sg_request *io);
void usb_sg_cancel(struct usb_sg_request *io);
When the USB controller doesn't support DMA, the ``usb_sg_init()`` would try
to submit URBs in PIO way as long as the page in scatterlists is not in the
Highmem, which could be very rare in modern architectures.
요약·해설
dma.rst:1-110USB driver는 DMA-ready buffer를 제공하되 mapping을 usbcore에 맡기거나 직접 관리할 수 있습니다. Coherent allocation은 반복 mapping 비용이 큰 특수 사례에만 사용하고 일반 buffer와 scatterlist는 DMA API 규칙에 따라 mapping해야 합니다.