Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Use pgprot_noncached for ARM64#28

Merged
ikwzm merged 1 commit intoikwzm:masterfromiasakura:use_pgprot_nonchached
Oct 26, 2019

Conversation

iasakura
Copy link
Contributor

はじめまして、お世話になっております。

Ultra96 上で udmabuf を用いて 72 byte のデータを udmabuf に配置しその後 AXI DMA を用いて PL に送信してみたのですが、最初の 64 byte のみ送信され、残りの値が 0 になってしまいました。
以下のコードで再現します。

#include<stdio.h>#include<stdint.h>#include<stdlib.h>#include<string.h>#include<assert.h>#include<fcntl.h>#include<sys/mman.h>#include<unistd.h>intmain(intargc,char*argv[]) {intfd;charattr[1024];if ((fd=open("/dev/udmabuf0",O_RDWR |O_SYNC))==-1) {printf("cannot open udmabuf0\n");exit(-1);    }uint32_tsize=0;    {intfd=0;if ((fd=open("/sys/class/udmabuf/udmabuf0/size",O_RDONLY))==-1) {fprintf(stderr,"cannot get size\n");exit(-1);        }read(fd,attr,1024);sscanf(attr,"%u",&size);close(fd);    }uint32_tphys_addr=0;    {intfd=0;if ((fd=open("/sys/class/udmabuf/udmabuf0/phys_addr",O_RDONLY))==-1) {fprintf(stderr,"cannot get phys_addr\n");exit(-1);        }read(fd,attr,1024);sscanf(attr,"%x",&phys_addr);close(fd);    }uint8_t*ptr= (uint8_t*)mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);if (ptr==NULL) {fprintf(stderr,"cannot mmap physically-backed mempool\n");exit(-1);    }// Truncate offset to a multiple of the page size, or mmap will fail.size_tpagesize=sysconf(_SC_PAGE_SIZE);off_tpage_base= (phys_addr /pagesize)*pagesize;off_tpage_offset=phys_addr-page_base;unsignedchar*mem=NULL;    {intfd=open("/dev/mem",O_RDWR);mem=mmap(NULL,page_offset+SIZE,PROT_READ |PROT_WRITE,MAP_PRIVATE,fd,page_base);if (mem==MAP_FAILED) {perror("Can't map memory");exit(-1);        }    }uint8_tarr[SIZE];for (inti=0;i<SIZE;++i) {arr[i]=1;    }intsize_in_bytes=atoi(argv[1]);memcpy(ptr,arr,size_in_bytes);printf("%d\n",mem[IDX]);return0;}

以下のようにして実行できます。

gcc -DSIZE=72 -DIDX=64 -g -o udmabuf_test udmabuf_test.c./udmabuf_test 720

1で埋められた配列 arr を udmabuf で確保したメモリに memcpy した後、/dev/mem を通じて phys_addr+64 にアクセスしているのですが、0が返ってきてしまいます。(0~63 にアクセスしたときは正しく1が返ってきます。)

この PR の変更を適用することで解決したのですが、もし正しい修正ならマージしていただけますでしょうか。

よろしくお願いいたします。

@ikwzm
Copy link
Owner

issue ありがとうございます。

私も当初、pgprot_noncached(vm_page_prot) にしていたのですが、ARM64 の場合、pgprot_noncached(vm_page_prot) だと memset() で Bus error が起きました。
当時の状況を再現すると次のようになります。

root@debian-fpga:~/work/u-dma-buf# insmod u-dma-buf.ko udmabuf0=0x100000[ 1503.464874] u-dma-buf u-dma-buf.0: DMA mask not set[ 1503.472404] u-dma-buf udmabuf0: driver version = 2.1.0-rc2[ 1503.477913] u-dma-buf udmabuf0: major number   = 239[ 1503.482876] u-dma-buf udmabuf0: minor number   = 0[ 1503.487662] u-dma-buf udmabuf0: phys address   = 0x0000000070100000[ 1503.493931] u-dma-buf udmabuf0: buffer size    = 1048576[ 1503.499240] u-dma-buf udmabuf0: dma device     = u-dma-buf.0[ 1503.504893] u-dma-buf udmabuf0: dma coherent   = 0[ 1503.509682] u-dma-buf u-dma-buf.0: driver installed.root@debian-fpga:~/work/u-dma-buf# ./u-dma-buf_testphys_addr=0x70100000size=1048576check_buf()sync_mode=0, O_SYNC=0, time = 0.340671 secsync_mode=0, O_SYNC=1, time = 0.340689 secsync_mode=1, O_SYNC=0, time = 0.340718 secsync_mode=1, O_SYNC=1, time = 2.287233 secsync_mode=2, O_SYNC=0, time = 0.340636 secsync_mode=2, O_SYNC=1, time = 2.284903 secsync_mode=3, O_SYNC=0, time = 0.340641 secsync_mode=3, O_SYNC=1, time = 2.285236 secsync_mode=4, O_SYNC=0, time = 0.340651 secsync_mode=4, O_SYNC=1, time = 0.340708 secsync_mode=5, O_SYNC=0, time = 2.286918 secsync_mode=5, O_SYNC=1, time = 2.286774 secsync_mode=6, O_SYNC=0, time = 2.284642 secsync_mode=6, O_SYNC=1, time = 2.285348 secsync_mode=7, O_SYNC=0, time = 2.285135 secsync_mode=7, O_SYNC=1, time = 2.288342 secclear_buf()sync_mode=0, O_SYNC=0, time = 0.026720 secsync_mode=0, O_SYNC=1, time = 0.026641 secsync_mode=1, O_SYNC=0, time = 0.026642 secBus errorroot@debian-fpga:~/work/u-dma-buf#

