Quelle est la différence entre dma_mmap_coherent et remap_pfn_range?

Actuellement, j’utilise un exemple de pilote à partir duquel je peux apprendre et à partir duquel j’ai basé mon propre pilote personnalisé. Le code mmap est presque identique, sauf que je permets à l’utilisateur de gérer sa propre taille et de baser mon allocation de mémoire autour de cela et que je crée automatiquement le périphérique de char dans / dev.

Pour expliquer le contexte, pour mon cas d’utilisation, j’aimerais préciser un problème que j’ai. dma_mmap_coherent fonctionne de dma_mmap_coherent testée avec la mémoire de kmalloc’d, mais quand j’ai une région d’adresse physique réservée que je veux utiliser avec remap_pfn_range, elle semble fonctionner tranquillement et dmesg ne signale aucune erreur, mais quand je vais lire, peu importe ce que j’ai écrit ici renvoie toujours des octets 0xff. Cela est vrai, que j’utilise iowrite & ioread dans le kernel après avoir effectué une ioremap de la mémoire ou essayé d’écrire dans un espace utilisateur en utilisant un petit test utilisateur de mmap’ing.

J’ai fait autant de recherches sur le sujet que possible, je pense. Tout ce que je peux trouver pour la documentation de remap_pfn_range est la page kernel.org , et certaines archives de la liste de diffusion gmain du kernel sur remap_pfn_range en remplaçant remap_page_range. Quant à dma_mmap_coherent, j’ai pu trouver un peu plus, y compris une présentation des archives Linux .

En fin de compte, il doit y avoir une différence. Il semble y avoir tellement de manières différentes de mapper la mémoire du kernel dans le champ utilisateur. La question que je me pose est la suivante: quelle est la différence entre dma_mmap_coherent et remap_pfn_range ?

Modifier cela pourrait être intéressant de donner un aperçu général des manières de mapper la mémoire du kernel dans l’espace utilisateur en général, en expliquant comment différents apis seraient utilisés dans un rappel mmap du pilote du kernel.

dma_mmap_coherent () est défini dans dma-mapping.h comme une enveloppe autour de dma_mmap_attrs (). dma_mmap_attrs () essaie de voir si un ensemble de dma_mmap_ops est associé au périphérique (struct device * dev) avec lequel vous travaillez, sinon il appelle dma_common_mmap (), ce qui provoque un appel à remap_pfn_range (), après avoir défini la protection de la page comme non cachable (voir dma_common_mmap () dans dma-mapping.c).

En ce qui concerne la présentation générale de la mémoire du kernel sur l’espace utilisateur, voici un moyen simple et rapide de configurer les tampons DMA à partir de l’espace utilisateur:

  1. Allouer un tampon via un IOCTL et désigner un identifiant de tampon pour chaque tampon avec des drapeaux:

     /* A copy-from-user call needs to be done before in the IOCTL */ static int my_ioctl_alloc(struct my_struct *info, struct alloc_info *alloc) { ... info->buf->kvaddr = dma_alloc_coherent(dev, alloc->size, info->buf->phyaddr, GFP_KERNEL); info->buf->buf_id = alloc->buf_id; ... } 
  2. Définir un fichier mmap ops:

     static const struct file_operations my_fops = { .open = my_open, .close = my_close, .mmap = my_mmap, .unlocked_ioctl = my_ioctl, }; 

    N’oubliez pas d’enregistrer la structure my_fops quelque part dans la fonction de sonde de votre pilote.

  3. Implémentez les opérations de fichier mmap:

      static int my_mmap(struct file *fptr, struct vm_area_struct *vma) { ... desc_id = vma->vm_pgoff; buf = find_buf_by_id(alloc, desc_id); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ret = remap_pfn_range(vma, vma->vm_start, buf->phyaddr >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); if (ret) { /* Error Handle */ } return 0; } 

Avec ceci votre pilote de kernel devrait avoir le minimum pour allouer et les tampons de mmap. Libérer les tampons est un exercice pour les points bonus!

Dans l’application, vous ouvrez () le fichier et obtenez un descripteur de fichier valide fd, appelez l’allocation IOCTL et définissez l’ID du tampon avant d’effectuer une copie au kernel. Dans mmap, vous donneriez l’ID du tampon via le paramètre offset:

  mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer_id << PAGE_SHIFT); 

PAGE_SHIFT est un temps de compilation dépendant de l'architecture, fixé par MACRO dans le kernel. J'espère que cela t'aides.

Ce n'est pas un code conforme à checkpatch.pl, ce n'est pas non plus la meilleure pratique, mais c'est une façon de savoir comment faire. Commentaires / améliorations / suggestions bienvenue!

Voir Pilotes de périphériques Linux - Chapitre 15: Mappage de mémoire et DMA pour les exemples de manuels et bonnes informations de base pour le lecteur intéressé.