wandres.dev
DMA E IOMMU · transferencias a dispositivos

Qué es DMA: el dispositivo lee la RAM sin la CPU

Por qué la E/S moderna no pasa byte a byte por la CPU sino que el dispositivo accede a la RAM por sí mismo como maestro del bus, y por qué para ello necesita direcciones de bus (dma_addr_t) y no las direcciones virtuales del kernel.

⏱ 13 min

Un SSD NVMe de 2026 mueve más de diez gigabytes por segundo. Si la CPU tuviera que copiar cada palabra desde un registro del dispositivo a la RAM con un bucle de instrucciones de E/S, no le quedaría un solo ciclo para ejecutar programas. DMA es la respuesta: el dispositivo se convierte en maestro del bus y escribe la memoria él solo. Pero para lograrlo necesita ver la RAM con sus propios ojos —direcciones de bus—, que no son las direcciones virtuales del kernel.

🎯 Al terminar esta lección sabrás
  • Distinguir la E/S programada (PIO) del DMA y por qué existe el DMA.
  • Entender qué significa que un dispositivo sea maestro del bus.
  • Ver por qué un dispositivo no puede usar las direcciones virtuales del kernel.
  • Conocer dma_addr_t como la dirección de bus opaca que ve el dispositivo.

De la E/S programada al DMA

En la E/S programada (PIO) la CPU es la que mueve cada byte: ejecuta una instrucción de lectura o escritura por cada palabra que cruza entre el dispositivo y la memoria. Es correcto, pero letal para el rendimiento: un núcleo entero se consume copiando datos que nunca mira.

/* PIO: la CPU ejecuta un acceso por cada palabra transferida */
static void leer_pio(struct mi_dev *d, u32 *buf, size_t n)
{
	size_t i;
	for (i = 0; i < n; i++)
		buf[i] = readl(d->regs + REG_DATA);  /* un acceso de CPU por palabra */
}

Con DMA (Direct Memory Access) la CPU delega. Le dice al dispositivo dónde está el buffer y cuánto transferir, arranca la operación y se marcha a hacer otra cosa. El motor de DMA del propio dispositivo bombea los datos hacia la RAM y, al terminar, levanta una interrupción. La CPU solo trabaja al principio y al final; el trasiego de bytes ocurre en paralelo con el resto del sistema.

/* DMA: la CPU solo arranca; el dispositivo hace el resto y avisa al terminar */
static void arrancar_dma(struct mi_dev *d, dma_addr_t dst, size_t len)
{
	writel(lower_32_bits(dst), d->regs + REG_DMA_ADDR_LO);
	writel(upper_32_bits(dst), d->regs + REG_DMA_ADDR_HI);
	writel(len,                d->regs + REG_DMA_LEN);
	writel(DMA_GO,             d->regs + REG_DMA_CTRL);  /* y la CPU queda libre */
}
flowchart LR
subgraph PIO
  C1[CPU] -->|copia cada palabra| R1[RAM]
  D1[Dispositivo] -->|registro de datos| C1
end
subgraph DMA
  C2[CPU] -->|programa direccion y longitud| D2[Dispositivo]
  D2 -->|transfiere solo| R2[RAM]
  D2 -. interrupcion al terminar .- C2
end

El dispositivo como maestro del bus

En PCI Express, un dispositivo puede iniciar transacciones de memoria en la malla del bus, igual que hace la CPU. A eso se le llama ser maestro del bus (bus master). El dispositivo emite lecturas y escrituras que llegan al controlador de memoria sin que la CPU intervenga. Deja de existir un único iniciador: la CPU es solo uno más entre varios.

/* habilitar al dispositivo PCI para que inicie transacciones de memoria */
pci_set_master(pdev);   /* activa el bit Bus Master en el registro de comando PCI */

Históricamente el motor de DMA era un chip central (el 8237 de la era ISA) que otros dispositivos pedían prestado; hoy cada dispositivo serio lleva su propio motor —first-party DMA— y programa sus transferencias mediante descriptores en memoria. El resultado es el mismo: alguien que no es la CPU escribe en tu RAM.

🐢

E/S programada

La CPU ejecuta un acceso por palabra. Simple y sin direcciones de bus, pero satura un núcleo entero. Solo válida para dispositivos lentos o registros de control.

🚀

DMA

El dispositivo transfiere solo mientras la CPU trabaja en otra cosa. Escala a gigabytes por segundo, pero exige direcciones de bus, coherencia de caché y protección.

Por qué no valen las direcciones virtuales del kernel

Aquí está el corazón de todo el nivel. El puntero que devuelve kmalloc es una dirección virtual del kernel, y solo tiene sentido tras pasar por la MMU de la CPU y sus tablas de página. El dispositivo no atraviesa la MMU de la CPU: pone una dirección cruda en el bus que el controlador de memoria —o un IOMMU— interpreta. Esa dirección es una dirección de bus o dirección de DMA, y puede no coincidir con la física.

