ฉันใช้ 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
หน้าเหล่านี้