Вызов flush_cache_range() ядра Linux, похоже, ничего не делает

Введение:

У нас есть приложение, в котором Linux, работающий на ARM, принимает данные от внешнего процессора, который передает данные в пространство памяти ARM. Затем ARM необходимо получить доступ к этим данным из кода пользовательского режима.

Диапазон адресов должен быть физически непрерывным, так как механизм прямого доступа к памяти во внешнем процессоре не поддерживает разброс/сборку. Этот диапазон памяти изначально выделяется ядром ARM с помощью вызова __get_free_pages(GFP_KERNEL | __GFP_DMA,order), поскольку это гарантирует, что выделенная память будет физически непрерывной. Затем вызов virt_to_phys() возвращаемого указателя дает нам физический адрес, который затем предоставляется внешнему процессору в начале процесса.

Этот физический адрес также известен коду пользовательского режима Linux, который использует его (в пользовательском режиме) для вызова mmap() API для получения указателя пользовательского режима на эту область памяти. Затем наш драйвер ядра Linux видит соответствующий вызов своей подпрограммы mmap в структуре драйвера file_operations. Затем драйвер сохраняет указатель vm_area_struct "vma", который передается ему при вызове его подпрограммы mmap для последующего использования.

Когда код пользовательского режима получает сигнал о том, что новые данные были DMA переданы по этому адресу памяти, ему необходимо получить к ним доступ из пользовательского режима с помощью указателя пользовательского режима, который мы получили при вызове mmap(), упомянутом выше. Прежде чем код пользовательского режима сделает это, конечно, кеш, соответствующий этому диапазону памяти, должен быть очищен. Чтобы выполнить эту очистку, код пользовательского режима вызывает драйвер (через ioctl), а в режиме ядра выполняется вызов flush_cache_range():

flush_cache_range (vma, начало, конец);

Аргументы, переданные вышеприведенному вызову, — это «vma», захваченная драйвером при вызове его подпрограммы mmap, а «start» и «end» — это адреса пользовательского режима, переданные в драйвер из кода пользовательского режима в структуре, предоставленной для вызов ioctl().

Эта проблема:

Что мы видим, так это то, что буфер, кажется, не очищается, поскольку мы видим то, что кажется устаревшими данными при доступе из пользовательского режима. В качестве теста вместо того, чтобы получать адрес пользовательского режима из вызова mmap() для нашего драйвера, мы вместо этого вызываем API mmap() для /dev/mem. В этом случае мы получаем некэшированный доступ к буферу (сброс не нужен) и дальше все работает отлично.

Наша версия ядра — 3.8.3, и она работает на ARM 9. Есть ли логическая ошибка в подходе, который мы пытаемся использовать?

Спасибо!


person user1967844    schedule 04.06.2013    source источник
comment
Дополнительная подсказка заключается в том, что если вместо вызова flush_cache_range(vma,start,end); Я вызываю flush_cache_mm(vma-›vm_mm); тогда код работает отлично.   -  person user1967844    schedule 05.06.2013


Ответы (1)


У меня есть несколько вопросов, на которые я мог бы ответить: 1) Как вы используете «ФИЗИЧЕСКИЙ» адрес в вызове mmap()? mmap не должен иметь ничего общего с физическими адресами. 2) Что именно вы делаете, чтобы получить виртуальные адреса пользователей в вашем драйвере? 3) Как вы сопоставляете эти виртуальные адреса пользователей с физическими адресами и где вы это делаете? 4) Поскольку вы предварительно выделяете с помощью get_free_pages(), сопоставляете ли вы его с пространством ядра с помощью ioremap_cache()?

person Raghu    schedule 28.06.2013