Come svuotare la cache della CPU per un’area di spazio indirizzo in Linux?

Sono interessato a svuotare la cache (L1, L2 e L3) solo per un’area di spazio indirizzo, ad esempio tutte le voci della cache dall’indirizzo A all’indirizzo B. Esiste un meccanismo per farlo in Linux, sia dall’utente che dallo spazio del kernel ?

Controlla questa pagina per un elenco dei metodi di lavaggio disponibili nel kernel di Linux: https://www.kernel.org/doc/Documentation/cachetlb.txt

Cache e TLB Flushing sotto Linux. David S. Miller

Sono disponibili funzioni di risciacquo dell’intervallo

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'. 

Puoi anche verificare l’implementazione della funzione – http://lxr.free-electrons.com/ident?a=sh;i=flush_cache_range

Ad esempio, in arm – 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 } 

Questo è per ARM.

GCC fornisce __builtin___clear_cache che dovrebbe fare syscall cacheflush . Tuttavia può avere i suoi avvertimenti .

La cosa importante qui è che Linux fornisce una chiamata di sistema (specifica ARM) per svuotare le cache. Puoi controllare Android / Bionic flushcache per sapere come utilizzare questa chiamata di sistema. Tuttavia non sono sicuro di quale tipo di garanzie dà Linux quando lo chiami o come viene implementato attraverso il suo funzionamento interno.

Questo post sul blog Cache e codice auto-modificante può aiutare ulteriormente.

Nella versione x86 di Linux è anche ansible trovare una funzione void clflush_cache_range(void *vaddr, unsigned int size) che viene utilizzata allo scopo di svuotare un intervallo di cache. Questa funzione si basa CLFLUSHOPT istruzioni CLFLUSH o CLFLUSHOPT . Ti consiglio di controllare che il tuo processore li supporti effettivamente, perché in teoria sono facoltativi.

CLFLUSHOPT è debolmente ordinato. CLFLUSH stato originariamente specificato come ordinato solo da MFENCE , ma tutte le CPU che lo implementano lo fanno con un forte ordering wrt. scrive e altre istruzioni CLFLUSH . Intel ha deciso di aggiungere una nuova istruzione ( CLFLUSHOPT ) invece di modificare il comportamento di CLFLUSH e di aggiornare il manuale per garantire che le future CPU implementino CLFLUSH come fortemente ordinato. Per questo uso, dovresti usare MFENCE dopo MFENCE usato, per assicurarti che lo scarico avvenga prima di qualsiasi carico dal tuo benchmark (non solo i negozi).

In realtà x86 fornisce un’altra istruzione che potrebbe essere utile: CLWB . CLWB dati dalla cache alla memoria senza rimuoverli, lasciandoli puliti ma ancora memorizzati nella cache.

Si noti inoltre che queste istruzioni sono coerenti con la cache. La loro esecuzione interesserà tutte le cache di tutti i processori (core del processore) nel sistema.

Tutte queste tre istruzioni sono disponibili in modalità utente. Pertanto, è ansible utilizzare l’assemblatore e creare il proprio file void clflush_cache_range(void *vaddr, unsigned int size) nell’applicazione dello spazio utente (ma non dimenticare di verificare la disponibilità prima dell’utilizzo effettivo).


Se capisco correttamente, è molto più difficile ragionare su ARM in questo senso. La famiglia di processori ARM è molto meno coerente della famiglia di processori IA-32. Puoi avere un ARM con cache complete e un altro completamente senza cache. Inoltre, molti produttori possono utilizzare MMU e MPU personalizzate. Quindi è meglio ragionare su un particolare modello di processore ARM.

Sfortunatamente, sembra quasi imansible eseguire una stima ragionevole del tempo necessario per svuotare alcuni dati. Questa volta è influenzata da troppi fattori tra cui il numero di linee di cache scaricate, l’esecuzione non ordinata delle istruzioni, lo stato di TLB (perché l’istruzione prende un indirizzo virtuale come argomento, ma le cache utilizzano gli indirizzi fisici), il numero di CPU nel sistema, carico effettivo in termini di operazioni di memoria sugli altri processori nel sistema e quante righe dell’intervallo vengono effettivamente memorizzate nella cache dai processori e infine dalle prestazioni della CPU, della memoria, del controller di memoria e del bus di memoria. In un risultato, penso che il tempo di esecuzione varierà in modo significativo in diversi ambienti e con carichi diversi. L’unico modo ragionevole è misurare il tempo di irrigazione sul sistema e con un carico simile al sistema di destinazione.


