Производительность Memcpy на /dev / mem вне оперативной памяти ядра
Я использую SoC с пользовательским linux на нем. Я зарезервировал верхние 512 МБ из 1 ГБ общей оперативной памяти, указав параметр загрузки ядра mem=512M.
Я могу получить доступ к верхней памяти из программы userspace, открыв /dev/mem и mmap верхние 512 МБ, которые не используются ядром.
Знаю, что я хочу скопировать большие куски памяти внутри этой области с помощью memcpy (), но производительность составляет около 50 МБ / сек. Когда я распределяю буферы ядром и memcpy между ними, я могу достигать около 500 МБ / сек.
Я совершенно уверен. это связано с тем, что кэш отключен для моей специальной области памяти, но не знаю, как сказать ядру использовать кэш здесь.
Есть ли у кого-нибудь идея, как это решить?
3 ответов:
Примечание: многое из этого предваряется моими главными комментариями, поэтому я постараюсь не повторять их дословно.
О буферах для DMA, доступа к ядру и доступа к пользовательскому пространству. Буферы могут быть выделены любым подходящим механизмом.
Как уже упоминалось, используяmem=512Mи/dev/memсmmapв пользовательском пространстве, драйверmemможет не задавать оптимальную политику кэширования. Кроме того,mem=512Mобычно используется для указания ядру просто никогда не использовать память (например, мы хотим тест с меньшим объемом системной памяти), и мы не собираемся использовать верхний 512M для чего-либо.Лучший способ может оставить
Область памяти может быть выбрана с помощью параметров командной строки ядра [изmem=512Mи использоватьCMA, Как вы упомянули. Другой способ может заключаться в привязке драйвера к ядру и резервировании полного блока памяти во время запуска системы [возможно, с помощьюCMA].grub.cfg], таких какmydev.area=иmydev.size=. Это полезно для" связанного " водителя, который должен знать эти значения на "ранних" этапах запуска системы. Итак, теперь у нас есть "большая" область. Теперь нам нужно иметь способ для устройства, чтобы получить доступ и приложение, чтобы получить его сопоставление. Драйвер ядра может сделать это. Когда устройство открыто,ioctlможет настроить сопоставления с правильной политикой ядра. Таким образом, в зависимости от механизма распределения,ioctlможет быть даноaddress/lengthприложением, или оно может передать их обратно приложению [соответствующим образом отображенный]. Когда мне пришлось это сделать, я создал структуру, которая описывала область памяти/буфер. Это может быть целая область или большая область может быть разделена по мере необходимости. Вместо использования динамической схемы переменной длины, эквивалентнойmalloc[как то, что вы писали], я обнаружил, что субпулы фиксированного размера работают лучше. В ядре это называется распределителем "сляба". Структура имела "идентификационный" номер для данной области. Он также имел три адреса: адрес приложение может использовать, адрес драйвер ядра может использовать, и адрес, который будет дан H / W устройству. Кроме того, в случае нескольких устройств он может иметь идентификатор, с которым он в данный момент связан. Итак, вы берете большую область и подразделяете ее вот так. 5 устройств. Dev0 нужно 10 1K буферов, Dev1 нужно 10 20k буферов, Dev3 нужно 10 2K буферов, ...Приложение и драйвер ядра будут хранить списки этих структур дескрипторов. Приложение запустило бы DMA с другим
ioctlдля этого потребуется идентификационный номер дескриптора. Повторите это для всех устройств.Затем приложение может выдать
ioctl, которое ожидает завершения. Драйвер заполняет дескриптор только что завершенной операции. Приложение обрабатывает данные и циклы. Он делает это "на месте" - см.Вы беспокоитесь о том, что скорость будет медленной. Как мы уже обсуждали, это может быть связано с тем, как вы использовали
mmapна/dev/mem.Но, если вы переходите с устройства в память, то Кэш процессора может стать устаревшим, поэтому вам придется учитывать это. Настоящий драйвер устройства имеет множество процедур поддержки в ядре, чтобы справиться с этим.
вот большой : Почему вам нужно делать
memcpyна всех? Если все настроено правильно, приложение может работать непосредственно с данными без необходимости их копирования. То есть операция DMA помещает данные именно в то место, где они нужны приложению.Наугад, прямо сейчас, у вас есть ваш
memcpy"гонки" против устройства. То есть, вы должны скопировать данные быстро, так что вы можете запустить следующий DMA без потери каких-либо данных."большая" область должна быть разделена [как упоминалось выше], и драйвер ядра должен знать о разделах. Итак, драйвер начинает DMA с id 0. Когда это завершается, он немедленно [в ISR] запускает DMA к id 1. Когда это завершается, он переходит к следующему в своем субпуле. Это можно сделать аналогичным образом для каждого устройства. Приложение может опрос для завершения с помощью
Таким образом, драйвер может поддерживать максимальную скорость работы всех устройств,а приложение может иметь достаточно времени для обработки заданного буфера. И, опять же, ему не нужно копировать его.ioctlЕще одна тема для разговора. Являются ли регистры DMA на ваших устройствах двойной буферизацией или нет? Я предполагаю, что ваши устройства не поддерживают сложные списки разброса/сбора и относительно просты.
В моем конкретном случае, в rev 1 из H/W регистры DMA были не дважды буферизованы.
Таким образом, после запуска DMA в буфере 0 драйвер должен был дождаться прерывания завершения для буфера 0, прежде чем настроить регистры DMA для следующей передачи в буфер 1. Таким образом, водитель должен был "мчаться", чтобы сделать настройку для следующего DMA [и имел очень короткое окно времени, чтобы сделать это]. После запуска буфера 0, если бы драйвер изменил регистры DMA на устройстве, это нарушило бы уже активный буфер. запрос.Мы исправили это в rev 2 с двойной буферизацией. Когда драйвер настроит DMA regs, он ударит по порту" пуск". Все порты DMA были немедленно защелкнуты устройством. В этот момент драйвер был свободен выполнить полную настройку буфера 1, и устройство автоматически переключится на него [без вмешательства драйвера], когда буфер 0 будет завершен. Драйвер получит прерывание, но может занять почти все время передачи, чтобы настроить следующий запрос.
Итак, с системой стилей rev 1 подход
uioмог бы не работать-это было бы слишком медленно. С rev 2,uioвозможно, но я не фанат, даже если это возможно.Примечание: в моем случае мы сделали не использовать
Более того, каковы требования? Количество передаваемых данных в секунду. Количество устройств, которые делают что? Являются ли они все входными или есть и выходные?read(2)илиwrite(2)к устройству чтения/записи обратных вызовов вообще. Все обрабатывалось с помощью специальных вызововioctl, которые принимали различные структуры, подобные упомянутой выше. В какой-то момент на ранней стадии мы действительно использовали чтение / запись таким образом аналогично тому, как их используетuio. Но мы обнаружили, что отображение является искусственным и ограниченным [и хлопотным], поэтому мы перешли к подходу "только ioctl".В моем случае [который делал R/T обработку широковещательного качества hidef H. 264 video], мы смогли сделать обработку в драйвере и пространство приложения, а также пользовательская логика FPGA. Но мы использовали полный подход [не uio] драйвера, хотя, архитектурно это выглядело как uio в некоторых местах.
У нас были жесткие требования к надежности, предсказуемости R/T, гарантированной задержке. Нам пришлось обработать видео 60 кадров / секунду. Если мы сбегали, хотя бы на долю секунды, наши клиенты начинали кричать.uioне смог бы сделать этого для нас. Итак, вы начали с простого подхода. Но я могу сделать шаг назад. и посмотрите на требования, возможности устройства / ограничения, альтернативные способы получения непрерывных буферов, пропускную способность R/T и задержку, а также переоцените вещи. Действительно ли ваше текущее решение удовлетворяет все потребности? В настоящее время вы уже сталкиваетесь с горячими точками [гонки данных между приложением и устройством] и/или ограничениями. Или, может быть, вам будет лучше с родным водителем, который дает вам больше гибкости (т. е. может быть еще неизвестный, который заставит родного водителя).Вероятно, Ксилинкс предоставляет подходящий скелет полный драйвер в их SDK, который вы могли бы взломать довольно быстро.
Большое Спасибо за ваше время. Ваш ответ очень полезен для меня. Мне нравится идея управления буферами (буферами dma) от самого драйвера.
Как вы выяснили по исходному коду / dev/mem , Когда я использую mmap на области, которая была исключена с mem=512M из ядра, угрозы ядра являются памятью устройства и отключают кэширование.
Я нашел для этого промежуточное решение. Я удалил аргумент загрузки ядра и добавил зарезервированную область памяти в дереве устройств, например это:/ { reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; my_reserved: databuffer@20000000 { reg = <0x20000000 0x20000000>; }; }; };Делая это, я получаю системную память 0x00000000 - 0x3fffffff из cat /proc/iomem. cat / proc / meminfo дает моей свободной памяти всего 500 МБ, поэтому моя область не используется.
Когда я сейчас открываю /dev/mem и mmap эту область я получил около 260 МБ / сек от memcpy () и около 1200 МБ/сек от memset(). Область обрабатывается как память и кэшируется. Я не знаю, почему это только половина производительности области malloc , но гораздо лучше.
Я думаю, что идеальным решением для моего случая было бы что-то вроде лучшего /dev/mem, такого как драйвер устройства /dev/cma, который выделяет буферы из области cma, которую я определяю в bootargs. На этом устройстве я мог бы установить такие вещи, как кэш, политика согласованности через ioctl(). Это дало бы мне возможность самому устанавливать предпочтения в этой области.
Я нашел интересные сообщения по этому вопросу, как другие люди решили его. непрерывная память на ARM и кэш когерентность
Ранее в нем будет представлен драйвер устройства, который я сделал для той же цели, что и вы. обратиться.
Https://github.com/ikwzm/udmabuf
Udmabuf (буфер DMA, отображаемый в пользовательском пространстве)
Обзор
Введение удмабуфа
Udmabuf-это драйвер устройства Linux, который выделяет смежные блоки памяти в пространстве ядра в виде буферов DMA и делает их доступными из пространства пользователя. Предполагается, что эти блоки памяти используются в качестве DMA буферы, когда пользовательское приложение реализует драйвер устройства в пространстве пользователя с помощью UIO (User space I/O).Буфер DMA, выделенный udmabuf, можно получить из пользовательского пространства, открыв файл устройства (например, /dev / udmabuf0) и сопоставив его с пользовательским пространством памяти или используя функции read ()/write ().
Кэш процессора для выделенного буфера DMA можно отключить, установив флагO_SYNCпри открытии файла устройства. Кроме того, можно очистить или аннулировать кэш процессора при сохранении Кэш процессора включен. Физический адрес буфера DMA, выделенного udmabuf, можно получить, прочитав/sys/class/udmabuf/udmabuf0/phys_addr. Размер буфера DMA и младший номер устройства можно задать при загрузке драйвера устройства (например, при загрузке с помощью командыinsmod). Некоторые платформы позволяют указывать их в дереве устройств.Архитектура удмабуфа
Рис. 1. Архитектура
Поддерживается платформы
- OS: версия ядра Linux 3.6 - 3.8, 3.18, 4.4
(автор тестировал на 3.18 и 4.4).- процессор: Cortex-A9 процессор (фирмы Xilinx ZYNQ / Альтера CycloneV соц)
Comments