void *va = kmalloc(len, GFP_KERNEL);   /* direccion VIRTUAL del kernel */
writel(virt_to_phys(va), d->regs + REG_DMA_ADDR_LO);  /* MAL: ni virtual ni fisica sirven */

Entregar virt_to_phys(va) al dispositivo parece razonable en un x86 sencillo, donde la dirección de bus suele igualar a la física. Pero es incorrecto y no portable: ignora los desplazamientos de bus de muchos SoC, la traducción de un IOMMU y los buffers de rebote (nivel 27.3). La forma correcta de obtener la dirección que el dispositivo debe usar es la DMA API (nivel 27.2), nunca virt_to_phys.

dma_addr_t: la dirección que ve el dispositivo

El tipo dma_addr_t representa una dirección de bus. Es opaco: lo guardas, lo programas en los registros o descriptores del dispositivo, y jamás lo desreferencias desde la CPU (no es un puntero válido para el núcleo). Su anchura la fija la plataforma con CONFIG_ARCH_DMA_ADDR_T_64BIT; un dispositivo de 32 bits solo podrá recibir valores por debajo de los 4 GiB, un límite que se declara con la máscara de DMA (nivel 27.5).

En la práctica, un dispositivo rápido no recibe una sola dma_addr_t por transferencia sino que lee un anillo de descriptores en memoria: cada descriptor lleva una dirección de bus, una longitud y unas banderas, y el dispositivo los consume en orden. La CPU rellena descriptores y toca un registro “timbre” (doorbell); el dispositivo transfiere y marca cada descriptor como completado, levantando una interrupción cada tantos para amortizar su coste. Ese patrón —anillo más timbre más interrupción— es el esqueleto de casi todo driver de red o almacenamiento, y lo construirás con la DMA API en el nivel 27.2.

💡
La otra mitad del DMA es la interrupción

El DMA solo es la mitad del baile. Cuando el dispositivo termina de escribir la RAM, la CPU necesita enterarse: ahí entra la interrupción de fin de transferencia, que despierta al driver para procesar los datos y reciclar los buffers. Los niveles de dispositivos (34, interrupciones a fondo) tratan esa cara; aquí basta con retener que DMA e interrupciones son las dos manos de la misma técnica: el dispositivo trabaja en paralelo y avisa al acabar.

ℹ️
dmaengine no es la DMA API

Cuidado con el nombre. El subsistema dmaengine (drivers/dma/) gestiona controladores de DMA esclavos, típicos en SoC embebidos, que copian entre periféricos y memoria bajo petición. La DMA API (dma_map_*, dma_alloc_coherent) es otra cosa: describe cómo dar a cualquier dispositivo una vista válida de la memoria. Este nivel trata la segunda; casi todo driver de PCI o de red la usa.

DMA es la prueba de que la CPU no es el centro del mundo

Detente en la inversión que acabas de cruzar, porque reordena todo lo que viene después. Durante los primeros veintiséis niveles la CPU fue el sujeto de cada frase: la CPU lee, la CPU escribe, la CPU protege. DMA rompe ese privilegio. El bus de memoria no es una carretera privada de la CPU sino una malla compartida con varios iniciadores, y el dispositivo es un igual con acceso directo a tu RAM. De esa única verdad brotan, en cascada, los tres problemas del resto del nivel. Primero, las direcciones: un iniciador que no pasa por la MMU de la CPU necesita su propio sistema de direcciones, las de bus. Segundo, la coherencia: si el dispositivo escribe RAM sin avisar a las cachés de la CPU, ¿quién garantiza que ambos vean lo mismo? Tercero, la protección: si cualquier dispositivo puede escribir cualquier dirección física, un periférico malicioso puede leerte las claves de cifrado de la memoria, y hace falta un guardián —el IOMMU— para confinarlo. Coherencia de caché, direcciones de bus e IOMMU no son tres temas sueltos: son las tres consecuencias inevitables de haber admitido a un segundo actor con acceso directo a la memoria. Interioriza el DMA como una descentralización del poder sobre la RAM y el resto del nivel dejará de ser una lista de APIs para convertirse en la respuesta obvia a “¿y ahora cómo controlo a este nuevo actor?”.

⚔️ Distingue quién mueve los bytes
  1. Abre un driver real (por ejemplo drivers/net/ethernet/intel/igc/) y localiza dónde llama a pci_set_master.
  2. Encuentra un registro donde el driver escriba una dma_addr_t y explica por qué ahí no aparece un puntero del kernel.
  3. Argumenta, en tres líneas, por qué virt_to_phys seguido de programarlo en el dispositivo funcionaría en tu portátil x86 pero rompería en un SoC ARM con desplazamiento de bus.
  4. Estima cuánta CPU costaría transferir 10 GB/s por PIO frente a DMA, y por qué el DMA es lo único que hace viable el almacenamiento y la red modernos.