E nota finale, non confondere cache di memoria e TLB. Sono entrambi cache, ma organizzati in modi diversi e al servizio di scopi diversi. TLB memorizza nella cache solo le traduzioni utilizzate più di recente tra indirizzi virtuali e fisici, ma non i dati indicati da tali indirizzi.

E TLB non è coerente, in contrasto con le cache di memoria. Fare attenzione, poiché lo svuotamento delle voci TLB non comporta il flussaggio dei dati appropriati dalla memoria cache.

Diverse persone hanno express dubbi su clear_cache . Di seguito è riportato un processo manuale per rimuovere la cache che è inefficiente, ma ansible da qualsiasi attività dello spazio utente (in qualsiasi sistema operativo).


PLD / LDR

E ‘ansible sfrattare le cache mettendo in errore l’istruzione pld . Il pld recupererà una linea di cache. Per rimuovere uno specifico indirizzo di memoria, è necessario conoscere la struttura delle cache. Ad esempio, una corteccia-a9 ha una cache di dati a 4 vie con 8 parole per linea. La dimensione della cache è configurabile in 16 KB, 32 KB o 64 KB. Quindi questo è 512, 1024 o 2048 linee. Le strade sono sempre insignificanti per i bit di indirizzo più bassi (quindi gli indirizzi sequenziali non sono in conflitto). Quindi riempirai un nuovo modo accedendo allo memory offset + cache size / ways . Quindi questo è ogni 4KB, 8KB e 16KB per una corteccia-a9.

Usare ldr in ‘C’ o ‘C ++’ è semplice. Hai solo bisogno di dimensionare un array in modo appropriato e accedervi.

Vedi: Prendi a livello di codice la dimensione della linea della cache?

Ad esempio, se si desidera rimuovere 0x12345, la riga inizia da 0x12340 e per una cache round robin da 16 KB un pld su 0x13340 , 0x14340 , 0x15340 e 0x16340 eliminerebbe qualsiasi forma di valore in questo modo. Lo stesso principio può essere applicato per sfrattare L2 (che è spesso unificato). L’iterazione su tutte le dimensioni della cache eliminerà l’intera cache. È necessario allocare una memoria non utilizzata della dimensione della cache per sfrattare l’intera cache. Questo potrebbe essere abbastanza grande per la L2. non è necessario utilizzare ldr/ldm , ma un accesso completo alla memoria ( ldr/ldm ). Per più CPU (evacuazione cache filettata) è necessario eseguire lo sfratto su ogni CPU. Solitamente L2 è globale per tutte le CPU, quindi deve essere eseguito solo una volta.

NB: questo metodo funziona solo con cache LRU (utilizzate meno di recente) o round-robin . Per la sostituzione pseudo-casuale, dovrai scrivere / leggere più dati per garantire lo sfratto, con un importo esatto che è altamente specifico della CPU. La sostituzione casuale di ARM si basa su un LFSR che va da 8-33 bit a seconda della CPU. Per alcune CPU, ha come valore predefinito round-robin e altri default per la modalità pseudo-casuale . Per alcune CPU una configurazione del kernel Linux selezionerà la modalità. ref: CPU_CACHE_ROUND_ROBIN Tuttavia, per le CPU più recenti, Linux utilizzerà l’impostazione predefinita dal boot loader e / o dal silicio. In altre parole, vale la pena tentare di ottenere chiamate del sistema operativo clear_cache per funzionare (vedere altre risposte) se è necessario essere completamente generici o si dovrà dedicare molto tempo a cancellare le cache in modo affidabile.

Contesto

È ansible aggirare la cache ingannando un sistema operativo utilizzando la MMU su alcune CPU ARM e sistemi operativi specifici. Su un sistema * nix, sono necessari più processi. È necessario passare da un processo all’altro e il sistema operativo deve svuotare le cache. In genere questo funziona solo su CPU ARM meno recenti (quelle che non supportano pld ) in cui il sistema operativo deve svuotare le cache per garantire che non vi siano perdite di informazioni tra i processi. Non è portatile e richiede che tu capisca molto del tuo sistema operativo.

I registri di svuotamento della cache più espliciti sono limitati alla modalità di sistema per impedire attacchi di tipo Denial of Service tra processi. Alcuni exploit possono tentare di ottenere informazioni vedendo quali linee sono state sfrattate da qualche altro processo (questo può dare informazioni su quali indirizzi sta accedendo a un altro processo). Questi attacchi sono più difficili con la sostituzione pseudo-casuale.