【ARM中文社区微信群的一个问题讨论】用户空间如何操作cache?
本文是原「蜗窝讨论区」的历史存档(2017-08-24),来自版块「Linux kernel技术问答」,共 2 帖。讨论区已停止服务,此处仅供查阅。
问题:各位大神,怎么样手动invalidate arm v8的cache?内核里面,__inval_cache_range是刷新已经存在的映射,但这是内核API,想手动将应用程序的某部分Cache Invalidate掉,这个能实现吗?虽然armv8在用户态不鼓励cache maintanece,但是我们的场景比较特殊,具体是这样的:我们在将加速器的一部分DMA地址直接映射到用户态,并且在用户态直接操作加速器硬件。在硬件操作完毕DMA后,我们需要将相应的虚拟地址空间invalidate。目前我们已经基本实现功能,在向DMA写入数据后,调用__clear_cache实现了回写,唯一的问题是硬件写入DMA后,没有找到invalidate的地方。
讨论过程: 1、如何保证coherence? 这里的场景基本上是想用cacheable的dma,mapping到用户空间, 然后自己在用户空间使用cache maintanence 来保证coherence?不过,gcc的build-in函数: void __builtinclearcache (char begin, char end) ARMv8下面最终会调用aarch64_sync_cache_range,而这个函数定义如下: void __aarch64_sync_cache_range (const void base, const void end) { unsigned icache_lsize; unsigned dcache_lsize; static unsigned int cache_info = 0; const char *address;
if (! cache_info) / CTR_EL0 [3:0] contains log2 of icache line size in words. CTR_EL0 [19:16] contains log2 of dcache line size in words. / asm volatile ("mrs\t%0, ctr_el0":"=r" (cache_info));
icache_lsize = 4 << (cache_info & 0xF); dcache_lsize = 4 << ((cache_info >> 16) & 0xF);
/ Loop over the address range, clearing one cache line at once. Data cache must be flushed to unification first to make sure the instruction cache fetches the updated data. 'end' is exclusive, as per the GNU definition of __clear_cache. /
/ Make the start address of the loop cache aligned. / address = (const char*) ((UINTPTR_TYPE) base & ~ (UINTPTR_TYPE) (dcache_lsize - 1));
for (; address < (const char *) end; address += dcache_lsize) asm volatile ("dc\tcvau, %0" : : "r" (address) : "memory"); asm volatile ("dsb\tish" : : : "memory");
/ Make the start address of the loop cache aligned. / address = (const char*) ((UINTPTR_TYPE) base & ~ (UINTPTR_TYPE) (icache_lsize - 1));
for (; address < (const char *) end; address += icache_lsize) asm volatile ("ic\tivau, %0" : : "r" (address) : "memory");
asm volatile ("dsb\tish; isb" : : : "memory"); } 显然,这里的cache 操作只是到PoU, 不是到PoC. 最终不一定到memory。因此,clear_cache 不能用于保证dma coherent,只能适用于self-modifying code。
2、用户态自己实现怎么样? 如果考虑在user space使用cache maintanence指令,不过这种方法有限制: A、在EL0,不是所有的cache maintanence指令都能用 B、 如果用户态aarch32,就不能用了
3、把用户空间的虚拟地址和size传递到内核,由内核调用__inval_cache_range可不可以? 让kernel去执行flush/invalidate用户空间虚拟地址的操作是行不通的, 问题在于__flush_dcache_area()只接受一个对于kernel合法的虚拟地址。
4、怎么优雅的解决目前的困境? 其实这个问题在kernel没有一个有效的方案,DMA-APIs不愿去提供这样的一个机制,是由于各个体系结构的差异。就拿ARM cache来说 就有VIPT, PIPT(cortex-A53), 在一个体系结构下有效 的方式,很难扩展应用到其他体系结构下, 所以就不能被合并到kernel source code中去
5、性能方面的考虑 其实可以仔细思考这个问题:使用cacheable的buffer真的能解决性能瓶颈么?这可能取决于application如何使用这些data,以及data的规模。试图缓存很大的一块buffer会不会污染CPU cache,进而有其他的系统影响?
