summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel/traps.c
diff options
context:
space:
mode:
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 cab094c234e..4d268d912b0 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);
}
/*