Saya menjalankan Linux 5.1 pada Cyclone V SoC, yang merupakan FPGA dengan dua core ARMv7 dalam satu chip. Tujuan saya adalah mengumpulkan banyak data dari antarmuka eksternal dan mengalirkan (bagian dari) data ini melalui soket TCP. Tantangannya di sini adalah kecepatan data sangat tinggi dan bisa mendekati saturasi antarmuka GbE. Saya memiliki implementasi yang berfungsi yang hanya menggunakan write()
panggilan ke soket, tetapi mencapai 55MB / s; kira-kira setengah dari batas GbE teoritis. Saya sekarang mencoba untuk mendapatkan nol-copy transmisi TCP untuk meningkatkan throughput, tapi saya memukul dinding.
Untuk mendapatkan data dari FPGA ke dalam ruang pengguna Linux, saya telah menulis driver kernel. Driver ini menggunakan blok DMA di FPGA untuk menyalin sejumlah besar data dari antarmuka eksternal ke memori DDR3 yang melekat pada inti ARMv7. Pengandar mengalokasikan memori ini sebagai sekelompok buffer 1MB yang berdekatan ketika diselidiki dma_alloc_coherent()
dengan GFP_USER
, dan memaparkan ini ke aplikasi userspace dengan mengimplementasikan mmap()
pada file di /dev/
dan mengembalikan alamat ke aplikasi menggunakan dma_mmap_coherent()
pada buffer preallocated.
Sejauh ini bagus; aplikasi ruang pengguna melihat data yang valid dan throughput lebih dari cukup di> 360MB / s dengan ruang kosong (antarmuka eksternal tidak cukup cepat untuk benar-benar melihat apa batas atas).
Untuk menerapkan zero-copy TCP networking, pendekatan pertama saya adalah menggunakan SO_ZEROCOPY
soket:
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
Namun, ini menghasilkan send: Bad address
.
Setelah googling sebentar, pendekatan kedua saya adalah menggunakan pipa dan splice()
diikuti oleh 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;
}
Namun, hasilnya adalah sama: vmsplice: Bad address
.
Perhatikan bahwa jika saya mengganti panggilan ke vmsplice()
atau send()
ke fungsi yang hanya mencetak data yang ditunjukkan oleh buf
(atau send()
tanpa MSG_ZEROCOPY
), semuanya berfungsi dengan baik; sehingga data dapat diakses oleh userspace, tetapi vmsplice()
/ send(..., MSG_ZEROCOPY)
panggilan tampaknya tidak dapat menanganinya.
Apa yang kulewatkan di sini? Apakah ada cara menggunakan pengiriman nol-salinan TCP dengan alamat ruang pengguna yang diperoleh dari driver kernel melalui dma_mmap_coherent()
? Apakah ada pendekatan lain yang bisa saya gunakan?
MEMPERBARUI
Jadi saya terjun sedikit lebih dalam ke sendmsg()
MSG_ZEROCOPY
jalur di kernel, dan panggilan yang akhirnya gagal adalah get_user_pages_fast()
. Panggilan ini kembali -EFAULT
karena check_vma_flags()
menemukan VM_PFNMAP
bendera yang diatur di vma
. Bendera ini tampaknya disetel saat halaman dipetakan ke dalam ruang pengguna menggunakan remap_pfn_range()
atau dma_mmap_coherent()
. Pendekatan saya berikutnya adalah menemukan cara lain untuk mmap
halaman-halaman ini.