2016-12-12 3 views
0

Je construis un système d'acquisition de données basé sur l'UltraScale + FPGA équipé d'une CPU arm64. Les données sont transmises à la RAM via DMA. Les tampons DMA dans le pilote sont réservés comme ci-dessous:Linux sur arm64: sendto provoque "Défaut non corrigé: erreur d'alignement (0x96000021)" lors de l'envoi de données à partir du tampon DMA mouillé cohérent

virt_buf[i] = dma_zalloc_coherent(&pdev->dev, BUF_SIZE, &phys_buf[i],GFP_KERNEL); 

Dans la fonction mmap du conducteur, la mise en correspondance de l'espace utilisateur se fait de la manière suivante:

#ifdef ARCH_HAS_DMA_MMAP_COHERENT 
    printk(KERN_INFO "Mapping with dma_map_coherent DMA buffer at phys: %p virt %p\n",phys_buf[off],virt_buf[off]); 
    res = dma_mmap_coherent(&my_pdev->dev, vma, virt_buf[off], phys_buf[off], vsize); 
#else 
    physical = phys_buf[off]; 
    res=remap_pfn_range(vma,vma->vm_start, physical >> PAGE_SHIFT , vsize, pgprot_noncached(vma->vm_page_prot)); 
    printk(KERN_INFO "Mapping with remap_pfn_range DMA buffer at phys: %p virt %p\n",physical,virt_buf[off]); 
#endif 

Sur mon UltraScale + remap_pfn_range CPU est utilisé. Dans l'application d'espace utilisateur, les données sont lues à partir du tampon et envoient immédiatement des paquets UDP d'une longueur limitée à MAX_DGRAM (à l'origine égale à 572).

int i = 0; 
int bleft = nbytes; 
while(i<nbytes) { 
    int bts = bleft < MAX_DGRAM ? bleft : MAX_DGRAM; 
    if (sendto(fd,&buf[nbuf][i],bts,0, res2->ai_addr,res2->ai_addrlen)==-1) { 
     printf("%s",strerror(errno)); 
     exit(1); 
    } 
    bleft -= bts; 
    i+= bts; 
} 

Tout a fonctionné parfaitement sur le FPGA Zynq 32 bits. Cependant, après l'avoir déplacé vers le FPGA UltraScale 64 bits, j'ai commencé à recevoir des erreurs aléatoires, après quelques centaines de transferts.

