summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/traps.c
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2013-05-13 15:21:49 +0100
committerWill Deacon <will.deacon@arm.com>2013-08-20 11:54:53 +0100
commit28256d612726a28a8b9d3c49f2b74198c4423d6a (patch)
treea64cd498ec1e9f4d34f77bcc827a541f19099bce /arch/arm/kernel/traps.c
parent377747c40657eb35ad98a56439606d96a928425a (diff)
downloadlinux-28256d612726a28a8b9d3c49f2b74198c4423d6a.tar.gz
linux-28256d612726a28a8b9d3c49f2b74198c4423d6a.tar.bz2
linux-28256d612726a28a8b9d3c49f2b74198c4423d6a.zip
ARM: cacheflush: split user cache-flushing into interruptible chunks
Flushing a large, non-faulting VMA from userspace can potentially result in a long time spent flushing the cache line-by-line without preemption occurring (in the case of CONFIG_PREEMPT=n). Whilst this doesn't affect the stability of the system, it can certainly affect the responsiveness and CPU availability for other tasks. This patch splits up the user cacheflush code so that it flushes in chunks of a page. After each chunk has been flushed, we may reschedule if appropriate and, before processing the next chunk, we allow any pending signals to be handled before resuming from where we left off. Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm/kernel/traps.c')
-rw-r--r--arch/arm/kernel/traps.c65
1 files changed, 57 insertions, 8 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index cab094c234ee..4d268d912b0e 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -499,6 +499,54 @@ static int bad_syscall(int n, struct pt_regs *regs)
return regs->ARM_r0;
}
+static long do_cache_op_restart(struct restart_block *);
+
+static inline int
+__do_cache_op(unsigned long start, unsigned long end)
+{
+ int ret;
+ unsigned long chunk = PAGE_SIZE;
+
+ do {
+ if (signal_pending(current)) {
+ struct thread_info *ti = current_thread_info();
+
+ ti->restart_block = (struct restart_block) {
+ .fn = do_cache_op_restart,
+ };
+
+ ti->arm_restart_block = (struct arm_restart_block) {
+ {
+ .cache = {
+ .start = start,
+ .end = end,
+ },
+ },
+ };
+
+ return -ERESTART_RESTARTBLOCK;
+ }
+
+ ret = flush_cache_user_range(start, start + chunk);
+ if (ret)
+ return ret;
+
+ cond_resched();
+ start += chunk;
+ } while (start < end);
+
+ return 0;
+}
+
+static long do_cache_op_restart(struct restart_block *unused)
+{
+ struct arm_restart_block *restart_block;
+
+ restart_block = &current_thread_info()->arm_restart_block;
+ return __do_cache_op(restart_block->cache.start,
+ restart_block->cache.end);
+}
+
static inline int
do_cache_op(unsigned long start, unsigned long end, int flags)
{
@@ -510,17 +558,18 @@ do_cache_op(unsigned long start, unsigned long end, int flags)
down_read(&mm->mmap_sem);
vma = find_vma(mm, start);
- if (vma && vma->vm_start < end) {
- if (start < vma->vm_start)
- start = vma->vm_start;
- if (end > vma->vm_end)
- end = vma->vm_end;
-
+ if (!vma || vma->vm_start >= end) {
up_read(&mm->mmap_sem);
- return flush_cache_user_range(start, end);
+ return -EINVAL;
}
+
+ if (start < vma->vm_start)
+ start = vma->vm_start;
+ if (end > vma->vm_end)
+ end = vma->vm_end;
up_read(&mm->mmap_sem);
- return -EINVAL;
+
+ return __do_cache_op(start, end);
}
/*