Transmisión de DMA en el controlador de núcleo PCIe Linux
Pregunta
Estoy trabajando en el controlador FPGA para Linux Kernel. El código parece funcionar bien en x86, pero en x86_64 tengo algunos problemas. Implementé la transmisión de DMA. Entonces va como
get_user_pages(...);
for (...) {
sg_set_page();
}
pci_map_sg();
Pero pci_map_sg
direcciones devueltas como 0xbd285800
, que no están alineados por PAGE_SIZE
, así que no puedo enviar la primera página completa, porque la especificación PCIe dice
"Las solicitudes no deben especificar una combinación de dirección/longitud que cause un acceso de espacio de memoria para cruzar un límite de 4 kb".
¿Hay alguna forma de alinear las direcciones, o me perdí algo importante?
Solución
La primera posibilidad que viene a la mente es que el búfer de usuario que ingresa no comienza en un límite de página. Si su dirección de inicio es 0x800 bytes a través de una página, entonces la compensación en su primera sg_set_page
La llamada será 0x800. Esto producirá una dirección DMA que termina en 0x800. Esto es algo normal, y no un error.
Como pci_map_sg
Páginas de Couness, este primer segmento puede ser mayor que una página. Lo importante es que pci_map_sg
produce bloques contiguos de memoria direccionable de DMA, pero no produce una lista de transacciones PCIe de bajo nivel. En X64 es más probable que obtenga una región grande, porque la mayoría de las plataformas X64 tienen un Iommu.
Muchos dispositivos con los que trato tienen motores DMA que me permitan especificar una longitud de transferencia lógica de varios megabytes. Normalmente, la implementación de DMA en el punto final PCIe es responsable de iniciar una nueva transacción PCIe en cada límite de 4KB, y el programador puede ignorar esa restricción. Si los recursos en el FPGA están demasiado limitados para manejar eso, puede considerar escribir el código del controlador para convertir la lista de bloques de memoria Linux en una lista (mucho más larga) de transacciones PCIe.