This patch adds new system call m[no]volatile.
If someone asks is_volatile system call, it could be added, too.
+/*
+ * Return -EINVAL if range doesn't include a right vma at all.
+ * Return -ENOMEM with interrupting range opeartion if memory is not enough to
+ * merge/split vmas.
+ * Return 0 if range consists of only proper vmas.
+ * Return 1 if part of range includes inavlid area(ex, hole/huge/ksm/mlock/
+ * special area)
+ */
+SYSCALL_DEFINE2(mvolatile, unsigned long, start, size_t, len)
+{
+ unsigned long end, tmp;
+ struct vm_area_struct *vma, *prev;
+ bool invalid = false;
+ int error = -EINVAL;
+
+ down_write(¤t->mm->mmap_sem);
+ if (start & ~PAGE_MASK)
+ goto out;
+
+ len &= PAGE_MASK;
+ if (!len)
+ goto out;
+
+ end = start + len;
+ if (end < start)
+ goto out;
+
+ vma = find_vma_prev(current->mm, start, &prev);
+ if (!vma)
+ goto out;
+
+ if (start > vma->vm_start)
+ prev = vma;
+
+ for (;;) {
+ /* Here start < (end|vma->vm_end). */
+ if (start < vma->vm_start) {
+ start = vma->vm_start;
+ if (start >= end)
+ goto out;
+ invalid = true;
+ }
+
+ /* Here vma->vm_start <= start < (end|vma->vm_end) */
+ tmp = vma->vm_end;
+ if (end < tmp)
+ tmp = end;
+
+ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
+ error = do_mvolatile(vma, &prev, start, tmp);
+ if (error == -ENOMEM) {
+ up_write(¤t->mm->mmap_sem);
+ return error;
+ }
+ if (error == -EINVAL)
+ invalid = true;
+ else
+ error = 0;
+ start = tmp;
+ if (prev && start < prev->vm_end)
+ start = prev->vm_end;
+ if (start >= end)
+ break;
+
+ vma = prev->vm_next;
+ if (!vma)
+ break;
+ }
+out:
+ up_write(¤t->mm->mmap_sem);
+ return invalid ? 1 : 0;
+}
+/*
+ * Return -ENOMEM with interrupting range opeartion if memory is not enough
+ * to merge/split vmas.
+ * Return 1 if part of range includes purged's one, otherwise, return 0
+ */
+SYSCALL_DEFINE2(mnovolatile, unsigned long, start, size_t, len)
+{
+ unsigned long end, tmp;
+ struct vm_area_struct *vma, *prev;
+ int ret, error = -EINVAL;
+ bool is_purged = false;
+
+ down_write(¤t->mm->mmap_sem);
+ if (start & ~PAGE_MASK)
+ goto out;
+
+ len &= PAGE_MASK;
+ if (!len)
+ goto out;
+
+ end = start + len;
+ if (end < start)
+ goto out;
+
+ vma = find_vma_prev(current->mm, start, &prev);
+ if (!vma)
+ goto out;
+
+ if (start > vma->vm_start)
+ prev = vma;
+
+ for (;;) {
+ /* Here start < (end|vma->vm_end). */
+ if (start < vma->vm_start) {
+ start = vma->vm_start;
+ if (start >= end)
+ goto out;
+ }
+
+ /* Here vma->vm_start <= start < (end|vma->vm_end) */
+ tmp = vma->vm_end;
+ if (end < tmp)
+ tmp = end;
+
+ /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
+ error = do_mnovolatile(vma, &prev, start, tmp, &is_purged);
+ if (error) {
+ WARN_ON(error != -ENOMEM);
+ goto out;
+ }
+ start = tmp;
+ if (prev && start < prev->vm_end)
+ start = prev->vm_end;
+ if (start >= end)
+ break;
+
+ vma = prev->vm_next;
+ if (!vma)
+ break;
+ }