From: Matthew Wilcox (Oracle) Date: Wed, 30 Sep 2020 16:49:02 +0000 (-0400) Subject: start of reparenting VMAs. buggy. X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Fmshare;p=users%2Fwilly%2Flinux.git start of reparenting VMAs. buggy. --- diff --git a/mm/mmap.c b/mm/mmap.c index 8c7ca737a19b..376837e90b4b 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -482,8 +482,7 @@ static __always_inline void vma_rb_erase_ignore(struct vm_area_struct *vma, __vma_rb_erase(vma, root); } -static __always_inline void vma_rb_erase(struct vm_area_struct *vma, - struct rb_root *root) +void vma_rb_erase(struct vm_area_struct *vma, struct rb_root *root) { /* * All rb_subtree_gap values must be consistent prior to erase, diff --git a/mm/mshare.c b/mm/mshare.c index f79e6519c94a..618912b1d14e 100644 --- a/mm/mshare.c +++ b/mm/mshare.c @@ -71,13 +71,65 @@ static const struct file_operations mshare_fops = { .release = mshare_release, }; +void vma_rb_erase(struct vm_area_struct *vma, struct rb_root *root); + +static +bool move_vmas(struct mm_struct *new_mm, unsigned long addr, unsigned long len) +{ + struct mm_struct *old_mm = current->mm; + struct vm_area_struct *vma, *next, *prev = NULL; + bool success = false; + int i = 0; + + mmap_write_lock(old_mm); + + /* Must not overlap end */ + vma = find_vma(old_mm, addr + len); + if (vma && vma->vm_start < addr + len) + goto unlock; + vma = find_vma(old_mm, addr); + if (vma && vma->vm_start < addr) + goto unlock; + success = true; + + if (vma) + prev = vma->vm_prev; + while (vma && vma->vm_start < addr + len) { + int ret; + +printk("found vma start %lx\n", vma->vm_start); + vma_rb_erase(vma, &old_mm->mm_rb); + old_mm->map_count--; + + next = vma->vm_next; + ret = insert_vm_struct(new_mm, vma); + if (ret) + goto unlock; + /* XXX: VM_ACCOUNT? */ + vm_stat_account(new_mm, vma->vm_flags, -vma_pages(vma)); + vma = next; + } + if (prev) + prev->vm_next = vma; + if (vma) + vma->vm_prev = prev; + + while (addr < new_mm->task_size) { + new_mm->pgd[i++] = *pgd_offset(old_mm, addr); + addr += PGDIR_SIZE; + } +unlock: + mmap_write_unlock(old_mm); + + return success; +} + SYSCALL_DEFINE3(mshare, unsigned long, addr, unsigned long, len, unsigned long, flags) { struct mm_struct *mm; - struct vm_area_struct *vma; + struct file *file; int fd; - int i = 0; if ((addr | len) & (PGDIR_SIZE - 1)) return -EINVAL; @@ -92,30 +144,24 @@ SYSCALL_DEFINE3(mshare, unsigned long, addr, unsigned long, len, if (!mm->task_size) mm->task_size--; - mmap_write_lock(current->mm); - - vma = find_vma(current->mm, addr + len); - if (vma && vma->vm_start < addr + len) - goto unlock; - vma = find_vma(current->mm, addr); - if (vma && vma->vm_start < addr) - goto unlock; - - while (addr < mm->task_size) { - mm->pgd[i++] = *pgd_offset(current->mm, addr); - addr += PGDIR_SIZE; - } - mmap_write_unlock(current->mm); - - fd = anon_inode_getfd("mshare", &mshare_fops, mm, O_RDWR); + fd = get_unused_fd_flags(O_RDWR); if (fd < 0) - goto nofd; + goto out; + file = anon_inode_getfile("mshare", &mshare_fops, mm, O_RDWR); + if (IS_ERR(file)) + goto nofile; + + if (!move_vmas(mm, addr, len)) + goto invalid; + fd_install(fd, file); out: return fd; -unlock: - mmap_write_unlock(current->mm); - fd = -EINVAL; -nofd: +invalid: + fput(file); + file = ERR_PTR(-EINVAL); +nofile: + put_unused_fd(fd); + fd = PTR_ERR(file); mmput(mm); goto out; }