ARM cacheflush syscall with range that spans multiple vma
Why does the ARM cacheflush syscall stop after the lowest vma which intersects the user-requested range? The range could span more than one vma having contiguous addresses, such as two files MAP_SHARED into adjacent pages; or even a region that contains holes (pages not present.)
The code path in arch/arm/kernel/traps.c is:
arm_syscall():
case NR(cacheflush):
return do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);do_cache_op() contains no loop for more than one vma:
vma = find_vma(mm, start);
Quoted 5 lines · John Reiser
Why does the ARM cacheflush syscall stop after the lowest vma which intersects the user-requested range? The range could span more than one vma having contiguous addresses, such as two files
MAP_SHAREDinto adjacent pages; or even a region that contains holes (pages not present.)
Because you're not supposed to use it on large ranges because it's an expensive operation.
Quoted 9 lines · Russell King - ARM Linux
Quoted 5 lines · John Reiser
Why does the ARM cacheflush syscall stop after the lowest vma which intersects the user-requested range? The range could span more than one vma having contiguous addresses, such as two files
MAP_SHAREDinto adjacent pages; or even a region that contains holes (pages not present.)Because you're not supposed to use it on large ranges because it's an expensive operation.
I posted some patches to address this recently. Obviously it's still expensive, but it makes the syscall restartable so that you can't DoS the system.
git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git cacheflush
There's WIP code there for a new iovec-based syscall too.
Will
Quoted 11 lines · Will Deacon
Quoted 9 lines · Russell King - ARM Linux
Quoted 5 lines · John Reiser
Why does the ARM cacheflush syscall stop after the lowest vma which intersects the user-requested range? The range could span more than one vma having contiguous addresses, such as two files
MAP_SHAREDinto adjacent pages; or even a region that contains holes (pages not present.)Because you're not supposed to use it on large ranges because it's an expensive operation.
Quoted 7 lines
I posted some patches to address this recently. Obviously it's still expensive, but it makes the syscall restartable so that you can't DoS the system.
git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.gitcacheflushThere's WIP code there for a new iovec-based syscall too.
Please merge those patches soon.
My "app" is user-mode execve() of a compressed ET_EXEC, so UPX must flush all of the re-generated .text, which can be a megabyte or more. Thus I flush one page per syscall, or write all of .text to a temporary file (achieves cache flush because DMA accesses only memory, not cache), or heuristically flush by "sweeping" 1/2 MB of consecutive words (thus generating deliberate collisions and evictions.) Each of those sucks.
It is *EXTREMELY* discouraging that cacheflush() misbehaves so badly. *PLEASE* return an error status when you decide not to honor the API!
Quoted 29 lines · John Reiser
Quoted 11 lines · Will Deacon
Quoted 9 lines · Russell King - ARM Linux
Quoted 5 lines · John Reiser
Why does the ARM cacheflush syscall stop after the lowest vma which intersects the user-requested range? The range could span more than one vma having contiguous addresses, such as two files
MAP_SHAREDinto adjacent pages; or even a region that contains holes (pages not present.)Because you're not supposed to use it on large ranges because it's an expensive operation.
Quoted 7 lines
I posted some patches to address this recently. Obviously it's still expensive, but it makes the syscall restartable so that you can't DoS the system.
git://git.kernel.org/pub/scm/linux/kernel/git/will/linux.gitcacheflushThere's WIP code there for a new iovec-based syscall too.
Please merge those patches soon.
My "app" is user-mode
execve()of a compressedET_EXEC, so UPX must flush all of the re-generated .text, which can be a megabyte or more. Thus I flush one page per syscall, or write all of .text to a temporary file (achieves cache flush because DMA accesses only memory, not cache), or heuristically flush by "sweeping" 1/2 MB of consecutive words (thus generating deliberate collisions and evictions.) Each of those sucks.
Quoted 2 lines
It is *EXTREMELY* discouraging that
cacheflush()misbehaves so badly. *PLEASE* return an error status when you decide not to honor the API!
So what, you're arranging for your memory to exist as a set of contiguous but separate mappings of one page each? Surely not.
You should be able to cover a complete mapping in one go. If the function stops because a page is not present and _can_ be populated, that is a bug. If it stops because a page is not present and that page can't be populated, then it's working as it should.
Quoted 9 lines · Russell King - ARM Linux
Quoted 5 lines · John Reiser
Why does the ARM cacheflush syscall stop after the lowest vma which intersects the user-requested range? The range could span more than one vma having contiguous addresses, such as two files
MAP_SHAREDinto adjacent pages; or even a region that contains holes (pages not present.)Because you're not supposed to use it on large ranges because it's an expensive operation.
FWIW: here's a simple test case which can really affect responsiveness on my TC2 (2GB of memory). It just creates a single VMA, doesn't bother faulting it in, then tries to cacheflush the whole range. On a kernel with CONFIG_PREEMPT_NONE=y, this effectively stalls the system (interrupts are still taken) until the flush has completed.
Will
--->8
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE 0x70000000
#define NR_cacheflush 0xf0002
int main(void)
{
int ret;
char *region = malloc(BUFSIZE);
if (!region) {
fprintf(stderr, "Failed to allocate %u-byte buffer\n", BUFSIZE);
return -1;
}
ret = syscall(NR_cacheflush, region, region + BUFSIZE, 0);
if (ret)
printf("syscall returned %d\n", ret);
return ret;
}