@@ -13,6 +13,7 @@
#include <linux/page-debug-flags.h>
#include <linux/uprobes.h>
#include <linux/page-flags-layout.h>
+#include <linux/vrange_types.h>
#include <asm/page.h>
#include <asm/mmu.h>
@@ -349,6 +350,9 @@ struct mm_struct {
*/
+#ifdef CONFIG_MMU
+ struct vrange_root vroot;
+#endif
unsigned long hiwater_rss; /* High-watermark of RSS usage */
unsigned long hiwater_vm; /* High-water virtual memory usage */
@@ -37,12 +37,17 @@ static inline int vrange_type(struct vrange *vrange)
}
extern void vrange_root_cleanup(struct vrange_root *vroot);
-
+extern int vrange_fork(struct mm_struct *new,
+ struct mm_struct *old);
#else
static inline void vrange_root_init(struct vrange_root *vroot,
int type, void *object) {};
static inline void vrange_root_cleanup(struct vrange_root *vroot) {};
+static inline int vrange_fork(struct mm_struct *new, struct mm_struct *old)
+{
+ return 0;
+}
#endif
#endif /* _LINIUX_VRANGE_H */
@@ -71,6 +71,7 @@
#include <linux/signalfd.h>
#include <linux/uprobes.h>
#include <linux/aio.h>
+#include <linux/vrange.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -377,6 +378,14 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
retval = khugepaged_fork(mm, oldmm);
if (retval)
goto out;
+ /*
+ * Note: vrange_fork can fail in the case of ENOMEM, but
+ * this only results in the child not having any active
+ * volatile ranges. This is not harmful. Thus in this case
+ * the child will not see any pages purged unless it remarks
+ * them as volatile.
+ */
+ vrange_fork(mm, oldmm);
prev = NULL;
for (mpnt = oldmm->mmap; mpnt; mpnt = mpnt->vm_next) {
@@ -538,6 +547,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
mm->nr_ptes = 0;
memset(&mm->rss_stat, 0, sizeof(mm->rss_stat));
spin_lock_init(&mm->page_table_lock);
+ vrange_root_init(&mm->vroot, VRANGE_MM, mm);
mm_init_aio(mm);
mm_init_owner(mm, p);
@@ -609,6 +619,7 @@ void mmput(struct mm_struct *mm)
if (atomic_dec_and_test(&mm->mm_users)) {
uprobe_clear_state(mm);
+ vrange_root_cleanup(&mm->vroot);
exit_aio(mm);
ksm_exit(mm);
khugepaged_exit(mm); /* must run before exit_mmap */
@@ -181,3 +181,43 @@ void vrange_root_cleanup(struct vrange_root *vroot)
vrange_unlock(vroot);
}
+/*
+ * It's okay to fail vrange_fork because worst case is child process
+ * can't have copied own vrange data structure so that pages in the
+ * vrange couldn't be purged. It would be better rather than failing
+ * fork.
+ */
+int vrange_fork(struct mm_struct *new_mm, struct mm_struct *old_mm)
+{
+ struct vrange_root *new, *old;
+ struct vrange *range, *new_range;
+ struct rb_node *next;
+
+ new = &new_mm->vroot;
+ old = &old_mm->vroot;
+
+ vrange_lock(old);
+ next = rb_first(&old->v_rb);
+ while (next) {
+ range = vrange_entry(next);
+ next = rb_next(next);
+ /*
+ * We can't use GFP_KERNEL because direct reclaim's
+ * purging logic on vrange could be deadlock by
+ * vrange_lock.
+ */
+ new_range = __vrange_alloc(GFP_NOIO);
+ if (!new_range)
+ goto fail;
+ __vrange_set(new_range, range->node.start,
+ range->node.last, range->purged);
+ __vrange_add(new_range, new);
+
+ }
+ vrange_unlock(old);
+ return 0;
+fail:
+ vrange_unlock(old);
+ vrange_root_cleanup(new);
+ return 0;
+}