Estoy implementando un controlador de dispositivo Linux para un dispositivo de adquisición de datos que transmite datos constantemente a un búfer circular que asigné en el kernel (usando __get_free_pages() ). El búfer circular (en el que está escrito el hardware PCIe) reside en la RAM, y quiero que el espacio de usuario pueda mmmap () esa región de RAM para que el espacio de usuario pueda leer su contenido.
Según LDD3:
Una limitación interesante de remap_pfn_range es que da acceso solo a páginas reservadas y direcciones físicas por encima de la parte superior de la memoria física. ... Por lo tanto, remap_pfn_range no le permitirá reasignar direcciones convencionales, que incluyen las que obtiene llamando a get_free_page. ... La forma de asignar RAM real al espacio del usuario es usar
vm_ops->nopagepara tratar las fallas de página una a la vez.
En mi caso, sé exactamente qué direcciones deberán asignarse a las ubicaciones de VMA dadas para la totalidad del búfer en el momento en que se llama a mmap() , entonces, ¿por qué tengo que usar el enfoque nopage() de fallas en el páginas una a la vez a medida que se accede a ellas?
¿Por qué no puedo simplemente configurar mi VMA para que la totalidad de mi búfer circular se asigne inmediatamente al espacio de direcciones del usuario? ¿Hay alguna forma de hacer esto?
También espero que el programa de espacio de usuario acceda a mi búfer de forma secuencial, lo que resultará en un impacto en el rendimiento cuando se invoque mi función nopage() cada vez que se cruce un límite de página. ¿Causa esto un impacto considerable en el rendimiento en la práctica? (Mi búfer es grande, digamos 16 MB).
(En particular, he usado remap_pfn_range() en la memoria devuelta de __get_free_pages() en uno de mis controladores de dispositivo anteriores y nunca tuve ningún problema, pero es posible que haya tenido suerte con ese sistema).
Después de investigar un poco más, parece que la declaración de LDD3 está desactualizada, según un LWN (un poco más reciente):
TL; DR: En el pasado, los conductores pueden haber establecido manualmente PG_reserved en las páginas asignadas por kmalloc() / __get_free_pages() y posteriormente usaron remap_pfn_range() , pero ahora los conductores deberían usar vm_insert_page() para hacer lo equivalente.
vm_insert_page() solo funciona en asignaciones order-0 (una sola página), por lo que si desea asignar N páginas, deberá llamar a vm_insert_page() N veces.
Un ejemplo de este uso se puede ver en el controlador Firewire: drivers/firewire/core-iso.c
Tenga en cuenta cómo las páginas individuales se asignan llamando repetidamente a alloc_page() en fw_iso_buffer_alloc() , y estas páginas luego se asignan a un espacio de usuario VMA llamando repetidamente a vm_insert_page() en fw_iso_buffer_map_vma() . (fw_iso_buffer_map_vma() es llamado por el controlador mmap en drivers/firewire/core-cdev.c ).