]> Pileus Git - ~andy/linux/blobdiff - arch/arm/kernel/traps.c
Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm...
[~andy/linux] / arch / arm / kernel / traps.c
index ab517fcce21b8308dc957bc5b6809039c2e5f1fe..8fcda140358d94d6056ea200b79b50bb6e7bde81 100644 (file)
@@ -497,28 +497,64 @@ 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 flags)
+__do_cache_op(unsigned long start, unsigned long end)
 {
-       struct mm_struct *mm = current->active_mm;
-       struct vm_area_struct *vma;
+       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)
+{
        if (end < start || flags)
                return -EINVAL;
 
-       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 (!access_ok(VERIFY_READ, start, end - start))
+               return -EFAULT;
 
-               up_read(&mm->mmap_sem);
-               return flush_cache_user_range(start, end);
-       }
-       up_read(&mm->mmap_sem);
-       return -EINVAL;
+       return __do_cache_op(start, end);
 }
 
 /*