@@ -26,6 +26,7 @@
#include <linux/hardirq.h>
#include <linux/task_work.h>
#include <linux/ima.h>
+#include <linux/vrange.h>
#include <linux/atomic.h>
@@ -244,6 +245,10 @@ static void __fput(struct file *file)
file->f_op->fasync(-1, file, 0);
}
ima_file_free(file);
+
+ /* drop all vranges on last close */
+ vrange_root_cleanup(&inode->i_mapping->vroot);
+
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);
security_file_free(file);
@@ -17,6 +17,7 @@
#include <linux/prefetch.h>
#include <linux/buffer_head.h> /* for inode_has_buffers */
#include <linux/ratelimit.h>
+#include <linux/vrange.h>
#include "internal.h"
/*
@@ -350,6 +351,7 @@ void address_space_init_once(struct address_space *mapping)
spin_lock_init(&mapping->private_lock);
mapping->i_mmap = RB_ROOT;
INIT_LIST_HEAD(&mapping->i_mmap_nonlinear);
+ vrange_root_init(&mapping->vroot, VRANGE_FILE);
}
EXPORT_SYMBOL(address_space_init_once);
@@ -27,6 +27,7 @@
#include <linux/lockdep.h>
#include <linux/percpu-rwsem.h>
#include <linux/blk_types.h>
+#include <linux/vrange_types.h>
#include <asm/byteorder.h>
#include <uapi/linux/fs.h>
@@ -411,6 +412,7 @@ struct address_space {
struct rb_root i_mmap; /* tree of private and shared mappings */
struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
struct mutex i_mmap_mutex; /* protect tree, count, list */
+ struct vrange_root vroot;
/* Protected by tree_lock together with the radix tree */
unsigned long nrpages; /* number of total pages */
pgoff_t writeback_index;/* writeback starts here */
@@ -3,6 +3,7 @@
#include <linux/vrange_types.h>
#include <linux/mm.h>
+#include <linux/fs.h>
#define vrange_entry(ptr) \
container_of(ptr, struct vrange, node.rb)
@@ -69,4 +69,6 @@
#define VRANGE_VOLATILE 0 /* unpin pages so VM can discard them */
#define VRANGE_NONVOLATILE 1 /* pin pages so VM can't discard them */
+#define VRANGE_MODE_SHARED 0x1 /* Volatility is shared on files */
+
#endif /* __ASM_GENERIC_MMAN_COMMON_H */
@@ -6,6 +6,7 @@
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/mman.h>
+#include <linux/file.h>
static struct kmem_cache *vrange_cachep;
@@ -235,6 +236,75 @@ static int vrange_private(struct mm_struct *mm,
return end-start;
}
+static int vrange_shared(struct mm_struct *mm,
+ unsigned long start, unsigned long end,
+ int mode, int *purged)
+{
+ struct vm_area_struct *vma;
+ int count = 0, ret = 0, orig_start = start;
+
+ down_write(&mm->mmap_sem);
+
+ vma = find_vma(mm, start);
+ for (;;) {
+ struct vrange_root *vroot;
+ unsigned long tmp, vstart, vend;
+
+ if (!vma)
+ goto out;
+
+ /* make sure start is at the front of the current vma*/
+ if (start < vma->vm_start) {
+ start = vma->vm_start;
+ if (start >= end)
+ goto out;
+ }
+
+ /* bound tmp to closer of vm_end & end */
+ tmp = vma->vm_end;
+ if (end < tmp)
+ tmp = end;
+
+ if (vma->vm_file) {
+ /* Convert to file relative offsets */
+ vroot = &vma->vm_file->f_mapping->vroot;
+ vstart = vma->vm_pgoff + start - vma->vm_start;
+ vend = vma->vm_pgoff + tmp - vma->vm_start;
+ } else {
+ vroot = &mm->vroot;
+ vstart = start;
+ vend = tmp;
+ }
+
+ /* mark or unmark */
+ if (mode == VRANGE_VOLATILE)
+ ret = vrange_add(vroot, vstart, vend - 1);
+ else if (mode == VRANGE_NONVOLATILE)
+ ret = vrange_remove(vroot, vstart, vend - 1, purged);
+
+ if (ret)
+ goto out;
+
+ /* update count to distance covered so far*/
+ count = tmp - orig_start;
+
+ /* move start up to the end of the vma*/
+ start = vma->vm_end;
+ if (start >= end)
+ goto out;
+ /* move to the next vma */
+ vma = vma->vm_next;
+ }
+out:
+ up_write(&mm->mmap_sem);
+
+ /* report bytes successfully marked, even if we're exiting on error */
+ if (count)
+ return count;
+
+ return ret;
+}
+
/*
* The vrange(2) system call.
*
@@ -250,7 +320,15 @@ static int vrange_private(struct mm_struct *mm,
* VRANGE_NONVOLATILE - Removes any volatile hints previous specified in that
* range.
*
- * behavior values (bitflags): None yet supported.
+ * behavior values (bitflags):
+ * VRANGE_MODE_SHARED - By default, all volatile ranges are private to the
+ * process. Thus no pages will be purged unless all processes
+ * that have that page mapped agree that it is volatile.
+ * The VRANGE_MODE_SHARED flag allows the volatility to be shared
+ * with any other process mapping those files in a shared fasion
+ * Thus if any process marks a page as volatile, it can be purged
+ * from any other process that maps that file (similar to the
+ * MAP_SHARED semantics for mmap).
*
* purged ptr:
* Returns 1 if any page in the range being marked nonvolatile has been purged.
@@ -269,8 +347,8 @@ SYSCALL_DEFINE5(vrange, unsigned long, start,
struct mm_struct *mm = current->mm;
int ret = -EINVAL;
- /* We don't yet support any behavior modes */
- if (behavior)
+ /* We only support SHARED behavior mode */
+ if (behavior & ~VRANGE_MODE_SHARED)
return -ENOTSUPP;
if (start & ~PAGE_MASK)
@@ -287,8 +365,12 @@ SYSCALL_DEFINE5(vrange, unsigned long, start,
if (start >= TASK_SIZE)
goto out;
- ret = vrange_private(mm, start, end, mode, purged);
-
+ if (behavior & VRANGE_MODE_SHARED)
+ ret = vrange_shared(mm, start, end, mode, purged);
+ else
+ ret = vrange_private(mm, start, end, mode, purged);
out:
return ret;
}
+
+
Extend vrange syscall to properly support VRANGE_MODE_SHARED behavior on file pages. Signed-off-by: John Stultz <john.stultz@linaro.org> --- fs/file_table.c | 5 ++ fs/inode.c | 2 + include/linux/fs.h | 2 + include/linux/vrange.h | 1 + include/uapi/asm-generic/mman-common.h | 2 + mm/vrange.c | 92 ++++++++++++++++++++++++++++++++-- 6 files changed, 99 insertions(+), 5 deletions(-)