GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo asigno un búfer DMA respaldado por 1GB HugePages en un módulo del kernel de Linux?

PROBLEMA

  1. Normalmente, si desea asignar un búfer DMA u obtener una dirección física, esto se hace en el espacio del kernel, ya que el código de usuario nunca debería tener que jugar con las direcciones físicas.
  2. Hugetlbfs solo proporciona asignaciones de espacio de usuario para asignar páginas enormes de 1 GB y obtener direcciones virtuales de espacio de usuario
  3. No existe ninguna función para asignar una dirección virtual de página enorme de usuario a una dirección física

EUREKA

¡Pero la función existe! Enterrada profundamente en el código fuente del kernel 2.6 se encuentra esta función para obtener una página de estructura desde una dirección virtual, marcada como "solo para prueba" y bloqueada con #if 0:

#if 0   /* This is just for testing */
struct page *
follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
{
    unsigned long start = address;
    int length = 1;
    int nr;
    struct page *page;
    struct vm_area_struct *vma;

    vma = find_vma(mm, addr);
    if (!vma || !is_vm_hugetlb_page(vma))
        return ERR_PTR(-EINVAL);

    pte = huge_pte_offset(mm, address);

    /* hugetlb should be locked, and hence, prefaulted */
    WARN_ON(!pte || pte_none(*pte));

    page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];

    WARN_ON(!PageHead(page));

    return page;
}

SOLUCIÓN:dado que la función anterior no está realmente compilada en el kernel, deberá agregarla a la fuente de su controlador.

FLUJO DE TRABAJO DEL LADO DEL USUARIO

  1. Asigne enormes páginas de 1 gb en el arranque con las opciones de arranque del kernel
  2. Llamar a get_huge_pages() con hugetlbfs para obtener un puntero de espacio de usuario (dirección virtual)
  3. Pasar la dirección virtual del usuario (el puntero normal se convierte en unsigned long) al controlador ioctl

FLUJO DE TRABAJO DEL CONTROLADOR DE KERNEL

  1. Aceptar la dirección virtual del usuario a través de ioctl
  2. Llama a follow_huge_addr para obtener la página de estructura*
  3. Llame a page_to_phys en la página de estructura* para obtener la dirección física
  4. Proporcione la dirección física al dispositivo para DMA
  5. Llame a kmap en la página de estructura* si también desea un puntero virtual del núcleo

DESCARGO DE RESPONSABILIDAD

  • Los pasos anteriores se están recordando varios años después. He perdido el acceso al código fuente original. Haga su debida diligencia y asegúrese de que no me estoy olvidando de un paso.
  • La única razón por la que esto funciona es porque se asignan páginas enormes de 1 GB en el momento del arranque y sus direcciones físicas están bloqueadas de forma permanente. ¡No intente mapear una dirección virtual de usuario respaldada por una página enorme que no sea de 1 GB en una dirección física DMA! ¡Lo vas a pasar mal!
  • Pruebe cuidadosamente en su sistema para confirmar que sus páginas enormes de 1GB están bloqueadas en la memoria física y que todo funciona exactamente. Este código funcionó perfectamente en mi configuración, pero aquí hay un gran peligro si algo sale mal.
  • Solo se garantiza que este código funcione en la arquitectura x86/x64 (donde la dirección física ==dirección del bus) y en la versión del kernel 2.6.XX. Puede haber una manera más fácil de hacer esto en versiones posteriores del kernel, o puede ser completamente imposible ahora.

Esto no se hace comúnmente en el espacio del núcleo, por lo que no hay demasiados ejemplos.

Al igual que cualquier otra página, las páginas grandes se asignan con alloc_pages, con la melodía:

struct page *p = alloc_pages(GFP_TRANSHUGE, HPAGE_PMD_ORDER);

HPAGE_PMD_ORDER es una macro que define un orden de una sola página enorme en términos de páginas normales. Lo anterior implica que las páginas grandes transparentes están habilitadas en el kernel.

Luego puede proceder a mapear el puntero de página obtenido de la manera habitual con kmap().

Descargo de responsabilidad:nunca lo probé yo mismo, por lo que es posible que deba experimentar un poco. Una cosa para verificar es esto:HPAGE_PMD_SHIFT representa un pedido de una página "enorme" más pequeña. Si desea utilizar esas páginas gigantes de 1 GB, probablemente deba probar un orden diferente, probablemente PUD_SHIFT - PAGE_SHIFT.


Linux
  1. Linux:¿cómo determinar qué módulo contamina el kernel?

  2. Linux:¿cómo recargar correctamente un módulo del kernel?

  3. ¿Cómo asignar un búfer del kernel de Linux al espacio del usuario?

  4. ¿Cómo codificar un módulo del kernel de Linux?

  5. ¿Cómo pasar un valor a un módulo kernel de Linux incorporado en el momento del arranque?

Cómo cargar y descargar módulos del kernel en Linux

Comando Modprobe en Linux

Cómo encontrar la dirección IP en Linux

Cómo instalar el módulo 'Python-Pexpect' en Linux

Cómo cambiar la dirección MAC en Linux

Cómo cambiar la dirección IP en Linux