GNU/Linux >> Tutoriels Linux >  >> Linux

Comment allouer un tampon DMA soutenu par HugePages de 1 Go dans un module du noyau Linux ?

PROBLÈME

  1. Normalement, si vous souhaitez allouer un tampon DMA ou obtenir une adresse physique, cela se fait dans l'espace du noyau, car le code utilisateur ne devrait jamais avoir à jouer avec les adresses physiques.
  2. Hugetlbfs fournit uniquement des mappages d'espace utilisateur pour allouer des pages volumineuses de 1 Go et obtenir des adresses virtuelles d'espace utilisateur
  3. Aucune fonction n'existe pour mapper une adresse virtuelle énorme page d'utilisateur à une adresse physique

EUREKA

Mais la fonction existe ! Enfouie profondément dans le code source du noyau 2.6 se trouve cette fonction pour obtenir une page de structure à partir d'une adresse virtuelle, marquée comme "juste pour tester" et bloquée avec #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;
}

SOLUTION :étant donné que la fonction ci-dessus n'est pas réellement compilée dans le noyau, vous devrez l'ajouter à la source de votre pilote.

FLUX DE TRAVAIL CÔTÉ UTILISATEUR

  1. Allouer 1 Go de pages énormes au démarrage avec les options de démarrage du noyau
  2. Appelez get_huge_pages() avec Hugetlbfs pour obtenir le pointeur de l'espace utilisateur (adresse virtuelle)
  3. Transmettre l'adresse virtuelle de l'utilisateur (pointeur normal converti en long non signé) au pilote ioctl

FLUX DE TRAVAIL DU PILOTE DU NOYAU

  1. Accepter l'adresse virtuelle de l'utilisateur via ioctl
  2. Appelez follow_huge_addr pour obtenir la page de structure*
  3. Appelez page_to_phys sur la page de structure* pour obtenir l'adresse physique
  4. Fournir une adresse physique à l'appareil pour le DMA
  5. Appelez kmap sur la page struct* si vous voulez également un pointeur virtuel du noyau

AVIS DE NON-RESPONSABILITÉ

  • Les étapes ci-dessus sont rappelées plusieurs années plus tard. J'ai perdu l'accès au code source d'origine. Faites preuve de diligence raisonnable et assurez-vous que je n'oublie aucune étape.
  • La seule raison pour laquelle cela fonctionne est que 1 Go d'énormes pages sont allouées au démarrage et que leurs adresses physiques sont définitivement verrouillées. N'essayez pas de mapper une adresse virtuelle d'utilisateur non basée sur une énorme page de 1 Go dans une adresse physique DMA ! Vous allez passer un mauvais moment !
  • Testez soigneusement votre système pour confirmer que vos énormes pages de 1 Go sont bien verrouillées dans la mémoire physique et que tout fonctionne correctement. Ce code a parfaitement fonctionné sur ma configuration, mais il y a un grand danger ici si quelque chose ne va pas.
  • Ce code est uniquement garanti pour fonctionner sur l'architecture x86/x64 (où l'adresse physique ==l'adresse du bus) et sur la version 2.6.XX du noyau. Il peut y avoir un moyen plus simple de le faire sur les versions ultérieures du noyau, ou cela peut être complètement impossible maintenant.

Ce n'est pas courant dans l'espace du noyau, donc pas trop d'exemples.

Comme toute autre page, les pages volumineuses sont allouées avec alloc_pages, à raison :

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

HPAGE_PMD_ORDER est une macro, définissant un ordre d'une seule grande page en termes de pages normales. Ce qui précède implique que les grandes pages transparentes sont activées dans le noyau.

Ensuite, vous pouvez procéder au mappage du pointeur de page obtenu de la manière habituelle avec kmap().

Avis de non-responsabilité:je ne l'ai jamais essayé moi-même, vous devrez donc peut-être faire quelques expériences. Une chose à vérifier est la suivante :HPAGE_PMD_SHIFT représente l'ordre d'une page "énorme" plus petite. Si vous souhaitez utiliser ces pages géantes de 1 Go, vous devrez probablement essayer un ordre différent, probablement PUD_SHIFT - PAGE_SHIFT.


Linux
  1. Linux - Comment déterminer quel module corrompt le noyau ?

  2. Linux – Un noyau corrompu sous Linux ?

  3. Linux - Comment recharger correctement un module du noyau ?

  4. Comment créer, compiler, charger des modules de noyau chargeables Linux LKM

  5. Comment configurer un rappel de minuterie périodique dans un module du noyau Linux

Comment charger et décharger les modules du noyau sous Linux

Commande Modprobe sous Linux

Comment trouver une adresse IP sous Linux

Comment installer le module 'Python-Pexpect' sous Linux

Comment changer l'adresse MAC sous Linux

Comment changer l'adresse IP sous Linux