Нужна помощь с отображением заранее зарезервированных ** кэшируемых ** буфер DMA на Xilinx/ARM SoC (Zynq 7000)
У меня есть плата на базе Xilinx Zynq 7000 с периферийным устройством в матрице ПЛИС с поддержкой DMA (на шине AXI). Мы разработали схему и работаем под Linux на ядрах ARM. У нас проблемы с производительностью при доступе к буферу DMA из пространства пользователя после того, как он был заполнен аппаратным обеспечением.
Резюме:
Мы предварительно зарезервировали во время загрузки раздел DRAM для использования в качестве большого буфера DMA. Очевидно, мы используем неправильные API для отображения этого буфера, потому что он, кажется, не кэшируется, а скорость доступа ужасна.
Использование его даже в качестве bounce-буфера неоправданно медленно из-за ужасной производительности. IIUC, ARM-кеши не связаны с DMA, поэтому я был бы очень признателен за понимание того, как сделать следующее:
- Сопоставьте регион DRAM с виртуальным адресным пространством ядра, но убедитесь, что он кешируется.
- Убедитесь, что сопоставление его с пользовательским пространством также не имеет нежелательного эффекта, даже если для этого требуется, чтобы мы предоставили вызов mmap нашим собственным драйвером.
- Явно лишает законной силы область физической памяти из иерархии кэша перед выполнением прямого доступа к памяти, чтобы гарантировать согласованность.
Больше информации:
Я пытался тщательно изучить это, прежде чем спрашивать. К сожалению, поскольку это ARM SoC/FPGA, информации по этому вопросу очень мало, поэтому я должен обратиться к экспертам напрямую.
Поскольку это SoC, многие вещи жестко запрограммированы для u-boot. Например, ядро и виртуальный диск загружаются в определенные места в DRAM перед передачей управления ядру. Мы воспользовались этим, чтобы зарезервировать раздел DRAM размером 64 МБ для буфера DMA (он должен быть таким большим, поэтому мы предварительно зарезервировали его). Не нужно беспокоиться о конфликте типов памяти или о том, что ядро "топает" эту память, потому что параметры загрузки сообщают ядру, над какой областью DRAM он контролирует.
Первоначально мы пытались отобразить этот физический диапазон адресов в пространство ядра, используя ioremap, но это, кажется, помечает область, которую нельзя кэшировать, и скорость доступа ужасна, даже если мы пытаемся использовать memcpy, чтобы сделать его буфером отказов. Мы используем /dev/mem, чтобы отобразить это также в пользовательское пространство, и я установил, что memcpy составляет около 70 МБ / с.
Судя по значительному количеству поисков по этой теме, кажется, что хотя половина людей хотят использовать ioremap, подобный этому (что, вероятно, откуда мы взяли идею), ioremap не должен использоваться для этой цели, и что есть DMA-связанные API, которые следует использовать вместо этого. К сожалению, похоже, что распределение буфера DMA является полностью динамическим, и я не понял, как сказать, "вот физический адрес уже выделен - используйте это".
Один документ, на который я смотрел, это тот, но он слишком x86 и ориентирован на ПК: https://www.kernel.org/doc/Documentation/DMA-API-HOWTO.txt
И этот вопрос также стоит в начале моих поисков, но нет реального ответа: получить физический адрес буфера в Linux
Глядя на стандартные вызовы, dma_set_mask_and_coherent и family не будут брать заранее определенный адрес и хотят структуру устройства для PCI. У меня нет такой структуры, потому что это ARM SoC без PCI. Я мог бы вручную заполнить такую структуру, но мне кажется, что я злоупотребляю API, а не использую его по назначению.
Кстати: это кольцевой буфер, в котором мы DMA блоки данных с различными смещениями, но мы выравниваем по границам строк кэша, поэтому нет риска ложного совместного использования.
Большое спасибо за любую помощь, которую вы можете оказать!
ОБНОВЛЕНИЕ: Кажется, что нет такой вещи как кешируемый буфер DMA на ARM, если вы делаете это обычным способом. Возможно, если я не сделаю вызов ioremap, регион не будет помечен как не кэшируемый, но тогда мне придется выяснить, как сделать управление кэшем в ARM, что я не могу понять. Одна из проблем заключается в том, что memcpy в пользовательском пространстве кажется действительно плохим. Есть ли реализация memcpy, оптимизированная для некэшируемой памяти, которую я могу использовать? Может быть, я мог бы написать один. Я должен выяснить, есть ли в этом процессоре Neon.
4 ответа
Вы пытались реализовать свое собственное устройство с mmap()
метод переназначения вашего буфера как кешируемый (с помощью remap_pfn_range()
)?
Я боролся с этим некоторое время с
udmabuf
и обнаружил, что ответ был таким же простым, как добавление
dma_coherent;
к его записи в дереве устройств. Я увидел резкое ускорение времени доступа благодаря этому простому шагу, хотя мне все еще нужно добавить код для аннулирования/сброса всякий раз, когда я передаю право собственности с/на устройство.
Zynq имеет неоновые инструкции, а реализация кода сборки memcpy с использованием неоновых инструкций с использованием выравнивания по границе кэша (32 байта) позволит достичь скорости 300 МБ / с или выше.
Я считаю, что вам нужен драйвер, который реализует mmap(), если вы хотите, чтобы отображение кэшировалось.
Для этого мы используем два драйвера устройства: portalmem и zynqportal. В проекте Connectal мы называем связь между программным обеспечением пространства пользователя и логикой FPGA "порталом". Эти драйверы требуют dma-buf, который был стабильным для нас начиная с версии ядра Linux 3.8.x.
Драйвер portalmem предоставляет ioctl для выделения подсчитанного фрагмента памяти и возвращает файловый дескриптор, связанный с этой памятью. Этот драйвер реализует совместное использование dma-buf. Он также реализует mmap(), чтобы приложения пользовательского пространства могли получить доступ к памяти.
Во время выделения приложение может выбрать кэшированное или некэшированное отображение памяти. На x86 отображение всегда кэшируется. Наша реализация mmap() в настоящее время начинается со строки 173 драйвера portalmem. Если отображение не кэшировано, оно изменяет vma-> vm_page_prot с помощью pgprot_writecombine (), включая буферизацию записей, но отключая кэширование.
Драйвер portalmem также предоставляет ioctl для аннулирования и при необходимости записи строк кэша данных.
Драйвер portalmem не знает о FPGA. Для этого мы используем драйвер zynqportal, который предоставляет ioctl для передачи таблицы перевода в FPGA, чтобы мы могли использовать логически смежные адреса на FPGA и преобразовывать их в фактические адреса DMA. Схема распределения, используемая portalmem, предназначена для создания компактных таблиц перевода.
Мы используем тот же драйвер portalmem с pcieportal для FPGA, подключенных к PCI Express, без изменения программного обеспечения пользователя.