Received: by 2002:a25:4158:0:0:0:0:0 with SMTP id o85csp4565060yba; Sun, 19 May 2019 23:02:22 -0700 (PDT) X-Google-Smtp-Source: APXvYqy5fskdLFgM8Rdd1QZN6aJagx6cvGBYyGIXe61hr/86o4Zt5Z4yRJ/PP6zUbQxfpoA98fhR X-Received: by 2002:a63:4c54:: with SMTP id m20mr73927231pgl.316.1558332142264; Sun, 19 May 2019 23:02:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1558332142; cv=none; d=google.com; s=arc-20160816; b=qoSa7TqlwCOywOVH0SlrYlrocTaEkUrHnuqu68HvLWgVMF2J05ItKXcxVETcoVTNo3 jjrOFzZv2v/DDHsc71oVFJh48fdPcxpHUwVsV9fpnG9YjIv56CzHjf9VqS7wIWr52Zfz 8CTk+730pXcB7iFSsu2tJNz+MQP2h2Cf3SmkD7vz4u4iM+BI5Kbm9Ml9dFAt6IUq9NKa HmJK6rZr1UXGtPFA8K2xdj8L99S31XqIoKFGO9fguKkOI5Wnj5JHPPF6z7BfOMIcRJpf kZ7c6sstISvDZ17CClyd0tcVYYyoHkx16CpvkQjCIOWodYdVV9DuYm8Jh6H/yZ9lCKxC Q/4A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=8fBJ35agorKCunTXs811LaBEjUlFt/o9xwPOQhaevY0=; b=zVTQsP869sTdCu/ecbUbzdYAo4MWE3523B3NwTfy1NaOS52EGn67w77OMFSa6fnGpg Z7d9wg8gupL86d8BDHnFWgrkp/vrPcOy3z1iZncIAdbDLV7aXXpBYRlmISbkZS5D2Mr5 pPn94EibdkJJJ5YlCmtNM36XvQry2+EZKqMyv+Wp2XDMMxP0Bga0cJqLx60/HYXvuejJ NZBXxSQuAZ5xnmIq8ySO91SuntBA1BDbXELfDk/EuMMD9Y5MxvAQJl550FhBzRxwhm/t 5Dk8MS4yFMP74ecONhAdcH3FqzxQpt2W1qxGMkpCfIq1FmViEI6W3UbykgiXl9yD0OEi wDcw== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=rCgV6grF; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id r5si18769267pfg.152.2019.05.19.23.02.07; Sun, 19 May 2019 23:02:22 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=rCgV6grF; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730326AbfETDxd (ORCPT + 99 others); Sun, 19 May 2019 23:53:33 -0400 Received: from mail-pg1-f194.google.com ([209.85.215.194]:36603 "EHLO mail-pg1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727626AbfETDxc (ORCPT ); Sun, 19 May 2019 23:53:32 -0400 Received: by mail-pg1-f194.google.com with SMTP id a3so6094003pgb.3 for ; Sun, 19 May 2019 20:53:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=8fBJ35agorKCunTXs811LaBEjUlFt/o9xwPOQhaevY0=; b=rCgV6grFd1VFVtus7aOkg2Nhi0kCxqIOMHIRqgpyweiFaeLMopGpQwJXCZSKLwsibC QgYt+TXrIKw2sHv+WiiCOfsbLvzeoIvzscG2JiDBvxnBWFBqW/JRMfK5IiyIMerLxj0v oKNyq7x5uN0yJc6DDoT8UNzSaWOedLJWVbhMqTVfpBBauFJz6d1py2+RYNYursmwonRB 9Ia1hQqeDHdslM+lJrbutkWYR5BmmcZdz7nZslS804Sd9V71hpb0Q89SJWPb0d/+qXQZ 8Iy+Iee1ApriyJSBoamNpeHPHSspH0yPc0PAoQ/N9QX4UN5TXeHvXsjvosacyIOHPyW0 URWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:mime-version:content-transfer-encoding; bh=8fBJ35agorKCunTXs811LaBEjUlFt/o9xwPOQhaevY0=; b=WksBsZxTy9PQ1CV6ZfWkhay9dDtBV3twvU1QyuGzZJcjb6zghtlOp5USMKVImgaR42 8aoOC+oJqny0OiA9RcOB32jJ2Lyop0C4wXqEa429sAd36iiB7BAsSQWVqUBMiMtzdNSv dGmvJZ8AYbQNTf6GbvUzCVwukXM/gXgMoAO1gbzN5ODB5USrgmkaIsEr1RDnczcVcRTu re3P2H/8/FqBEhrhKPlcEi1EGgoofKP+XwP3Zwdevb++4OUly44VByDUE7NUwSRwWEo/ A5rRPsk+fxXuhW06MfZ0ghh4V+Gh3jhm+SfuwAjK5EJIRBdjNPOkv7iNvodV7bksklD7 RQVQ== X-Gm-Message-State: APjAAAXVWg0EihBb1+ULxMZTRpSc6eoe9gFprXFb2mQ6t0+q01WRON/0 vjymDWfLF93UdVT+3m9K5zw= X-Received: by 2002:a63:191b:: with SMTP id z27mr73201987pgl.327.1558324411188; Sun, 19 May 2019 20:53:31 -0700 (PDT) Received: from bbox-2.seo.corp.google.com ([2401:fa00:d:0:98f1:8b3d:1f37:3e8]) by smtp.gmail.com with ESMTPSA id x66sm3312779pfx.139.2019.05.19.20.53.26 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 19 May 2019 20:53:30 -0700 (PDT) From: Minchan Kim To: Andrew Morton Cc: LKML , linux-mm , Michal Hocko , Johannes Weiner , Tim Murray , Joel Fernandes , Suren Baghdasaryan , Daniel Colascione , Shakeel Butt , Sonny Rao , Brian Geffon , Minchan Kim Subject: [RFC 6/7] mm: extend process_madvise syscall to support vector arrary Date: Mon, 20 May 2019 12:52:53 +0900 Message-Id: <20190520035254.57579-7-minchan@kernel.org> X-Mailer: git-send-email 2.21.0.1020.gf2820cf01a-goog In-Reply-To: <20190520035254.57579-1-minchan@kernel.org> References: <20190520035254.57579-1-minchan@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Currently, process_madvise syscall works for only one address range so user should call the syscall several times to give hints to multiple address range. This patch extends process_madvise syscall to support multiple hints, address ranges and return vaules so user could give hints all at once. struct pr_madvise_param { int size; /* the size of this structure */ const struct iovec __user *vec; /* address range array */ } int process_madvise(int pidfd, ssize_t nr_elem, int *behavior, struct pr_madvise_param *results, struct pr_madvise_param *ranges, unsigned long flags); - pidfd target process fd - nr_elem the number of elemenent of array behavior, results, ranges - behavior hints for each address range in remote process so that user could give different hints for each range. - results array of buffers to get results for associated remote address range action. - ranges array to buffers to have remote process's address ranges to be processed - flags extra argument for the future. It should be zero this moment. Example) struct pr_madvise_param { int size; const struct iovec *vec; }; int main(int argc, char *argv[]) { struct pr_madvise_param retp, rangep; struct iovec result_vec[2], range_vec[2]; int hints[2]; long ret[2]; void *addr[2]; pid_t pid; char cmd[64] = {0,}; addr[0] = mmap(NULL, ALLOC_SIZE, PROT_READ|PROT_WRITE, MAP_POPULATE|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (MAP_FAILED == addr[0]) return 1; addr[1] = mmap(NULL, ALLOC_SIZE, PROT_READ|PROT_WRITE, MAP_POPULATE|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); if (MAP_FAILED == addr[1]) return 1; hints[0] = MADV_COLD; range_vec[0].iov_base = addr[0]; range_vec[0].iov_len = ALLOC_SIZE; result_vec[0].iov_base = &ret[0]; result_vec[0].iov_len = sizeof(long); retp.vec = result_vec; retp.size = sizeof(struct pr_madvise_param); hints[1] = MADV_COOL; range_vec[1].iov_base = addr[1]; range_vec[1].iov_len = ALLOC_SIZE; result_vec[1].iov_base = &ret[1]; result_vec[1].iov_len = sizeof(long); rangep.vec = range_vec; rangep.size = sizeof(struct pr_madvise_param); pid = fork(); if (!pid) { sleep(10); } else { int pidfd = open(cmd, O_DIRECTORY | O_CLOEXEC); if (pidfd < 0) return 1; /* munmap to make pages private for the child */ munmap(addr[0], ALLOC_SIZE); munmap(addr[1], ALLOC_SIZE); system("cat /proc/vmstat | egrep 'pswpout|deactivate'"); if (syscall(__NR_process_madvise, pidfd, 2, behaviors, &retp, &rangep, 0)) perror("process_madvise fail\n"); system("cat /proc/vmstat | egrep 'pswpout|deactivate'"); } return 0; } Signed-off-by: Minchan Kim --- include/uapi/asm-generic/mman-common.h | 5 + mm/madvise.c | 184 +++++++++++++++++++++---- 2 files changed, 166 insertions(+), 23 deletions(-) diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h index b9b51eeb8e1a..b8e230de84a6 100644 --- a/include/uapi/asm-generic/mman-common.h +++ b/include/uapi/asm-generic/mman-common.h @@ -74,4 +74,9 @@ #define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\ PKEY_DISABLE_WRITE) +struct pr_madvise_param { + int size; /* the size of this structure */ + const struct iovec __user *vec; /* address range array */ +}; + #endif /* __ASM_GENERIC_MMAN_COMMON_H */ diff --git a/mm/madvise.c b/mm/madvise.c index af02aa17e5c1..f4f569dac2bd 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -320,6 +320,7 @@ static int madvise_cool_pte_range(pmd_t *pmd, unsigned long addr, struct page *page; struct vm_area_struct *vma = walk->vma; unsigned long next; + long nr_pages = 0; next = pmd_addr_end(addr, end); if (pmd_trans_huge(*pmd)) { @@ -380,9 +381,12 @@ static int madvise_cool_pte_range(pmd_t *pmd, unsigned long addr, ptep_test_and_clear_young(vma, addr, pte); deactivate_page(page); + nr_pages++; + } pte_unmap_unlock(orig_pte, ptl); + *(long *)walk->private += nr_pages; cond_resched(); return 0; @@ -390,11 +394,13 @@ static int madvise_cool_pte_range(pmd_t *pmd, unsigned long addr, static void madvise_cool_page_range(struct mmu_gather *tlb, struct vm_area_struct *vma, - unsigned long addr, unsigned long end) + unsigned long addr, unsigned long end, + long *nr_pages) { struct mm_walk cool_walk = { .pmd_entry = madvise_cool_pte_range, .mm = vma->vm_mm, + .private = nr_pages }; tlb_start_vma(tlb, vma); @@ -403,7 +409,8 @@ static void madvise_cool_page_range(struct mmu_gather *tlb, } static long madvise_cool(struct vm_area_struct *vma, - unsigned long start_addr, unsigned long end_addr) + unsigned long start_addr, unsigned long end_addr, + long *nr_pages) { struct mm_struct *mm = vma->vm_mm; struct mmu_gather tlb; @@ -413,7 +420,7 @@ static long madvise_cool(struct vm_area_struct *vma, lru_add_drain(); tlb_gather_mmu(&tlb, mm, start_addr, end_addr); - madvise_cool_page_range(&tlb, vma, start_addr, end_addr); + madvise_cool_page_range(&tlb, vma, start_addr, end_addr, nr_pages); tlb_finish_mmu(&tlb, start_addr, end_addr); return 0; @@ -429,6 +436,7 @@ static int madvise_cold_pte_range(pmd_t *pmd, unsigned long addr, int isolated = 0; struct vm_area_struct *vma = walk->vma; unsigned long next; + long nr_pages = 0; next = pmd_addr_end(addr, end); if (pmd_trans_huge(*pmd)) { @@ -492,7 +500,7 @@ static int madvise_cold_pte_range(pmd_t *pmd, unsigned long addr, list_add(&page->lru, &page_list); if (isolated >= SWAP_CLUSTER_MAX) { pte_unmap_unlock(orig_pte, ptl); - reclaim_pages(&page_list); + nr_pages += reclaim_pages(&page_list); isolated = 0; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); orig_pte = pte; @@ -500,19 +508,22 @@ static int madvise_cold_pte_range(pmd_t *pmd, unsigned long addr, } pte_unmap_unlock(orig_pte, ptl); - reclaim_pages(&page_list); + nr_pages += reclaim_pages(&page_list); cond_resched(); + *(long *)walk->private += nr_pages; return 0; } static void madvise_cold_page_range(struct mmu_gather *tlb, struct vm_area_struct *vma, - unsigned long addr, unsigned long end) + unsigned long addr, unsigned long end, + long *nr_pages) { struct mm_walk warm_walk = { .pmd_entry = madvise_cold_pte_range, .mm = vma->vm_mm, + .private = nr_pages, }; tlb_start_vma(tlb, vma); @@ -522,7 +533,8 @@ static void madvise_cold_page_range(struct mmu_gather *tlb, static long madvise_cold(struct vm_area_struct *vma, - unsigned long start_addr, unsigned long end_addr) + unsigned long start_addr, unsigned long end_addr, + long *nr_pages) { struct mm_struct *mm = vma->vm_mm; struct mmu_gather tlb; @@ -532,7 +544,7 @@ static long madvise_cold(struct vm_area_struct *vma, lru_add_drain(); tlb_gather_mmu(&tlb, mm, start_addr, end_addr); - madvise_cold_page_range(&tlb, vma, start_addr, end_addr); + madvise_cold_page_range(&tlb, vma, start_addr, end_addr, nr_pages); tlb_finish_mmu(&tlb, start_addr, end_addr); return 0; @@ -922,7 +934,7 @@ static int madvise_inject_error(int behavior, static long madvise_vma(struct task_struct *tsk, struct vm_area_struct *vma, struct vm_area_struct **prev, unsigned long start, - unsigned long end, int behavior) + unsigned long end, int behavior, long *nr_pages) { switch (behavior) { case MADV_REMOVE: @@ -930,9 +942,9 @@ madvise_vma(struct task_struct *tsk, struct vm_area_struct *vma, case MADV_WILLNEED: return madvise_willneed(vma, prev, start, end); case MADV_COOL: - return madvise_cool(vma, start, end); + return madvise_cool(vma, start, end, nr_pages); case MADV_COLD: - return madvise_cold(vma, start, end); + return madvise_cold(vma, start, end, nr_pages); case MADV_FREE: case MADV_DONTNEED: return madvise_dontneed_free(tsk, vma, prev, start, @@ -981,7 +993,7 @@ madvise_behavior_valid(int behavior) } static int madvise_core(struct task_struct *tsk, unsigned long start, - size_t len_in, int behavior) + size_t len_in, int behavior, long *nr_pages) { unsigned long end, tmp; struct vm_area_struct *vma, *prev; @@ -996,6 +1008,7 @@ static int madvise_core(struct task_struct *tsk, unsigned long start, if (start & ~PAGE_MASK) return error; + len = (len_in + ~PAGE_MASK) & PAGE_MASK; /* Check to see whether len was rounded up from small -ve to zero */ @@ -1035,6 +1048,8 @@ static int madvise_core(struct task_struct *tsk, unsigned long start, blk_start_plug(&plug); for (;;) { /* Still start < end. */ + long pages = 0; + error = -ENOMEM; if (!vma) goto out; @@ -1053,9 +1068,11 @@ static int madvise_core(struct task_struct *tsk, unsigned long start, tmp = end; /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ - error = madvise_vma(tsk, vma, &prev, start, tmp, behavior); + error = madvise_vma(tsk, vma, &prev, start, tmp, + behavior, &pages); if (error) goto out; + *nr_pages += pages; start = tmp; if (prev && start < prev->vm_end) start = prev->vm_end; @@ -1140,26 +1157,137 @@ static int madvise_core(struct task_struct *tsk, unsigned long start, */ SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior) { - return madvise_core(current, start, len_in, behavior); + unsigned long dummy; + + return madvise_core(current, start, len_in, behavior, &dummy); } -SYSCALL_DEFINE4(process_madvise, int, pidfd, unsigned long, start, - size_t, len_in, int, behavior) +static int pr_madvise_copy_param(struct pr_madvise_param __user *u_param, + struct pr_madvise_param *param) +{ + u32 size; + int ret; + + memset(param, 0, sizeof(*param)); + + ret = get_user(size, &u_param->size); + if (ret) + return ret; + + if (size > PAGE_SIZE) + return -E2BIG; + + if (!size || size > sizeof(struct pr_madvise_param)) + return -EINVAL; + + ret = copy_from_user(param, u_param, size); + if (ret) + return -EFAULT; + + return ret; +} + +static int process_madvise_core(struct task_struct *tsk, int *behaviors, + struct iov_iter *iter, + const struct iovec *range_vec, + unsigned long riovcnt, + unsigned long flags) +{ + int i; + long err; + + for (err = 0, i = 0; i < riovcnt && iov_iter_count(iter); i++) { + long ret = 0; + + err = madvise_core(tsk, (unsigned long)range_vec[i].iov_base, + range_vec[i].iov_len, behaviors[i], + &ret); + if (err) + ret = err; + + if (copy_to_iter(&ret, sizeof(long), iter) != + sizeof(long)) { + err = -EFAULT; + break; + } + + err = 0; + } + + return err; +} + +SYSCALL_DEFINE6(process_madvise, int, pidfd, ssize_t, nr_elem, + const int __user *, hints, + struct pr_madvise_param __user *, results, + struct pr_madvise_param __user *, ranges, + unsigned long, flags) { int ret; struct fd f; struct pid *pid; struct task_struct *tsk; struct mm_struct *mm; + struct pr_madvise_param result_p, range_p; + const struct iovec __user *result_vec, __user *range_vec; + int *behaviors; + struct iovec iovstack_result[UIO_FASTIOV]; + struct iovec iovstack_r[UIO_FASTIOV]; + struct iovec *iov_l = iovstack_result; + struct iovec *iov_r = iovstack_r; + struct iov_iter iter; + + if (flags != 0) + return -EINVAL; + + ret = pr_madvise_copy_param(results, &result_p); + if (ret) + return ret; + + ret = pr_madvise_copy_param(ranges, &range_p); + if (ret) + return ret; + + result_vec = result_p.vec; + range_vec = range_p.vec; + + if (result_p.size != sizeof(struct pr_madvise_param) || + range_p.size != sizeof(struct pr_madvise_param)) + return -EINVAL; + + behaviors = kmalloc_array(nr_elem, sizeof(int), GFP_KERNEL); + if (!behaviors) + return -ENOMEM; + + ret = copy_from_user(behaviors, hints, sizeof(int) * nr_elem); + if (ret < 0) + goto free_behavior_vec; + + ret = import_iovec(READ, result_vec, nr_elem, UIO_FASTIOV, + &iov_l, &iter); + if (ret < 0) + goto free_behavior_vec; + + if (!iov_iter_count(&iter)) { + ret = -EINVAL; + goto free_iovecs; + } + + ret = rw_copy_check_uvector(CHECK_IOVEC_ONLY, range_vec, nr_elem, + UIO_FASTIOV, iovstack_r, &iov_r); + if (ret <= 0) + goto free_iovecs; f = fdget(pidfd); - if (!f.file) - return -EBADF; + if (!f.file) { + ret = -EBADF; + goto free_iovecs; + } pid = pidfd_to_pid(f.file); if (IS_ERR(pid)) { ret = PTR_ERR(pid); - goto err; + goto put_fd; } ret = -EINVAL; @@ -1167,7 +1295,7 @@ SYSCALL_DEFINE4(process_madvise, int, pidfd, unsigned long, start, tsk = pid_task(pid, PIDTYPE_PID); if (!tsk) { rcu_read_unlock(); - goto err; + goto put_fd; } get_task_struct(tsk); rcu_read_unlock(); @@ -1176,12 +1304,22 @@ SYSCALL_DEFINE4(process_madvise, int, pidfd, unsigned long, start, ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; if (ret == -EACCES) ret = -EPERM; - goto err; + goto put_task; } - ret = madvise_core(tsk, start, len_in, behavior); + + ret = process_madvise_core(tsk, behaviors, &iter, iov_r, + nr_elem, flags); mmput(mm); +put_task: put_task_struct(tsk); -err: +put_fd: fdput(f); +free_iovecs: + if (iov_r != iovstack_r) + kfree(iov_r); + kfree(iov_l); +free_behavior_vec: + kfree(behaviors); + return ret; } -- 2.21.0.1020.gf2820cf01a-goog