ここでの u-dma-buf_test は、https://github.com/ikwzm/udmabuf の含まれている u-dma-buf_test.c をコンパイルしたものです。

この問題は pgprot_noncached(vm_page_prot) を pgprot_writecombine(vm_page_prot) に変更することで治りました。変更後の u-dma-buf_test 実行結果は次の通りです。

root@debian-fpga:~/work/u-dma-buf# insmod u-dma-buf.ko udmabuf0=0x100000[ 1871.450164] u-dma-buf u-dma-buf.0: DMA mask not set[ 1871.458503] u-dma-buf udmabuf0: driver version = 2.1.0-rc2[ 1871.463998] u-dma-buf udmabuf0: major number   = 238[ 1871.468970] u-dma-buf udmabuf0: minor number   = 0[ 1871.473763] u-dma-buf udmabuf0: phys address   = 0x0000000070100000[ 1871.480032] u-dma-buf udmabuf0: buffer size    = 1048576[ 1871.485341] u-dma-buf udmabuf0: dma device     = u-dma-buf.0[ 1871.490993] u-dma-buf udmabuf0: dma coherent   = 0[ 1871.495782] u-dma-buf u-dma-buf.0: driver installed.root@debian-fpga:~/work/u-dma-buf# ./u-dma-buf_testphys_addr=0x70100000size=1048576check_buf()sync_mode=0, O_SYNC=0, time = 0.340644 secsync_mode=0, O_SYNC=1, time = 0.340684 secsync_mode=1, O_SYNC=0, time = 0.340817 secsync_mode=1, O_SYNC=1, time = 2.284729 secsync_mode=2, O_SYNC=0, time = 0.340670 secsync_mode=2, O_SYNC=1, time = 2.284785 secsync_mode=3, O_SYNC=0, time = 0.340598 secsync_mode=3, O_SYNC=1, time = 2.284776 secsync_mode=4, O_SYNC=0, time = 0.340752 secsync_mode=4, O_SYNC=1, time = 0.340691 secsync_mode=5, O_SYNC=0, time = 2.284722 secsync_mode=5, O_SYNC=1, time = 2.284562 secsync_mode=6, O_SYNC=0, time = 2.284552 secsync_mode=6, O_SYNC=1, time = 2.284584 secsync_mode=7, O_SYNC=0, time = 2.284632 secsync_mode=7, O_SYNC=1, time = 2.284620 secclear_buf()sync_mode=0, O_SYNC=0, time = 0.019320 secsync_mode=0, O_SYNC=1, time = 0.019328 secsync_mode=1, O_SYNC=0, time = 0.019312 secsync_mode=1, O_SYNC=1, time = 0.026633 secsync_mode=2, O_SYNC=0, time = 0.019315 secsync_mode=2, O_SYNC=1, time = 0.026621 secsync_mode=3, O_SYNC=0, time = 0.019306 secsync_mode=3, O_SYNC=1, time = 0.026632 secsync_mode=4, O_SYNC=0, time = 0.019307 secsync_mode=4, O_SYNC=1, time = 0.019317 secsync_mode=5, O_SYNC=0, time = 0.026609 secsync_mode=5, O_SYNC=1, time = 0.026621 secsync_mode=6, O_SYNC=0, time = 0.026615 secsync_mode=6, O_SYNC=1, time = 0.026624 secsync_mode=7, O_SYNC=0, time = 0.026605 secsync_mode=7, O_SYNC=1, time = 0.026623 sec

ですので、pgprot_noncached に変更するのは見送らせてください。

しかし、ご指摘の通り、問題があるのはわかりました。
write_combine は CPUからの書き込み要求をキャッシュラインサイズ(ARM64 では 64Byte)単位でをマージしてからメモリに書き込む方法なので、ご指摘のプログラムのようにキャッシュライン単位じゃない書き込み(72Byte書き込み)の後、すぐに読み出すとおかしくなるのかもしれません。
ですが、これって、デバイスドライバレベルで修正できるものなのかわかりません。。。

@ikwzmikwzm merged commitf1e73f4 intoikwzm:masterOct 26, 2019
@ikwzm
Copy link
Owner

前言撤回します

あなたの pull request をマージさせてもらいました。ありがとうございます。

マージした理由は、Readme.md や Readme.ja.md にも書きましたが、キャッシュコヒーレンシの問題は発症した場合とても分かりにくくデバッグが困難だからです。それに悩むくらいなら、バスエラーを発生させた方がまだよいだろうと判断しました。

もしバスエラーが発生した場合は、O_SYNC によるキャッシュ無効化ではなく、ハードウェアによるキャッシュコヒーレンシ維持か、sync_for_cpu、sync_for_device によるマニュアルでのキャッシュコヒーレンシ維持を使うことを検討してください。

この度は、貴重な pull request ありがとうございました。今後ともよろしくお願いします。

@iasakura
Copy link
ContributorAuthor

レビュー & マージありがとうございました!
なるほど、memset でバスエラーが生じる問題があったのですね…。こちらでも再現させてみて、もしなにか分かったらまた報告させていただこうと思います。

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers
No reviews
Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

2 participants
@iasakura@ikwzm

[8]ページ先頭

©2009-2025 Movatter.jp