Как очистить кеш процессора для области адресного пространства в Linux?
Я заинтересован в очистке кеша (L1, L2 и L3) только для области адресного пространства, например, всех записей кэша от адреса A до адреса B. Существует ли механизм для этого в Linux, либо от пользователя или пространства ядра?
Ответы
Ответ 1
Проверьте эту страницу на список доступных методов очистки в ядре linux: https://www.kernel.org/doc/Documentation/cachetlb.txt
Кэш и TLB Flushing под Linux. Дэвид С. Миллер
Существует множество функций промывки диапазона
2) flush_cache_range(vma, start, end);
change_range_of_page_tables(mm, start, end);
flush_tlb_range(vma, start, end);
3) void flush_cache_range (struct vm_area_struct * vma, unsigned long start, unsigned long end)
Here we are flushing a specific range of (user) virtual
addresses from the cache. After running, there will be no
entries in the cache for 'vma->vm_mm' for virtual addresses in
the range 'start' to 'end-1'.
Вы также можете проверить реализацию функции - http://lxr.free-electrons.com/ident?a=sh;i=flush_cache_range
Например, в руке - http://lxr.free-electrons.com/source/arch/arm/mm/flush.c?a=sh&v=3.13#L67
67 void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
68 {
69 if (cache_is_vivt()) {
70 vivt_flush_cache_range(vma, start, end);
71 return;
72 }
73
74 if (cache_is_vipt_aliasing()) {
75 asm( "mcr p15, 0, %0, c7, c14, 0\n"
76 " mcr p15, 0, %0, c7, c10, 4"
77 :
78 : "r" (0)
79 : "cc");
80 }
81
82 if (vma->vm_flags & VM_EXEC)
83 __flush_icache_all();
84 }
Ответ 2
Это для ARM.
GCC предоставляет __builtin___clear_cache
, который делает syscall cacheflush
. Однако он может иметь предостережения.
Важное замечание здесь - Linux предоставляет системный вызов (специфичный для ARM) для очистки кешей. Вы можете проверить Android/Bionic flushcache для использования этого системного вызова. Однако я не уверен, какие гарантии предоставляет Linux, когда вы называете это, или как он реализуется посредством его внутренних действий.
Это сообщение в блоге Кэши и самомодифицирующийся код могут помочь в дальнейшем.
Ответ 3
В x86-версии Linux вы также можете найти функцию void clflush_cache_range(void *vaddr, unsigned int size)
, которая используется для очистки диапазона кеша. Эта функция зависит от CLFLUSH
или CLFLUSHOPT
инструкции. Я бы рекомендовал проверить, что ваш процессор фактически поддерживает их, потому что теоретически они являются необязательными.
CLFLUSHOPT
слабо упорядочен. CLFLUSH
изначально был указан как упорядоченный только MFENCE
, но все процессоры, которые его реализуют, делают это с сильным упорядочением по. пишет и другие инструкции CLFLUSH
. Intel решила добавить новую инструкцию (CLFLUSHOPT
) вместо изменения поведения CLFLUSH
и обновить руководство, чтобы гарантировать, что будущие процессоры будут реализовывать CLFLUSH
как сильно упорядоченное. Для этого используйте MFENCE
после использования либо, чтобы убедиться, что промывка выполняется до любых нагрузок из вашего теста (а не только магазинов).
На самом деле x86 предоставляет еще одну инструкцию, которая может быть полезна: CLWB
. CLWB
сбрасывает данные из кеша в память, не вызывая его, оставляя его чистым, но все же кэшированным.
Обратите также внимание на то, что эти инструкции являются когерентными. Их выполнение повлияет на все кеши всех процессоров (процессорных ядер) в системе.
Все эти три команды доступны в пользовательском режиме. Таким образом, вы можете использовать ассемблер и создать свой собственный void clflush_cache_range(void *vaddr, unsigned int size)
в своем приложении для пользовательского пространства (но не забудьте проверить их доступность до фактического использования).
Если я правильно понимаю, гораздо сложнее рассуждать о ARM в этом отношении. Семья ARM-процессоров намного менее последовательна, чем семейство процессоров IA-32. У вас может быть одна ARM с полнофункциональными кэшами, а другая - полностью без кешей. Кроме того, многие производители могут использовать индивидуальные MMU и MPU. Поэтому лучше рассуждать о какой-то конкретной модели процессора ARM.
К сожалению, похоже, что практически невозможно провести разумную оценку времени, необходимого для очистки некоторых данных. На это время влияет слишком много факторов, включая количество сброшенных строк кеша, неупорядоченное выполнение инструкций, состояние TLB (поскольку команда принимает виртуальный адрес в качестве аргумента, но кэши используют физические адреса), количество процессоров в системе, фактическая нагрузка с точки зрения операций с памятью на других процессорах в системе и сколько строк из диапазона фактически кэшируются процессорами и, наконец, производительность процессора, памяти, контроллера памяти и шины памяти. В результате я думаю, что время выполнения будет существенно различаться в разных средах и с разными нагрузками. Единственный разумный способ - измерить время промывки в системе и нагрузку, похожую на целевую систему.
И последнее замечание, не путайте тайники памяти и TLB. Они оба кэша, но организованы по-разному и служат различным целям. TLB кэширует совсем недавно используемые переводы между виртуальными и физическими адресами, но не данные, на которые указывают эти адреса.
И TLB не является когерентным, в отличие от кэшей памяти. Будьте осторожны, поскольку очистка записей TLB не приводит к потере соответствующих данных из кеша памяти.
Ответ 4
Несколько человек выразили опасения по поводу clear_cache
. Ниже приведен ручный процесс извлечения кэша, который неэффективен, но возможен из любой задачи пользовательского пространства (в любой ОС).
PLD/LDR
Можно вырезать кеши путем неправильного использования инструкции pld
. pld
отобразит строку кэша. Чтобы вытеснить определенный адрес памяти, вам нужно знать структуру ваших кешей. Например, cortex-a9 имеет 4-сторонний кэш данных с 8 словами в строке. Размер кеша настраивается в 16 КБ, 32 КБ или 64 КБ. Так что это 512, 1024 или 2048 строк. Пути всегда несущественны для младших адресных битов (поэтому последовательные адреса не конфликтуют). Таким образом, вы заполните новый способ, обратившись к memory offset + cache size / ways
. Так что каждый 4KB, 8KB и 16KB для коры-a9.
Использование ldr
в 'C' или 'С++' просто. Вам просто нужно правильно распределить массив и получить доступ к нему.
Смотрите: Программно получить размер строки кеша?
Например, если вы хотите вывести 0x12345, строка начинается с 0x12340, а для кеша с циклическим кэшем 16 КБ a pld
на 0x13340, 0x14340, 0x15340 и 0x16340 будет вытеснять любую форму значения таким образом. Тот же принцип может применяться для выселения L2 (который часто унифицирован). Итерирование по всему размеру кеша приведет к выселению всего кеша. Вы должны выделить неиспользуемую память размером кеша, чтобы исключить весь кеш. Это может быть довольно большим для L2. pld
не нужно использовать, но полный доступ к памяти (ldr/ldm
). Для нескольких процессоров (вытеснение кеш файлов) необходимо выполнить выключение на каждом CPU. Обычно L2 является глобальным для всех процессоров, поэтому его нужно запускать только один раз.
NB: Этот метод работает только с LRU (менее всего недавно используемым) или циклическим кэшем. Для псевдослучайной замены вам нужно будет написать/прочитать больше данных, чтобы обеспечить выселение, с точной суммой, имеющей высокую специфичность для процессора. Случайная замена ARM основана на LFSR, которая составляет от 8-33 бит в зависимости от процессора. Для некоторых процессоров по умолчанию используется циклический режим, а другие по умолчанию - псевдослучайный. Для нескольких процессоров конфигурация ядра Linux выберет режим. ref: CPU_CACHE_ROUND_ROBIN Однако для более новых процессоров Linux будет использовать значение по умолчанию от загрузчика и/или кремния. Другими словами, стоит попытаться получить вызовы clear_cache
OS для работы (см. Другие ответы), если вам нужно быть полностью общим, или вам придется потратить много времени, чтобы надежно очистить кеши.
Контекст swich
Можно обойти кеш, обманув ОС с помощью MMU на некоторых процессорах ARM и конкретных ОС. В системе * nix вам нужно несколько процессов. Вам нужно переключиться между процессами, и ОС должна очистить кеши. Как правило, это будет работать только на более старых ARM-процессорах (которые не поддерживают pld
), где ОС должна скрывать кеширование, чтобы избежать утечки информации между процессами. Он не переносится и требует, чтобы вы много разбирались в своей ОС.
Большинство явных регистров промывки кэша ограничены системным режимом для предотвращения атак типа отказа в обслуживании между процессами. Некоторые эксплойты могут пытаться получить информацию, видя, какие строки были вытеснены каким-либо другим процессом (это может дать информацию о том, к каким адресам обращается другой процесс). Эти атаки сложнее с псевдослучайной заменой.
Ответ 5
В x86 для очистки всей иерархии кеша вы можете использовать это
native_wbinvd()
Который определен в arch/x86/include/asm/special_insns.h. Если вы посмотрите на его реализацию, он просто вызывает инструкцию WBINVD
static inline void native_wbinvd(void)
{
asm volatile("wbinvd": : :"memory");
}
Обратите внимание, что для выполнения инструкции WBINVD X86 вам нужно находиться в привилегированном режиме. Это отличается от инструкции CLFLUSH x86, которая очищает одну строку кэша и не требует, чтобы вызывающий абонент находился в привилегированном режиме.
Если вы посмотрите на код ядра x86 Linux, вы увидите только несколько (6 мест, когда я пишу это) этой инструкции. Это потому, что это замедляет все объекты, работающие в этой системе. Представьте себе запуск этого на сервере с 100 МБ LLC. Эта инструкция будет означать перемещение всего 100+ МБ из кэша в ОЗУ. Кроме того, мне стало известно, что эта инструкция не прерывается. Таким образом, его использование может значительно повлиять на детерминизм системы RT, например,
(Хотя первоначальный вопрос спрашивает о том, как очистить определенный диапазон адресов, я подумал, что информация об очистке всей иерархии кэша также будет полезна для некоторых читателей)