memfd_restricted_bind() sets the NUMA memory policy, which consists of a policy mode and zero or more nodes, for an offset within a restrictedmem file with file descriptor fd and continuing for len bytes. This is intended to be like mbind() but specially for restrictedmem files, which cannot be mmap()ed into userspace and hence has no memory addresses that can be used with mbind(). Unlike mbind(), memfd_restricted_bind() will override any existing memory policy if a new memory policy is defined for the same ranges. For now, memfd_restricted_bind() does not perform migrations and no flags are supported. This syscall is specialised just for restrictedmem files because this functionality is not required by other files. Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx> --- arch/x86/entry/syscalls/syscall_32.tbl | 1 + arch/x86/entry/syscalls/syscall_64.tbl | 1 + include/linux/mempolicy.h | 2 +- include/linux/syscalls.h | 5 ++ include/uapi/asm-generic/unistd.h | 5 +- include/uapi/linux/mempolicy.h | 7 ++- kernel/sys_ni.c | 1 + mm/restrictedmem.c | 75 ++++++++++++++++++++++++++ scripts/checksyscalls.sh | 1 + 9 files changed, 95 insertions(+), 3 deletions(-) diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index dc70ba90247e..c94e9ce46cc3 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -456,3 +456,4 @@ 449 i386 futex_waitv sys_futex_waitv 450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node 451 i386 memfd_restricted sys_memfd_restricted +452 i386 memfd_restricted_bind sys_memfd_restricted_bind diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index 06516abc8318..6bd86b45d63a 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -373,6 +373,7 @@ 449 common futex_waitv sys_futex_waitv 450 common set_mempolicy_home_node sys_set_mempolicy_home_node 451 common memfd_restricted sys_memfd_restricted +452 common memfd_restricted_bind sys_memfd_restricted_bind # # Due to a historical design error, certain syscalls are numbered differently diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 15facd9de087..af62233df0c0 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -126,7 +126,7 @@ struct shared_policy { int vma_dup_policy(struct vm_area_struct *src, struct vm_area_struct *dst); struct mempolicy *mpol_create( - unsigned long mode, const unsigned long __user *nmask, unsigned long maxnode) + unsigned long mode, const unsigned long __user *nmask, unsigned long maxnode); void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol); int __mpol_set_shared_policy(struct shared_policy *info, struct mempolicy *mpol, unsigned long pgoff_start, unsigned long npages); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 660be0bf89d5..852b202d3837 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1059,6 +1059,11 @@ asmlinkage long sys_set_mempolicy_home_node(unsigned long start, unsigned long l unsigned long home_node, unsigned long flags); asmlinkage long sys_memfd_restricted(unsigned int flags); +asmlinkage long sys_memfd_restricted_bind(int fd, struct file_range __user *range, + unsigned long mode, + const unsigned long __user *nmask, + unsigned long maxnode, + unsigned int flags); /* * Architecture-specific system calls diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index e2ea7cd964f8..b5a1385bb4a7 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -889,10 +889,13 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) #ifdef __ARCH_WANT_MEMFD_RESTRICTED #define __NR_memfd_restricted 451 __SYSCALL(__NR_memfd_restricted, sys_memfd_restricted) + +#define __NR_memfd_restricted_bind 452 +__SYSCALL(__NR_memfd_restricted_bind, sys_memfd_restricted_bind) #endif #undef __NR_syscalls -#define __NR_syscalls 452 +#define __NR_syscalls 453 /* * 32 bit systems traditionally used different diff --git a/include/uapi/linux/mempolicy.h b/include/uapi/linux/mempolicy.h index 046d0ccba4cd..979499abd253 100644 --- a/include/uapi/linux/mempolicy.h +++ b/include/uapi/linux/mempolicy.h @@ -6,9 +6,9 @@ #ifndef _UAPI_LINUX_MEMPOLICY_H #define _UAPI_LINUX_MEMPOLICY_H +#include <asm-generic/posix_types.h> #include <linux/errno.h> - /* * Both the MPOL_* mempolicy mode and the MPOL_F_* optional mode flags are * passed by the user to either set_mempolicy() or mbind() in an 'int' actual. @@ -72,4 +72,9 @@ enum { #define RECLAIM_WRITE (1<<1) /* Writeout pages during reclaim */ #define RECLAIM_UNMAP (1<<2) /* Unmap pages during reclaim */ +struct file_range { + __kernel_loff_t offset; + __kernel_size_t len; +}; + #endif /* _UAPI_LINUX_MEMPOLICY_H */ diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 7c4a32cbd2e7..db24d3fe6dc5 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -362,6 +362,7 @@ COND_SYSCALL(memfd_secret); /* memfd_restricted */ COND_SYSCALL(memfd_restricted); +COND_SYSCALL(memfd_restricted_bind); /* * Architecture specific weak syscall entries. diff --git a/mm/restrictedmem.c b/mm/restrictedmem.c index 55e99e6c09a1..9c249722c61b 100644 --- a/mm/restrictedmem.c +++ b/mm/restrictedmem.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include <linux/mempolicy.h> #include "linux/sbitmap.h" #include <linux/pagemap.h> #include <linux/pseudo_fs.h> @@ -359,3 +360,77 @@ int restrictedmem_get_page(struct file *file, pgoff_t offset, return 0; } EXPORT_SYMBOL_GPL(restrictedmem_get_page); + +static int restrictedmem_set_shared_policy( + struct file *file, loff_t start, size_t len, struct mempolicy *mpol) +{ + struct restrictedmem *rm; + unsigned long end; + + if (!PAGE_ALIGNED(start)) + return -EINVAL; + + len = PAGE_ALIGN(len); + end = start + len; + + if (end < start) + return -EINVAL; + if (end == start) + return 0; + + rm = file->f_mapping->private_data; + return __mpol_set_shared_policy(shmem_shared_policy(rm->memfd), mpol, + start >> PAGE_SHIFT, len >> PAGE_SHIFT); +} + +static long do_memfd_restricted_bind( + int fd, loff_t offset, size_t len, + unsigned long mode, const unsigned long __user *nmask, + unsigned long maxnode, unsigned int flags) +{ + long ret; + struct fd f; + struct mempolicy *mpol; + + /* None of the flags are supported */ + if (flags) + return -EINVAL; + + f = fdget_raw(fd); + if (!f.file) + return -EBADF; + + if (!file_is_restrictedmem(f.file)) + return -EINVAL; + + mpol = mpol_create(mode, nmask, maxnode); + if (IS_ERR(mpol)) { + ret = PTR_ERR(mpol); + goto out; + } + + ret = restrictedmem_set_shared_policy(f.file, offset, len, mpol); + + mpol_put(mpol); + +out: + fdput(f); + + return ret; +} + +SYSCALL_DEFINE6(memfd_restricted_bind, int, fd, struct file_range __user *, range, + unsigned long, mode, const unsigned long __user *, nmask, + unsigned long, maxnode, unsigned int, flags) +{ + loff_t offset; + size_t len; + + if (unlikely(get_user(offset, &range->offset))) + return -EFAULT; + if (unlikely(get_user(len, &range->len))) + return -EFAULT; + + return do_memfd_restricted_bind(fd, offset, len, mode, nmask, + maxnode, flags); +} diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh index 3c4d2508226a..e253529cf1ec 100755 --- a/scripts/checksyscalls.sh +++ b/scripts/checksyscalls.sh @@ -46,6 +46,7 @@ cat << EOF #ifndef __ARCH_WANT_MEMFD_RESTRICTED #define __IGNORE_memfd_restricted +#define __IGNORE_memfd_restricted_bind #endif /* Missing flags argument */ -- 2.40.0.634.g4ca3ef3211-goog