2010-07-26 12 views
11

Estoy escribiendo un controlador de dispositivo en linux-2.6.26. Quiero tener un buffer dma mapeado en el espacio de usuario para enviar datos desde el controlador a la aplicación de espacio de usuario. Por favor sugiera un buen tutorial sobre esto.Asignación de búferes DMA al espacio de usuario

Gracias

+0

Compre LDD http://lwn.net/Kernel/LDD3/ y busque la implementación de mmap() en un controlador de dispositivo. – Dummy00001

+0

Tengo LDD ... ¿me puede decir el nombre de algunos controladores de dispositivo para buscar? –

+0

@ Dummy00001 ¿Qué pasa con el direccionamiento físico/virtual? –

Respuesta

5

Bueno, si usted tiene LDD, se puede echar un vistazo a el capítulo 15, y más precisamente la página 435, donde se describen las operaciones de E/S directa.

La llamada al kernel que lo ayudará a lograr esto es get_user_pages. En su caso, ya que desea enviar datos del kernel al espacio de usuario, debe establecer el indicador de escritura en 1.

Tenga en cuenta que las E/S asíncronas pueden permitirle obtener los mismos resultados pero con su aplicación de espacio de usuario no tener que esperar a que termine la lectura, que puede ser mejor.

+2

'get_user_pages' mapas en un búfer de espacio de usuario en el espacio del núcleo (¿verdad?) Pero cómo ¿uno de ellos vincula eso a una operación de DMA? Es decir, ¿activar un dispositivo DMA para escribir en la memoria a la que pueda acceder el espacio de usuario? (vea http://stackoverflow.com/q/5539375/119790) –

4

Echa un vistazo a los controladores de Infiniband. Se esfuerzan mucho para hacer que DMA y RDMA de copia cero trabajen en el espacio del usuario.

me olvidó añadir esto antes de grabar:

Haciendo DMA directamente a mapas de memoria de espacio de usuario está llena de problemas, por lo menos que tenga requisitos muy altos de rendimiento como Infiniband o Ethernet de 10 Gb, no lo haga . En su lugar, copie los datos DMA en los búferes del espacio de usuario. Te ahorrará mucho dolor.

Por solo un ejemplo, ¿qué ocurre si el programa del usuario finaliza antes de que se complete el DMA? ¿Qué pasa si la memoria del usuario se reasigna a otro proceso después de la salida pero el hardware todavía está configurado en DMA en esa página? ¡Desastre!

+0

¿Puede sugerir uno de los archivos Infiniband más fáciles? ¡Todos son bastante complejos! –

+0

La copia extra es una pérdida de tiempo en mi mente. Si deseamos arriesgar los problemas, ¿cuál es exactamente la solución? –

+3

'get_user_pages' pins las páginas. Por lo tanto, no importa si el programa finaliza antes de que se complete el DMA ... Las páginas no se pueden reutilizar hasta que haga el 'put_page' correspondiente para liberarlas. – Nemo

7

Bien, esto es lo que hice.

Descargo de responsabilidad: Soy un hacker en el puro sentido de la palabra y mi código no es el más bonito.

Leí LDD3 y el código fuente infiniband y otras cosas predecesoras y decidí que get_user_pages y fijarlos y todo ese otro rollo era demasiado doloroso para contemplar mientras hungover. Además, estaba trabajando con la otra persona en el bus PCIe y también fui responsable de "diseñar" la aplicación de espacio de usuario.

Escribí el controlador de modo que en el momento de la carga, preasigna tantos buffers como pueda con el tamaño más grande llamando a la función myAddr[i] = pci_alloc_consistent(blah,size,&pci_addr[i]) hasta que falla. (Error ->myAddr[i] es NULL Creo que, lo olvido). Pude asignar alrededor de 2.5GB de buffers, cada tamaño de 4MiB en mi pequeña máquina que solo tiene 4GiB de memoria. El número total de almacenamientos intermedios varía según el momento en que se carga el módulo kernel. Cargue el controlador en el momento del arranque y la mayoría de los búferes están asignados. El tamaño de cada búfer individual alcanza un máximo de 4MiB en mi sistema. No estoy seguro por qué. I cat ted /proc/buddyinfo para asegurarme de que no estaba haciendo nada estúpido, que es, por supuesto, mi patrón de partida habitual.

El controlador procede a dar la matriz de pci_addr al dispositivo PCIe junto con sus tamaños. El conductor se queda sentado allí esperando que comience la tormenta de interrupción. Mientras tanto, en el espacio de usuario, la aplicación abre el controlador, consulta el número de almacenamientos intermedios asignados (n) y sus tamaños (usando ioctl s o read s etc.) y luego llama al sistema llamando al mmap() varias (n) veces. Por supuesto, mmap() debe implementarse correctamente en el controlador y las páginas LDD3 422-423 son útiles.

El espacio de usuario ahora tiene n punteros a n áreas de la memoria del controlador. Cuando el dispositivo PCIe interrumpe el controlador, se le informa qué búferes están "llenos" o "disponibles" para que se sequen. La aplicación, a su vez, está pendiente en un read() o ioctl() para saber qué memorias intermedias están llenas de datos útiles.

La parte difícil fue gestionar el espacio de usuario para la sincronización del espacio del kernel de modo que los búferes que están siendo DMA en el PCIe no se modifiquen también por el espacio de usuario, pero para eso nos pagan. Espero que esto tenga sentido y estaría más que feliz de que me digan que soy un idiota, pero por favor díganme por qué.

También recomiendo este libro por cierto: http://www.amazon.com/Linux-Programming-Interface-System-Handbook/dp/1593272200. Ojalá tuviera ese libro hace siete años cuando escribí mi primer controlador de Linux.

Hay otro tipo de engaño posible al agregar aún más memoria y no dejar que el kernel lo use y mmap ping en ambos lados del espacio de usuario/división del espacio de núcleo, pero el dispositivo PCI también debe admitir direcciones DMA superiores a 32 bits. No lo he intentado pero no me sorprendería si eventualmente me veré obligado a hacerlo.

9

Aquí es lo que he utilizado, en resumen ...

get_user_pages de precisar la página (s) del usuario y le dará una serie de struct page * punteros.

dma_map_page en cada struct page * para obtener la dirección DMA (también conocida como "dirección de E/S") para la página. Esto también crea una asignación de IOMMU (si es necesario en su plataforma).

Ahora indique a su dispositivo que realice el DMA en la memoria usando esas direcciones DMA. Obviamente, pueden ser no contiguos; la memoria solo está garantizada para ser contigua en múltiplos del tamaño de la página.

dma_sync_single_for_cpu para hacer cualquier descarga de memoria caché necesaria o amortiguador de rebote blitting o lo que sea. Esta llamada garantiza que la CPU realmente puede ver el resultado del DMA, ya que en muchos sistemas, la modificación de la memoria RAM física detrás de la CPU da como resultado memorias caché obsoletas.

dma_unmap_page para liberar la asignación de IOMMU (si fue necesario en su plataforma).

put_page para anular las páginas del usuario (s).

Tenga en cuenta que debe comprobar errores hasta el final aquí, porque hay recursos limitados por todas partes. get_user_pages devuelve un número negativo para un error absoluto (-errno), pero puede devolver un número positivo para decirle cuántas páginas logró anclar (la memoria física no es ilimitada). Si es menor de lo que solicitó, debe recorrer todas las páginas en las que hizo el pin para llamar al put_page. (De lo contrario se filtra la memoria del núcleo;. Muy mal)

dma_map_page también puede devolver un error (-errno), debido a las asignaciones IOMMU son otro recurso limitado.

dma_unmap_page y put_page return void, como es habitual para las funciones de "liberación" de Linux. (Las rutinas de administración de recursos del kernel de Linux solo devuelven errores porque algo salió mal, no porque se equivocó y pasó un puntero malo o algo. La suposición básica es que usted está nunca atornillando porque este es el código del kernel.Aunque get_user_pages sí verifica para garantizar la validez de las direcciones de usuario y devolverá un error si el usuario le entregó un puntero incorrecto)

También puede considerar el uso de las funciones _sg si desea una interfaz amigable para dispersar/reunir. De allí tendría que llamar en lugar de dma_map_sgdma_map_page, en lugar de dma_sync_sg_for_cpudma_sync_single_for_cpu, etc.

También tenga en cuenta que muchas de estas funciones pueden ser más o menos no-ops en su plataforma, lo que a menudo puede salirse con ser descuidado . (En particular, dma_sync _... y dma_unmap _... no hacen nada en mi sistema x86_64.) Pero en esas plataformas, las llamadas mismas se compilan en nada, por lo que no hay excusa para ser descuidado.

+1

Recientemente me contrataron para implementar un controlador para el puerto SSI de un procesador iMX para un proyecto integrado y DMA no funcionó con estas funciones. El paquete BSP (Board Support Package) de Freescale tiene una serie de funciones (llamadas API) para su núcleo SDMA en el chip iMX51. Supongo que el OP está en hardware Intel x86 ... Linux es una bolsa mixta cuando sales del camino trillado. – Eric

2

La función remap_pfn_range (utilizada en el controlador de llamada mmap) se puede utilizar para asignar la memoria del kernel al espacio del usuario.

Un ejemplo real se puede encontrar en el controlador de caracteres de memoria drivers/char/mem.c.

Cuestiones relacionadas