আমি একটি ঘূর্ণিঝড় ভি এসসিতে লিনাক্স 5.1 চালাচ্ছি, এটি একটি চিপায় দুটি এআরএমভি 7 কোর সহ একটি এফপিজিএ। আমার লক্ষ্য হ'ল বাহ্যিক ইন্টারফেস থেকে প্রচুর ডেটা সংগ্রহ করা এবং কোনও টিসিপি সকেটের মাধ্যমে এই ডেটা স্ট্রিমের অংশ (অংশ)। এখানে চ্যালেঞ্জটি হ'ল ডেটা হার খুব বেশি এবং জিবিই ইন্টারফেসটি স্যাচুরেট করার কাছাকাছি আসতে পারে। আমার একটি কার্যকরী বাস্তবায়ন রয়েছে যা write()সকেটে কেবল কলগুলি ব্যবহার করে তবে এটি 55MB / s এ শীর্ষে রয়েছে; প্রায় তাত্ত্বিক জিবিই সীমা অর্ধেক। থ্রিপুট বাড়ানোর জন্য আমি এখন শূন্য-অনুলিপি টিসিপি ট্রান্সমিশনটি পাওয়ার চেষ্টা করছি, তবে আমি একটি প্রাচীরটি আঘাত করছি।
লিনাক্স ব্যবহারকারী-স্পেসে এফপিজিএ থেকে ডেটা পেতে, আমি একটি কার্নেল ড্রাইভার লিখেছি written এই ড্রাইভারটি এপিপিজিএ-তে একটি ডিএমএ ব্লক ব্যবহার করে এআরএমভি c কোরের সাথে সংযুক্ত ডিডিআর 3 মেমরির একটি বহিরাগত ইন্টারফেস থেকে প্রচুর পরিমাণে ডেটা অনুলিপি করতে। ড্রাইভার এই মেমরিটি একত্রে 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।
কিছুক্ষণ গুগল করার পরে, আমার দ্বিতীয় পদ্ধতিটি ছিল একটি পাইপ ব্যবহার করা এবং তার 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এই পৃষ্ঠাগুলির অন্য কোনও উপায় সন্ধান করা ।