ฉันใช้ Linux 5.1 บน Cyclone V SoC ซึ่งเป็น FPGA ที่มีสอง ARMv7 cores ในหนึ่งชิป เป้าหมายของฉันคือการรวบรวมข้อมูลจำนวนมากจากอินเทอร์เฟซภายนอกและสตรีม (ส่วนหนึ่ง) ข้อมูลนี้ผ่านทางซ็อกเก็ต TCP ความท้าทายที่นี่คืออัตราข้อมูลสูงมากและอาจเข้าใกล้อินเทอร์เฟซ GbE ที่อิ่มตัว ฉันมีการนำไปใช้งานที่ใช้การwrite()เรียกไปยังซ็อกเก็ต แต่มันใช้งานได้ที่ 55MB / s; ประมาณครึ่งหนึ่งของขีด จำกัด GbE ตามทฤษฎี ตอนนี้ฉันกำลังพยายามให้การส่ง TCP เป็นศูนย์คัดลอกเพื่อเพิ่มปริมาณงาน แต่ฉันชนกำแพง
เพื่อให้ได้ข้อมูลจาก FPGA ลงในพื้นที่ผู้ใช้ Linux ฉันได้เขียนไดรเวอร์เคอร์เนล ไดรเวอร์นี้ใช้บล็อก DMA ใน FPGA เพื่อคัดลอกข้อมูลจำนวนมากจากอินเทอร์เฟซภายนอกไปยังหน่วยความจำ DDR3 ที่ต่ออยู่กับแกน ARMv7 จัดสรรโปรแกรมควบคุมหน่วยความจำนี้เป็นพวงของบัฟเฟอร์ 1MB ต่อเนื่องกันเมื่อตรวจสอบโดยใช้dma_alloc_coherent()กับGFP_USERและ exposes เหล่านี้ไปยังโปรแกรมประยุกต์ userspace โดยการใช้mmap()ไฟล์ใน/dev/และกลับมาอยู่กับการประยุกต์ใช้dma_mmap_coherent()ในบัฟเฟอร์ preallocated
จนถึงตอนนี้ดีมาก; แอปพลิเคชันพื้นที่ผู้ใช้เห็นข้อมูลที่ถูกต้องและปริมาณงานมีมากเกินพอที่> 360MB / s พร้อมพื้นที่ว่าง (อินเทอร์เฟซภายนอกไม่เร็วพอที่จะดูว่าขอบเขตบนคืออะไร)
ในการใช้เครือข่าย TCP แบบ zero-copy แนวทางแรกของฉันคือใช้SO_ZEROCOPYกับซ็อกเก็ต:
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
    perror("send");
    return -1;
}
send: Bad addressอย่างไรก็ตามผลนี้
หลังจาก googling สักพักวิธีที่สองของฉันคือใช้ไพพ์และ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)โทรจะไม่สามารถจัดการได้
ฉันหายไปนี่อะไร มีวิธีการใช้การส่ง TCP เป็นศูนย์สำเนาด้วยที่อยู่พื้นที่ผู้ใช้ที่ได้รับจากเคอร์เนลไดรเวอร์ผ่านdma_mmap_coherent()? มีวิธีอื่นที่ฉันสามารถใช้ได้หรือไม่?
UPDATE
ดังนั้นผมจึงนกพิราบบิตลึกเข้าไปในsendmsg() MSG_ZEROCOPYเส้นทางใน kernel get_user_pages_fast()และโทรที่ล้มเหลวในที่สุดคือ ผลตอบแทนที่สายนี้-EFAULTเพราะcheck_vma_flags()พบชุดธงในVM_PFNMAP vmaตั้งค่าสถานะนี้เห็นได้ชัดว่าเมื่อหน้าเว็บที่มีการแมปลงในพื้นที่ของผู้ใช้โดยใช้หรือremap_pfn_range() dma_mmap_coherent()แนวทางต่อไปของฉันคือการหาวิธีอื่นในmmapหน้าเหล่านี้