하나의 칩에 2 개의 ARMv7 코어가있는 FPGA 인 Cyclone V SoC에서 Linux 5.1을 실행하고 있습니다. 내 목표는 외부 인터페이스에서 많은 데이터를 수집하고 TCP 소켓을 통해이 데이터를 스트림하는 것입니다. 여기서의 문제는 데이터 전송률이 매우 높고 GbE 인터페이스를 포화시키는 데 근접 할 수 있다는 것입니다. write()
소켓에 대한 호출을 사용하는 실제 구현이 있지만 55MB / s에서 최고입니다. 이론적 GbE 한계의 약 절반입니다. 처리량을 높이기 위해 제로 복사 TCP 전송을 시도하고 있지만 벽에 부딪 치고 있습니다.
FPGA에서 데이터를 Linux 사용자 공간으로 가져 오기 위해 커널 드라이버를 작성했습니다. 이 드라이버는 FPGA의 DMA 블록을 사용하여 많은 양의 데이터를 외부 인터페이스에서 ARMv7 코어에 연결된 DDR3 메모리로 복사합니다. 드라이버 는 dma_alloc_coherent()
with를 사용하여 프로브 할 때이 메모리를 연속 된 1MB 버퍼의 무리로 할당 하고 GFP_USER
, mmap()
파일 을 구현 하고 사전 할당 된 버퍼를 /dev/
사용하여 애플리케이션에 주소를 반환 하여 사용자 공간 애플리케이션에이를 노출합니다 dma_mmap_coherent()
.
여태까지는 그런대로 잘됐다; 사용자 공간 응용 프로그램에 유효한 데이터가 표시되고 여유 공간이있는> 360MB / s에서 처리량이 충분합니다 (외부 인터페이스는 상한을 실제로 볼 수있을만큼 빠르지 않습니다).
무 복사 TCP 네트워킹을 구현하기 위해 첫 번째 접근 방식은 SO_ZEROCOPY
소켓 에서 사용 하는 것입니다.
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
그러나이 결과는 send: Bad address
입니다.
조금 인터넷 검색을 한 후, 두 번째 접근 방식은 파이프를 사용하고 splice()
다음 과 vmsplice()
같습니다.
ssize_t sent_bytes;
int pipes[2];
struct iovec iov = {
.iov_base = buf,
.iov_len = len
};
pipe(pipes);
sent_bytes = vmsplice(pipes[1], &iov, 1, 0);
if (sent_bytes < 0) {
perror("vmsplice");
return -1;
}
sent_bytes = splice(pipes[0], 0, fd, 0, sent_bytes, SPLICE_F_MOVE);
if (sent_bytes < 0) {
perror("splice");
return -1;
}
그러나 결과는 동일 vmsplice: Bad address
합니다.
내가 가리키는 (또는 없는 ) 데이터를 인쇄하는 함수 호출 vmsplice()
또는 send()
함수를 대체하면 모든 것이 잘 작동합니다. 따라서 사용자 공간에서 데이터에 액세스 할 수 있지만 / 호출로 처리 할 수없는 것 같습니다.buf
send()
MSG_ZEROCOPY
vmsplice()
send(..., MSG_ZEROCOPY)
내가 여기서 무엇을 놓치고 있습니까? 커널 드라이버에서 얻은 사용자 공간 주소로 zero-copy TCP 전송을 사용하는 방법이 dma_mmap_coherent()
있습니까? 사용할 수있는 다른 방법이 있습니까?
최신 정보
그래서 sendmsg()
MSG_ZEROCOPY
커널 의 경로를 조금 더 깊이 파고 들었고 결국 실패하는 호출은 get_user_pages_fast()
입니다. 이 호출은 에서 설정된 플래그를 찾기 -EFAULT
때문에 반환 됩니다. 페이지가 사용하는 사용자 공간에 매핑 할 때이 플래그는 분명히 설정 또는 . 다음 접근 방법은 이 페이지에 다른 방법을 찾는 것 입니다.check_vma_flags()
VM_PFNMAP
vma
remap_pfn_range()
dma_mmap_coherent()
mmap