Tôi đang chạy Linux 5.1 trên Cyclone V SoC, đây là một GPU có hai lõi ARMv7 trong một chip. Mục tiêu của tôi là thu thập nhiều dữ liệu từ giao diện bên ngoài và truyền phát (một phần) dữ liệu này qua ổ cắm TCP. Thách thức ở đây là tốc độ dữ liệu rất cao và có thể tiến gần đến việc bão hòa giao diện GbE. Tôi có một triển khai hoạt động chỉ sử dụng write()
các cuộc gọi đến ổ cắm, nhưng nó đạt tốc độ 55MB / s; khoảng một nửa giới hạn GbE lý thuyết. Bây giờ tôi đang cố gắng để truyền TCP không sao chép để hoạt động để tăng thông lượng, nhưng tôi đang va vào tường.
Để đưa dữ liệu ra khỏi FPGA vào không gian người dùng Linux, tôi đã viết một trình điều khiển hạt nhân. Trình điều khiển này sử dụng một khối DMA trong FPGA để sao chép một lượng lớn dữ liệu từ giao diện bên ngoài vào bộ nhớ DDR3 được gắn vào lõi ARMv7. Các giao đất điều khiển bộ nhớ này là một loạt các bộ đệm 1MB tiếp giáp khi sử dụng thăm dò dma_alloc_coherent()
với GFP_USER
, và cho thấy những cho các ứng dụng userspace bằng cách thực hiện mmap()
trên một tập tin trong /dev/
và trả lại một địa chỉ để các ứng dụng sử dụng dma_mmap_coherent()
trên bộ đệm preallocated.
Càng xa càng tốt; ứng dụng không gian người dùng đang nhìn thấy dữ liệu hợp lệ và thông lượng là quá đủ với> 360 MB / giây có chỗ trống (giao diện bên ngoài không đủ nhanh để thực sự thấy giới hạn trên là gì).
Để thực hiện kết nối mạng TCP không sao chép, cách tiếp cận đầu tiên của tôi là sử dụng SO_ZEROCOPY
trên ổ cắm:
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
Tuy nhiên, điều này dẫn đến send: Bad address
.
Sau khi googling một chút, cách tiếp cận thứ hai của tôi là sử dụng một đường ống và splice()
theo sau 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;
}
Tuy nhiên, kết quả là như nhau : vmsplice: Bad address
.
Lưu ý rằng nếu tôi thay thế cuộc gọi đến vmsplice()
hoặc send()
đến một chức năng chỉ in dữ liệu được trỏ đến buf
(hoặc send()
không có MSG_ZEROCOPY
), mọi thứ đều hoạt động tốt; vì vậy dữ liệu có thể truy cập vào không gian người dùng, nhưng các cuộc gọi vmsplice()
/ send(..., MSG_ZEROCOPY)
dường như không thể xử lý nó.
Tôi đang thiếu gì ở đây? Có cách nào sử dụng gửi TCP không sao chép với địa chỉ không gian người dùng thu được từ trình điều khiển hạt nhân thông qua dma_mmap_coherent()
không? Có cách tiếp cận nào khác tôi có thể sử dụng?
CẬP NHẬT
Vì vậy, tôi tìm hiểu sâu hơn một chút về sendmsg()
MSG_ZEROCOPY
đường dẫn trong kernel và cuộc gọi cuối cùng thất bại là get_user_pages_fast()
. Cuộc gọi này trả về -EFAULT
vì check_vma_flags()
tìm thấy VM_PFNMAP
cờ được đặt trong vma
. Cờ này rõ ràng được đặt khi các trang được ánh xạ vào không gian người dùng bằng cách sử dụng remap_pfn_range()
hoặc dma_mmap_coherent()
. Cách tiếp cận tiếp theo của tôi là tìm một cách khác cho mmap
các trang này.