मैं एक चक्रवात V SoC पर लिनक्स 5.1 चला रहा हूं, जो एक चिप में दो ARMv7 कोर के साथ एक FPGA है। मेरा लक्ष्य एक बाहरी इंटरफ़ेस और टीसीपी सॉकेट के माध्यम से इस डेटा के प्रवाह (भाग) से बहुत सारे डेटा को इकट्ठा करना है। यहां चुनौती यह है कि डेटा दर बहुत अधिक है और GbE इंटरफ़ेस को संतृप्त करने के करीब आ सकती है। मेरे पास एक कार्यशील कार्यान्वयन है जो सिर्फ write()
सॉकेट के लिए कॉल का उपयोग करता है , लेकिन यह 55 एमबी / एस में सबसे ऊपर है; लगभग आधा सैद्धांतिक जीबीई सीमा। मैं अब थ्रूपुट को बढ़ाने के लिए शून्य-प्रतिलिपि टीसीपी ट्रांसमिशन प्राप्त करने की कोशिश कर रहा हूं, लेकिन मैं एक दीवार मार रहा हूं।
FPGA से डेटा को लिनक्स यूजर-स्पेस में लाने के लिए, मैंने कर्नेल ड्राइवर लिखा है। यह ड्राइवर एक बाहरी इंटरफ़ेस से ARMv7 कोर से जुड़ी DDR3 मेमोरी में बड़ी मात्रा में डेटा की प्रतिलिपि बनाने के लिए FPGA में DMA ब्लॉक का उपयोग करता है। चालक इस मेमोरी को सन्निहित 1MB बफ़र्स के एक समूह के रूप में आवंटित करता है, जब उपयोग करने की संभावना dma_alloc_coherent()
होती है GFP_USER
, और उन्हें उपयोक्ता अनुप्रयोग में mmap()
एक फाइल पर लागू करके और उपयोक्ता के बफ़र पर /dev/
प्रयोग करके एक पते पर वापस dma_mmap_coherent()
भेज देता है।
अब तक सब ठीक है; उपयोगकर्ता-स्पेस एप्लिकेशन वैध डेटा देख रहा है और थ्रूपुट कमरे के अतिरिक्त> 360MB / s पर कमरे के अतिरिक्त (बाहरी इंटरफ़ेस वास्तव में ऊपरी सीमा क्या है यह देखने के लिए पर्याप्त तेज़ नहीं है) से अधिक है।
शून्य-प्रतिलिपि टीसीपी नेटवर्किंग को लागू करने के लिए, मेरा पहला दृष्टिकोण 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)
कॉल इसे संभालने में असमर्थ हैं।
मुझे यहां क्या समझ नहीं आ रहा है? क्या कर्नेल ड्राइवर से प्राप्त उपयोगकर्ता-स्थान पते के साथ शून्य-प्रतिलिपि टीसीपी भेजने का उपयोग करने का कोई तरीका है dma_mmap_coherent()
? क्या कोई अन्य तरीका है जिसका मैं उपयोग कर सकता हूं?
अपडेट करें
इसलिए मैं sendmsg()
MSG_ZEROCOPY
कर्नेल में पथ में थोड़ा गहरा काम करता हूं , और अंततः विफल होने वाली कॉल है get_user_pages_fast()
। यह कॉल लौटाता है -EFAULT
क्योंकि ध्वज सेट को check_vma_flags()
ढूँढता VM_PFNMAP
है vma
। यह ध्वज स्पष्ट रूप से तब सेट किया जाता है जब पृष्ठों का उपयोग करके उपयोगकर्ता स्थान में मैप किया जाता है remap_pfn_range()
या dma_mmap_coherent()
। मेरा अगला तरीका mmap
इन पन्नों को खोजने का है ।