@@ -55,6 +55,7 @@
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
#include <linux/compat.h>
+#include <linux/vrange.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
@@ -34,12 +34,15 @@ static inline int vrange_type(struct vrange *vrange)
void vrange_init(void);
extern void vrange_root_cleanup(struct vrange_root *vroot);
-
+extern int vrange_root_duplicate(struct vrange_root *orig,
+ struct vrange_root *new);
#else
static inline void vrange_init(void) {};
static inline void vrange_root_init(struct vrange_root *vroot, int type) {};
static inline void vrange_root_cleanup(struct vrange_root *vroot) {};
+static inline int vrange_root_duplicate(struct vrange_root *orig,
+ struct vrange_root *new) {return 0};
#endif
#endif /* _LINIUX_VRANGE_H */
@@ -847,6 +847,12 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
if (mm->binfmt && !try_module_get(mm->binfmt->module))
goto free_pt;
+ /* XXX - Shouldn't this be already done in mm_init? */
+ vrange_root_init(&mm->vroot, VRANGE_MM);
+
+ if (vrange_root_duplicate(&oldmm->vroot, &mm->vroot))
+ goto free_pt;
+
return mm;
free_pt:
@@ -148,6 +148,60 @@ static int vrange_remove(struct vrange_root *vroot,
return 0;
}
+int vrange_root_duplicate(struct vrange_root *old, struct vrange_root *new)
+{
+ struct vrange *old_range, *new_range, *alloc_range;
+ struct rb_node *old_next, *new_next;
+ int ret = 0;
+
+ /*
+ * This is awkward, as taking the vrange_lock here causes problems
+ * since if call vrange_alloc, while holding the vrange_lock,
+ * the allocation could then trigger direct reclaim which could
+ * then try to take the vrange_lock() and deadlock.
+ *
+ * So instead, dance around this dropping locks & restarting when
+ * we have to allocate.
+ */
+again:
+ alloc_range = __vrange_alloc();
+ if (!alloc_range)
+ return -ENOMEM;
+
+ mutex_lock_nested(&old->v_lock, I_MUTEX_PARENT);
+ mutex_lock_nested(&new->v_lock, I_MUTEX_CHILD);
+
+ old_next = rb_first(&old->v_rb);
+ new_next = rb_first(&new->v_rb);
+ while (old_next) {
+ old_range = vrange_entry(old_next);
+ if (!new_next) {
+ new_range = alloc_range;
+ alloc_range = NULL;
+ } else {
+ new_range = vrange_entry(new_next);
+ __vrange_remove(new_range);
+ }
+ __vrange_set(new_range, old_range->node.start,
+ old_range->node.last, old_range->purged);
+ __vrange_add(new_range, new);
+
+ if (!alloc_range) {
+ vrange_unlock(new);
+ vrange_unlock(old);
+ goto again;
+ }
+
+ old_next = rb_next(old_next);
+ new_next = rb_next(new_next);
+ }
+ vrange_unlock(new);
+ vrange_unlock(old);
+
+ __vrange_free(alloc_range);
+
+ return ret;
+}
void vrange_root_cleanup(struct vrange_root *vroot)
{
Volatile ranges should be copied on fork, and cleared on exec. This patch tries to add these semantics. The duplicating of the vranges on fork is a little ackward. This is because we cannot allocate while holding the vrange_root lock, since the allocation could cause reclaim, which may take the vrange_root lock and deadlock. Thus we have to drop all the vrange_root locks for each allocation. Ideas for a better approach would be appreciated! Signed-off-by: John Stultz <john.stultz@linaro.org> --- fs/exec.c | 1 + include/linux/vrange.h | 5 ++++- kernel/fork.c | 6 ++++++ mm/vrange.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 1 deletion(-)