summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/thread_info.h11
-rw-r--r--arch/arm/kernel/traps.c65
2 files changed, 68 insertions, 8 deletions
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 214d4158089a..7d77645128a8 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -43,6 +43,16 @@ struct cpu_context_save {
__u32 extra[2]; /* Xscale 'acc' register, etc */
};
+struct arm_restart_block {
+ union {
+ /* For user cache flushing */
+ struct {
+ unsigned long start;
+ unsigned long end;
+ } cache;
+ };
+};
+
/*
* low level task data that entry.S needs immediate access to.
* __switch_to() assumes cpu_context follows immediately after cpu_domain.
@@ -68,6 +78,7 @@ struct thread_info {
unsigned long thumbee_state; /* ThumbEE Handler Base register */
#endif
struct restart_block restart_block;
+ struct arm_restart_block arm_restart_block;
};
#define INIT_THREAD_INFO(tsk) \
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);
}
/*