[ 852.703491] Unhandled fault: alignment fault (0x96000021) at 0x0000007f82635584 
[ 852.710739] Internal error: : 96000021 [#4] SMP 
[ 852.715235] Modules linked in: axi4s2dmov(O) ksgpio(O) 
[ 852.720358] CPU: 0 PID: 1870 Comm: a4s2dmov_send Tainted: G  D O 4.4.0 #3 
[ 852.728001] Hardware name: ZynqMP ZCU102 RevB (DT) 
[ 852.732769] task: ffffffc0718ac180 ti: ffffffc0718b8000 task.ti: ffffffc0718b8000 
[ 852.740248] PC is at __copy_from_user+0x8c/0x180 
[ 852.744836] LR is at copy_from_iter+0x70/0x24c 
[ 852.749261] pc : [<ffffffc00039210c>] lr : [<ffffffc0003a36a8>] pstate: 80000145 
[ 852.756644] sp : ffffffc0718bba40 
[ 852.759935] x29: ffffffc0718bba40 x28: ffffffc06a4bae00 
[ 852.765228] x27: ffffffc0718ac820 x26: 000000000000000c 
[ 852.770523] x25: 0000000000000014 x24: 0000000000000000 
[ 852.775818] x23: ffffffc0718bbe08 x22: ffffffc0710eba38 
[ 852.781112] x21: ffffffc0718bbde8 x20: 000000000000000c 
[ 852.786407] x19: 000000000000000c x18: ffffffc000823020 
[ 852.791702] x17: 0000000000000000 x16: 0000000000000000 
[ 852.796997] x15: 0000000000000000 x14: 00000000c0a85f32 
[ 852.802292] x13: 0000000000000000 x12: 0000000000000032 
[ 852.807586] x11: 0000000000000014 x10: 0000000000000014 
[ 852.812881] x9 : ffffffc0718bbcf8 x8 : 000000000000000c 
[ 852.818176] x7 : ffffffc0718bbdf8 x6 : ffffffc0710eba2c 
[ 852.823471] x5 : ffffffc0710eba38 x4 : 0000000000000000 
[ 852.828766] x3 : 000000000000000c x2 : 000000000000000c 
[ 852.834061] x1 : 0000007f82635584 x0 : ffffffc0710eba2c 
[ 852.839355] 
[ 852.840833] Process a4s2dmov_send (pid: 1870, stack limit = 0xffffffc0718b8020) 
[ 852.848134] Stack: (0xffffffc0718bba40 to 0xffffffc0718bc000) 
[ 852.853858] ba40: ffffffc0718bba90 ffffffc0006a1b2c 000000000000000c ffffffc06a9bdb00 
[ 852.861676] ba60: 00000000000005dc ffffffc071a0d200 0000000000000000 ffffffc0718bbdf8 
[ 852.869488] ba80: 0000000000000014 ffffffc06a959000 ffffffc0718bbad0 ffffffc0006a2358 
[...] 
[ 853.213212] Call trace: 
[ 853.215639] [<ffffffc00039210c>] __copy_from_user+0x8c/0x180 
[ 853.221284] [<ffffffc0006a1b2c>] ip_generic_getfrag+0xa4/0xc4 
[ 853.227011] [<ffffffc0006a2358>] __ip_append_data.isra.43+0x80c/0xa70 
[ 853.233434] [<ffffffc0006a3d50>] ip_make_skb+0xc4/0x148 
[ 853.238642] [<ffffffc0006c9d04>] udp_sendmsg+0x280/0x740 
[ 853.243937] [<ffffffc0006d38e4>] inet_sendmsg+0x7c/0xbc 
[ 853.249145] [<ffffffc000651f5c>] sock_sendmsg+0x18/0x2c 
[ 853.254352] [<ffffffc000654b14>] SyS_sendto+0xb0/0xf0 
[ 853.259388] [<ffffffc000084470>] el0_svc_naked+0x24/0x28 
[ 853.264682] Code: a88120c7 a8c12027 a88120c7 36180062 (f8408423) 
[ 853.270791] ---[ end trace 30e1cd8e2ccd56c5 ]--- 
Segmentation fault 
[email protected]_2:~# 

La chose étrange est que quand je lis simplement les mots de la mémoire tampon, il ne provoque pas d'erreurs d'alignement.

Il semble que la fonction envoyer utilise incorrectement la __copy_from_user fonction, ce qui provoque un accès mémoire non alignés. La question est: est-ce le bogue du noyau, ou ai-je fait quelque chose de mal? Cependant, en général, l'envoi du bloc de données ne démarrant pas à une limite de 8 octets ne déclenche pas l'erreur d'alignement. Le problème se produit avec une probabilité relativement faible. Je n'ai pas pu isoler les conditions qui ont conduit à l'erreur. J'ai résolu le problème en ajustant le MAX_DGRAM pour qu'il soit un multiple de 8. Cependant, je crains que le problème puisse réapparaître si les données dans le tampon mapped sont soumises à un traitement plus complexe. Certaines personnes ont signalé des problèmes similaires dans l'architecture arm64 liée à la fonction memcpy (par exemple [https://bugs.launchpad.net/linux-linaro/+bug/1271649]]).

Quelle est la méthode correcte pour mapper des tampons DMA cohérents à l'espace utilisateur pour éviter les erreurs d'alignement de la mémoire?

Répondre

1

Ce pilote doit être mis à jour. ARCH_HAS_DMA_MMAP_COHERENT n'a pas été défini par autre chose que PowerPC depuis longtemps, et même cela ressemble à un reste oublié.

Il existe une implémentation dma_mmap_coherent() générique since 3.6, qui peut et doit être utilisée sans condition. Le résultat du code actuel est que, grâce au #ifdef, vous prenez toujours l'autre chemin, puis grâce au pgprot_noncached() vous finissez par faire le mapping de l'espace utilisateur du buffer Fortement ordonné (Device nGnRnE en termes AArch64). C'est généralement une mauvaise idée, car le code de l'espace utilisateur supposera qu'il fonctionne toujours sur la mémoire normale (sauf si explicitement conçu pour ne pas le faire), et peut faire des choses comme des accès non-alignés ou exclusifs . Je ne vais même pas demander quel genre de folie finit avec le noyau copiant les données d'un espace utilisateur d'un noyau tampon*, mais il suffit de dire le noyau - via copy_{to,from,in}_user() - suppose également que les adresses d'espace utilisateur sont mappé en tant que mémoire normale et donc sûr pour les accès non alignés.Franchement, je suis un peu surpris que cela ne grimpe pas de la même façon sur un ARM 32 bits, donc je suppose que vos données sont toujours alignées sur au moins 4 octets - ce qui expliquerait aussi pourquoi lire des mots (avec des accès 32 bits) est bien, si seulement les accès double-mots 64 bits peuvent potentiellement être mal alignés. En bref, il suffit d'utiliser dma_mmap_coherent(), et de se débarrasser de l'équivalent pauvre à code ouvert. Cela donnera à l'espace utilisateur un mappage normal non mis en mémoire cache (ou un mappage pouvant être mis en cache pour un périphérique matériel cohérent) qui fonctionnera comme prévu. Il n'est pas non plus considéré comme un dma_addr_t est une adresse physique, comme le code de votre pilote semble le faire - c'est une autre chose qui est susceptible de venir et vous mordre dans les fesses tôt ou tard (ZynqMP a une MMU système, donc vous pouvez probablement mettre à jour vers un noyau 4.9, câbler quelques identifiants de flux, les ajouter à la DT, et regarder cette hypothèse aller bang de manière nouvelle et passionnante). * Bien qu'il me soit venu à l'esprit qu'il y avait des circonstances dans lesquelles la copie depuis la fin d'une page peut parfois être relue à la page suivante, ce qui pourrait le déclencher involontairement si la page suivante se trouvait être un Device/Mappage fortement ordonné, qui a conduit à this patch in 4.5. La réponse de Linus à de telles dispositions de mémoire était "...and nobody sane actually does that..."

+0

Merci beaucoup! En effet, c'était un problème. J'ai dû ignorer la suppression de ARCH_HAS_DMA_MMAP_COHERENT. Comme j'ai affaire à des SoCs - à la fois avec les FPGA et les pilotes de noyau, je trouve que mes connaissances sont de plus en plus difficiles à mettre à jour ... En fait, je suis assez amusé que le problème ne se soit pas produit. accès à la partie non-alignée du buffer! Je suis passé à dma_mmap_coherent (en rappelant de mettre vma-> vm_pgoff à 0, après qu'il soit utilisé pour définir le numéro du buffer) et tout fonctionne parfaitement. – wzab