MAP_POPULATE for device memory
Subsystems (11)
Jarkko Sakkinen
maintainercover MAP_POPULATE for device memory
For device memory (aka VM_IO | VM_PFNMAP) MAP_POPULATE does nothing. Allow to use that for initializing the device memory by providing a new callback f_ops->populate() for the purpose.
SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
Jarkko Sakkinen (3):
mm: Add f_ops->populate()
x86/sgx: Export sgx_encl_page_alloc() x86/sgx: Implement EAUG population with MAP_POPULATE
arch/mips/kernel/vdso.c | 2 +- arch/x86/kernel/cpu/sgx/driver.c | 129 +++++++++++++++++++++ arch/x86/kernel/cpu/sgx/encl.c | 38 ++++++ arch/x86/kernel/cpu/sgx/encl.h | 3 + arch/x86/kernel/cpu/sgx/ioctl.c | 38 ------ drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 2 +- fs/coda/file.c | 2 +- fs/overlayfs/file.c | 2 +- include/linux/fs.h | 12 +- include/linux/mm.h | 2 +- ipc/shm.c | 2 +- mm/mmap.c | 10 +- mm/nommu.c | 4 +- 13 files changed, 193 insertions(+), 53 deletions(-)
Signature · 1 line
2.35.1
Jarkko Sakkinen
maintainerRFC PATCH 1/3 mm: Add f_ops->populate()
Sometimes you might want to use MAP_POPULATE to ask a device driver to initialize the device memory in some specific manner. SGX driver can use this to request more memory by issuing ENCLS[EAUG] x86 opcode for each page in the address range.
Add f_ops->populate() with the same parameters as f_ops->mmap() and make it conditionally called inside call_mmap(). Update call sites accodingly.
---
v3:
- if (!ret && do_populate && file->f_op->populate)
+ if (!ret && do_populate && file->f_op->populate &&
+ !!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
(reported by Matthew Wilcox)
v2:
- if (!ret && do_populate)
+ if (!ret && do_populate && file->f_op->populate)
(reported by Jan Harkes)
--- arch/mips/kernel/vdso.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 2 +- fs/coda/file.c | 2 +- fs/overlayfs/file.c | 2 +- include/linux/fs.h | 12 ++++++++++-- include/linux/mm.h | 2 +- ipc/shm.c | 2 +- mm/mmap.c | 10 +++++----- mm/nommu.c | 4 ++-- 9 files changed, 23 insertions(+), 15 deletions(-)
arch/mips/kernel/vdso.c +1 -1
@@ -102,7 +102,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) base =mmap_region (NULL ,STACK_TOP ,PAGE_SIZE ,VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , -0 ,NULL ); +0 ,NULL , false);if (IS_ERR_VALUE (base)) { ret = base; goto out;
drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +1 -1
@@ -107,7 +107,7 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *if (!obj->base.filp)return -ENODEV ; - ret =call_mmap (obj->base.filp, vma); + ret =call_mmap (obj->base.filp, vma, false);if (ret)return ret;
fs/coda/file.c +1 -1
@@ -173,7 +173,7 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)spin_unlock (&cii->c_lock); vma->vm_file =get_file (host_file); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);if (ret) {/* if call_mmap fails, our caller will put host_file so we
fs/overlayfs/file.c +1 -1
@@ -503,7 +503,7 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)vma_set_file (vma, realfile); old_cred =ovl_override_creds (file_inode (file)->i_sb); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);revert_creds (old_cred);ovl_file_accessed (file);
include/linux/fs.h +10 -2
@@ -42,6 +42,7 @@#include <linux/mount.h> #include <linux/cred.h> #include <linux/mnt_idmapping.h> +#include <linux/mm.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h>
@@ -1993,6 +1994,7 @@ struct file_operations {long (*unlocked_ioctl) (struct file *,unsigned int ,unsigned long );long (*compat_ioctl) (struct file *,unsigned int ,unsigned long );int (*mmap) (struct file *,struct vm_area_struct *); +int (*populate)(struct file *,struct vm_area_struct *);unsigned long mmap_supported_flags;int (*open) (struct inode *,struct file *);int (*flush) (struct file *,fl_owner_t id);
@@ -2074,9 +2076,15 @@ static inline ssize_t call_write_iter(struct file *file, struct kiocb *kio,return file->f_op->write_iter (kio, iter); } -static inline int call_mmap (struct file *file,struct vm_area_struct *vma) +static inline int call_mmap (struct file *file,struct vm_area_struct *vma,bool do_populate) { -return file->f_op->mmap (file, vma); +int ret = file->f_op->mmap (file, vma); + +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP ))) + ret = file->f_op->populate (file, vma); + +return ret; }extern ssize_t vfs_read (struct file *,char __user *,size_t ,loff_t *);
include/linux/mm.h +1 -1
@@ -2683,7 +2683,7 @@ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned loextern unsigned long mmap_region (struct file *file,unsigned long addr,unsigned long len,vm_flags_t vm_flags,unsigned long pgoff, -struct list_head *uf); +struct list_head *uf,bool do_populate);extern unsigned long do_mmap (struct file *file,unsigned long addr,unsigned long len,unsigned long prot,unsigned long flags,unsigned long pgoff,unsigned long *populate,struct list_head *uf);
ipc/shm.c +1 -1
@@ -587,7 +587,7 @@ static int shm_mmap(struct file *file, struct vm_area_struct *vma)if (ret)return ret; - ret =call_mmap (sfd->file, vma); + ret =call_mmap (sfd->file, vma, do_populate);if (ret) {shm_close (vma);return ret;
mm/mmap.c +5 -5
@@ -1413,6 +1413,7 @@ unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long flags, unsigned long pgoff, unsigned long *populate, struct list_head *uf) { + bool do_populate = (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE;struct mm_struct *mm = current->mm;vm_flags_t vm_flags;int pkey =0 ;
@@ -1579,10 +1580,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr, vm_flags |=VM_NORESERVE ; } - addr =mmap_region (file, addr, len, vm_flags, pgoff, uf); + addr =mmap_region (file, addr, len, vm_flags, pgoff, uf, do_populate);if (!IS_ERR_VALUE (addr) && - ((vm_flags &VM_LOCKED ) || - (flags & (MAP_POPULATE |MAP_NONBLOCK )) ==MAP_POPULATE )) + ((vm_flags &VM_LOCKED ) || do_populate)) *populate = len;return addr; }
@@ -1721,7 +1721,7 @@ static inline int accountable_mapping(struct file *file, vm_flags_t vm_flags)unsigned long mmap_region (struct file *file,unsigned long addr,unsigned long len,vm_flags_t vm_flags,unsigned long pgoff, -struct list_head *uf) +struct list_head *uf,bool do_populate) {struct mm_struct *mm = current->mm;struct vm_area_struct *vma, *prev, *merge;
@@ -1790,7 +1790,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, } vma->vm_file =get_file (file); - error =call_mmap (file, vma); + error =call_mmap (file, vma, do_populate);if (error) goto unmap_and_free_vma;
mm/nommu.c +2 -2
@@ -941,7 +941,7 @@ static int do_mmap_shared_file(struct vm_area_struct *vma) {int ret; - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);if (ret ==0 ) { vma->vm_region->vm_top = vma->vm_region->vm_end;return 0 ;
@@ -972,7 +972,7 @@ static int do_mmap_private(struct vm_area_struct *vma,* - VM_MAYSHARE will be set if it may attempt to share */ if (capabilities &NOMMU_MAP_DIRECT ) { - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);if (ret ==0 ) {/* shouldn't return success if we're not sharing */ BUG_ON (!(vma->vm_flags &VM_MAYSHARE ));
Signature · 1 line
2.35.1
Re: [PATCH RFC 1/3] mm: Add f_ops->populate()
Quoted 126 lines · Jarkko Sakkinen
Sometimes you might want to use
MAP_POPULATEto ask a device driver to initialize the device memory in some specific manner. SGX driver can use this to request more memory by issuing ENCLS[EAUG] x86 opcode for each page in the address range.Add
f_ops->populate()with the same parameters asf_ops->mmap()and make it conditionally called insidecall_mmap(). Update call sites accodingly.
---Signed-off-by Jarkko Sakkinen <[email protected]>v3:
include/linux/fs.h +2
-1-if (!ret && do_populate && file->f_op->populate) +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP )))(reported by Matthew Wilcox)
v2:(quoted diff) +1
-1- if (!ret && do_populate) + if (!ret && do_populate && file->f_op->populate)(reported by Jan Harkes)
--- arch/mips/kernel/vdso.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 2 +- fs/coda/file.c | 2 +- fs/overlayfs/file.c | 2 +- include/linux/fs.h | 12 ++++++++++-- include/linux/mm.h | 2 +- ipc/shm.c | 2 +- mm/mmap.c | 10 +++++----- mm/nommu.c | 4 ++-- 9 files changed, 23 insertions(+), 15 deletions(-)arch/mips/kernel/vdso.c +1
-1@@ -102,7 +102,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) base =mmap_region (NULL ,STACK_TOP ,PAGE_SIZE ,VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , -0 ,NULL ); +0 ,NULL , false);if (IS_ERR_VALUE (base)) { ret = base; goto out;drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +1
-1@@ -107,7 +107,7 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *if (!obj->base.filp)return -ENODEV ; - ret =call_mmap (obj->base.filp, vma); + ret =call_mmap (obj->base.filp, vma, false);if (ret)return ret;fs/coda/file.c +1
-1@@ -173,7 +173,7 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)spin_unlock (&cii->c_lock); vma->vm_file =get_file (host_file); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);if (ret) {/* if call_mmap fails, our caller will put host_file so we fs/overlayfs/file.c +1
-1@@ -503,7 +503,7 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)vma_set_file (vma, realfile); old_cred =ovl_override_creds (file_inode (file)->i_sb); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);revert_creds (old_cred);ovl_file_accessed (file);include/linux/fs.h +10
-2@@ -42,6 +42,7 @@#include <linux/mount.h> #include <linux/cred.h> #include <linux/mnt_idmapping.h> +#include <linux/mm.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h> @@ -1993,6 +1994,7 @@ struct file_operations {long (*unlocked_ioctl) (struct file *,unsigned int ,unsigned long );long (*compat_ioctl) (struct file *,unsigned int ,unsigned long );int (*mmap) (struct file *,struct vm_area_struct *); +int (*populate)(struct file *,struct vm_area_struct *);unsigned long mmap_supported_flags;int (*open) (struct inode *,struct file *);int (*flush) (struct file *,fl_owner_t id);@@ -2074,9 +2076,15 @@ static inline ssize_t call_write_iter(struct file *file, struct kiocb *kio,return file->f_op->write_iter (kio, iter); } -static inline int call_mmap (struct file *file,struct vm_area_struct *vma) +static inline int call_mmap (struct file *file,struct vm_area_struct *vma,bool do_populate) { -return file->f_op->mmap (file, vma); +int ret = file->f_op->mmap (file, vma); + +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP ))) + ret = file->f_op->populate (file, vma); + +return ret; }extern ssize_t vfs_read (struct file *,char __user *,size_t ,loff_t *);include/linux/mm.h +0
-0
include/linux/mm.h extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned lo · line 2686
Quoted 5 lines
include/linux/mm.h +1
-1@@ -2683,7 +2683,7 @@ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned loextern unsigned long mmap_region (struct file *file,unsigned long addr,unsigned long len,vm_flags_t vm_flags,unsigned long pgoff, -struct list_head *uf); +struct list_head *uf,bool do_populate);
As I have said many times before, don't add random boolean flags to function arguments, as they provide no hint as to what they do at all. When you read the code, you then have to go back and look up the function definition here and see what exactly it means and the flow is broken.
Make function names mean something obvious, for this, if it really is a good idea to have this new flag (and I doubt it, but that's not my call), then make this a mmap_region_populate() call to make it obvious it is something different than the notmal mmap_region() call.
But as is, this is pretty horrid, don't you agree?
thanks,
greg k-h
Jarkko Sakkinen
maintainerRe: [PATCH RFC 1/3] mm: Add f_ops->populate()
Quoted 139 lines · Greg Kroah-Hartman
Quoted 126 lines · Jarkko Sakkinen
Sometimes you might want to use
MAP_POPULATEto ask a device driver to initialize the device memory in some specific manner. SGX driver can use this to request more memory by issuing ENCLS[EAUG] x86 opcode for each page in the address range.Add
f_ops->populate()with the same parameters asf_ops->mmap()and make it conditionally called insidecall_mmap(). Update call sites accodingly.
---Signed-off-by Jarkko Sakkinen <[email protected]>v3:
include/linux/fs.h +2
-1-if (!ret && do_populate && file->f_op->populate) +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP )))(reported by Matthew Wilcox)
v2:(quoted diff) +1
-1- if (!ret && do_populate) + if (!ret && do_populate && file->f_op->populate)(reported by Jan Harkes)
--- arch/mips/kernel/vdso.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 2 +- fs/coda/file.c | 2 +- fs/overlayfs/file.c | 2 +- include/linux/fs.h | 12 ++++++++++-- include/linux/mm.h | 2 +- ipc/shm.c | 2 +- mm/mmap.c | 10 +++++----- mm/nommu.c | 4 ++-- 9 files changed, 23 insertions(+), 15 deletions(-)arch/mips/kernel/vdso.c +1
-1@@ -102,7 +102,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) base =mmap_region (NULL ,STACK_TOP ,PAGE_SIZE ,VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , -0 ,NULL ); +0 ,NULL , false);if (IS_ERR_VALUE (base)) { ret = base; goto out;drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +1
-1@@ -107,7 +107,7 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *if (!obj->base.filp)return -ENODEV ; - ret =call_mmap (obj->base.filp, vma); + ret =call_mmap (obj->base.filp, vma, false);if (ret)return ret;fs/coda/file.c +1
-1@@ -173,7 +173,7 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)spin_unlock (&cii->c_lock); vma->vm_file =get_file (host_file); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);if (ret) {/* if call_mmap fails, our caller will put host_file so we fs/overlayfs/file.c +1
-1@@ -503,7 +503,7 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)vma_set_file (vma, realfile); old_cred =ovl_override_creds (file_inode (file)->i_sb); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);revert_creds (old_cred);ovl_file_accessed (file);include/linux/fs.h +10
-2@@ -42,6 +42,7 @@#include <linux/mount.h> #include <linux/cred.h> #include <linux/mnt_idmapping.h> +#include <linux/mm.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h> @@ -1993,6 +1994,7 @@ struct file_operations {long (*unlocked_ioctl) (struct file *,unsigned int ,unsigned long );long (*compat_ioctl) (struct file *,unsigned int ,unsigned long );int (*mmap) (struct file *,struct vm_area_struct *); +int (*populate)(struct file *,struct vm_area_struct *);unsigned long mmap_supported_flags;int (*open) (struct inode *,struct file *);int (*flush) (struct file *,fl_owner_t id);@@ -2074,9 +2076,15 @@ static inline ssize_t call_write_iter(struct file *file, struct kiocb *kio,return file->f_op->write_iter (kio, iter); } -static inline int call_mmap (struct file *file,struct vm_area_struct *vma) +static inline int call_mmap (struct file *file,struct vm_area_struct *vma,bool do_populate) { -return file->f_op->mmap (file, vma); +int ret = file->f_op->mmap (file, vma); + +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP ))) + ret = file->f_op->populate (file, vma); + +return ret; }extern ssize_t vfs_read (struct file *,char __user *,size_t ,loff_t *);include/linux/mm.h +0
-0include/linux/mm.h extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned lo · line 2686
Quoted 5 lines
include/linux/mm.h +1
-1@@ -2683,7 +2683,7 @@ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned loextern unsigned long mmap_region (struct file *file,unsigned long addr,unsigned long len,vm_flags_t vm_flags,unsigned long pgoff, -struct list_head *uf); +struct list_head *uf,bool do_populate);As I have said many times before, don't add random boolean flags to function arguments, as they provide no hint as to what they do at all. When you read the code, you then have to go back and look up the function definition here and see what exactly it means and the flow is broken.
Quoted 4 lines
Make function names mean something obvious, for this, if it really is a good idea to have this new flag (and I doubt it, but that's not my call), then make this a
mmap_region_populate()call to make it obvious it is something different than the notmalmmap_region()call.
I can create:
* mmap_region_populate()
* call_mmap_populate()
This would localize the changes and leave out those boolean parameters.
Quoted 1 line
But as is, this is pretty horrid, don't you agree?
So can I conclude from this that in general having populate available for device memory is something horrid, or just the implementation path?
That's the main reason why I made this RFC patch set, to get clear answer to that question. I.e. if it is in general sense a bad idea, I'll just create ioctl. If it is the implementation, I'll try to improve it.
Otherwise, I don't know whether or not it is good idea to include such patch into the main SGX2 patch set. No means enforcibl tryy to push support IO memory populate.
Quoted 3 lines
thanks,
greg k-h
BR, Jarkko
Jarkko Sakkinen
maintainerRe: [PATCH RFC 1/3] mm: Add f_ops->populate()
Quoted 157 lines · Jarkko Sakkinen
Quoted 139 lines · Greg Kroah-Hartman
Quoted 126 lines · Jarkko Sakkinen
Sometimes you might want to use
MAP_POPULATEto ask a device driver to initialize the device memory in some specific manner. SGX driver can use this to request more memory by issuing ENCLS[EAUG] x86 opcode for each page in the address range.Add
f_ops->populate()with the same parameters asf_ops->mmap()and make it conditionally called insidecall_mmap(). Update call sites accodingly.
---Signed-off-by Jarkko Sakkinen <[email protected]>v3:
include/linux/fs.h +2
-1-if (!ret && do_populate && file->f_op->populate) +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP )))(reported by Matthew Wilcox)
v2:(quoted diff) +1
-1- if (!ret && do_populate) + if (!ret && do_populate && file->f_op->populate)(reported by Jan Harkes)
--- arch/mips/kernel/vdso.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c | 2 +- fs/coda/file.c | 2 +- fs/overlayfs/file.c | 2 +- include/linux/fs.h | 12 ++++++++++-- include/linux/mm.h | 2 +- ipc/shm.c | 2 +- mm/mmap.c | 10 +++++----- mm/nommu.c | 4 ++-- 9 files changed, 23 insertions(+), 15 deletions(-)arch/mips/kernel/vdso.c +1
-1@@ -102,7 +102,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) base =mmap_region (NULL ,STACK_TOP ,PAGE_SIZE ,VM_READ |VM_EXEC |VM_MAYREAD |VM_MAYWRITE |VM_MAYEXEC , -0 ,NULL ); +0 ,NULL , false);if (IS_ERR_VALUE (base)) { ret = base; goto out;drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +1
-1@@ -107,7 +107,7 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *if (!obj->base.filp)return -ENODEV ; - ret =call_mmap (obj->base.filp, vma); + ret =call_mmap (obj->base.filp, vma, false);if (ret)return ret;fs/coda/file.c +1
-1@@ -173,7 +173,7 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)spin_unlock (&cii->c_lock); vma->vm_file =get_file (host_file); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);if (ret) {/* if call_mmap fails, our caller will put host_file so we fs/overlayfs/file.c +1
-1@@ -503,7 +503,7 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)vma_set_file (vma, realfile); old_cred =ovl_override_creds (file_inode (file)->i_sb); - ret =call_mmap (vma->vm_file, vma); + ret =call_mmap (vma->vm_file, vma, false);revert_creds (old_cred);ovl_file_accessed (file);include/linux/fs.h +10
-2@@ -42,6 +42,7 @@#include <linux/mount.h> #include <linux/cred.h> #include <linux/mnt_idmapping.h> +#include <linux/mm.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h> @@ -1993,6 +1994,7 @@ struct file_operations {long (*unlocked_ioctl) (struct file *,unsigned int ,unsigned long );long (*compat_ioctl) (struct file *,unsigned int ,unsigned long );int (*mmap) (struct file *,struct vm_area_struct *); +int (*populate)(struct file *,struct vm_area_struct *);unsigned long mmap_supported_flags;int (*open) (struct inode *,struct file *);int (*flush) (struct file *,fl_owner_t id);@@ -2074,9 +2076,15 @@ static inline ssize_t call_write_iter(struct file *file, struct kiocb *kio,return file->f_op->write_iter (kio, iter); } -static inline int call_mmap (struct file *file,struct vm_area_struct *vma) +static inline int call_mmap (struct file *file,struct vm_area_struct *vma,bool do_populate) { -return file->f_op->mmap (file, vma); +int ret = file->f_op->mmap (file, vma); + +if (!ret && do_populate && file->f_op->populate && + !!(vma->vm_flags & (VM_IO |VM_PFNMAP ))) + ret = file->f_op->populate (file, vma); + +return ret; }extern ssize_t vfs_read (struct file *,char __user *,size_t ,loff_t *);include/linux/mm.h +0
-0include/linux/mm.h extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned lo · line 2686
Quoted 5 lines
include/linux/mm.h +1
-1@@ -2683,7 +2683,7 @@ extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned loextern unsigned long mmap_region (struct file *file,unsigned long addr,unsigned long len,vm_flags_t vm_flags,unsigned long pgoff, -struct list_head *uf); +struct list_head *uf,bool do_populate);As I have said many times before, don't add random boolean flags to function arguments, as they provide no hint as to what they do at all. When you read the code, you then have to go back and look up the function definition here and see what exactly it means and the flow is broken.
Quoted 4 lines
Make function names mean something obvious, for this, if it really is a good idea to have this new flag (and I doubt it, but that's not my call), then make this a
mmap_region_populate()call to make it obvious it is something different than the notmalmmap_region()call.I can create:
*
mmap_region_populate()
*call_mmap_populate()This would localize the changes and leave out those boolean parameters.
Quoted 1 line
But as is, this is pretty horrid, don't you agree?
So can I conclude from this that in general having populate available for device memory is something horrid, or just the implementation path?
Quoted 6 lines
That's the main reason why I made this RFC patch set, to get clear answer to that question. I.e. if it is in general sense a bad idea, I'll just create ioctl. If it is the implementation, I'll try to improve it.
Otherwise, I don't know whether or not it is good idea to include such patch into the main SGX2 patch set. No means enforcibl tryy to push support
~~~~~
intentionBR, Jarkko
Re: [PATCH RFC 1/3] mm: Add f_ops->populate()
In reply to Jarkko SakkinenQuoted 2 lines · Jarkko Sakkinen
So can I conclude from this that in general having populate available for device memory is something horrid, or just the implementation path?
You haven't even attempted to explain what the problem is you're trying to solve. You've shown up with some terrible code and said "Hey, is this a good idea". No, no, it's not.
Jarkko Sakkinen
maintainerRe: [PATCH RFC 1/3] mm: Add f_ops->populate()
Quoted 7 lines · Matthew Wilcox
Quoted 2 lines · Jarkko Sakkinen
So can I conclude from this that in general having populate available for device memory is something horrid, or just the implementation path?
You haven't even attempted to explain what the problem is you're trying to solve. You've shown up with some terrible code and said "Hey, is this a good idea". No, no, it's not.
The problem is that in order to include memory to enclave, which is essentially a reserved address range processes virtual address space there's two steps into it:
1. Host side (kernel) does ENCLS[EAUG] to request a new page to be added to the enclave.
2. Enclave accepts request with ENCLU[EACCEPT] or ENCLU[EACCEPTCOPY].
In the current SGX2 patch set this taken care by the page fault handler. I.e. the enclave calls ENCLU[EACCEPT] for an empty address and the #PF handler then does EAUG for a single page.
So if you want to process a batch of pages this generates O(n) round-trips.
So if there was a way pre-do a batch of EAUG's, that would allow to load data to the enclave without causing page faults happening constantly.
One solution for this simply add ioctl:
https://lore.kernel.org/linux-sgx/[email protected]/T/#m195ec84bf85614a140abeee245c5118c22ace8f3
But in practice when you wanted to use it, you would setup the parameters so that they match the mmap() range. So for pratical user space API having mmap() take care of this would be much more lean option.
BR, Jarkko
Jarkko Sakkinen
maintainerRe: [PATCH RFC 1/3] mm: Add f_ops->populate()
Quoted 28 lines · Jarkko Sakkinen
Quoted 7 lines · Matthew Wilcox
Quoted 2 lines · Jarkko Sakkinen
So can I conclude from this that in general having populate available for device memory is something horrid, or just the implementation path?
You haven't even attempted to explain what the problem is you're trying to solve. You've shown up with some terrible code and said "Hey, is this a good idea". No, no, it's not.
The problem is that in order to include memory to enclave, which is essentially a reserved address range processes virtual address space there's two steps into it:
1. Host side (kernel) does ENCLS[EAUG] to request a new page to be added to the enclave.
2. Enclave accepts request with ENCLU[EACCEPT] or ENCLU[EACCEPTCOPY].In the current SGX2 patch set this taken care by the page fault handler. I.e. the enclave calls ENCLU[EACCEPT] for an empty address and the #PF handler then does EAUG for a single page.
So if you want to process a batch of pages this generates O(n) round-trips.
So if there was a way pre-do a batch of EAUG's, that would allow to load data to the enclave without causing page faults happening constantly.
Quoted 8 lines
One solution for this simply add ioctl:
https://lore.kernel.org/linux-sgx/[email protected]/T/#m195ec84bf85614a140abeee245c5118c22ace8f3
But in practice when you wanted to use it, you would setup the parameters so that they match the
mmap()range. So for pratical user space API havingmmap()take care of this would be much more lean option.
For something like Graphene [1] the lazy #PF based option is probably a way to go. For wasm runtime that we're doing in Enarx [2] we get better performance by having something like this. I.e. we most of the time take as much as we use.
[1] https://github.com/gramineproject/graphene
[2] https://enarx.dev/
BR, Jarkko
Jarkko Sakkinen
maintainerRFC PATCH 2/3 x86/sgx: Export sgx_encl_page_alloc()
In reply to Jarkko SakkinenMove sgx_encl_page_alloc() to encl.c and export it so that it can be used in the implementation for MAP_POPULATE, which requires to allocate new enclave pages.
--- arch/x86/kernel/cpu/sgx/encl.c | 38 +++++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/sgx/encl.h | 3 +++ arch/x86/kernel/cpu/sgx/ioctl.c | 38 --------------------------------- 3 files changed, 41 insertions(+), 38 deletions(-)
arch/x86/kernel/cpu/sgx/encl.c +38 -0
@@ -914,6 +914,44 @@ int sgx_encl_test_and_clear_young(struct mm_struct *mm,return ret; } +struct sgx_encl_page *sgx_encl_page_alloc (struct sgx_encl *encl, +unsigned long offset, +u64 secinfo_flags) +{ +struct sgx_encl_page *encl_page; +unsigned long prot; + + encl_page =kzalloc (sizeof (*encl_page),GFP_KERNEL ); +if (!encl_page) +return ERR_PTR (-ENOMEM ); + + encl_page->desc = encl->base + offset; + encl_page->encl = encl; + + prot =_calc_vm_trans (secinfo_flags,SGX_SECINFO_R ,PROT_READ ) | +_calc_vm_trans (secinfo_flags,SGX_SECINFO_W ,PROT_WRITE ) | +_calc_vm_trans (secinfo_flags,SGX_SECINFO_X ,PROT_EXEC ); + +/* +* TCS pages must always RW set for CPU access while the SECINFO +* permissions are *always* zero - the CPU ignores the user provided +* values and silently overwrites them with zero permissions. +*/ +if ((secinfo_flags &SGX_SECINFO_PAGE_TYPE_MASK ) ==SGX_SECINFO_TCS ) + prot |=PROT_READ |PROT_WRITE ; + +/* Calculate maximum of the VM flags for the page. */ + encl_page->vm_max_prot_bits =calc_vm_prot_bits (prot,0 ); + +/* +* At time of allocation, the runtime protection bits are the same +* as the maximum protection bits. +*/ + encl_page->vm_run_prot_bits = encl_page->vm_max_prot_bits; + +return encl_page; +} +/** * sgx_zap_enclave_ptes() - remove PTEs mapping the address from enclave * @encl: the enclave
arch/x86/kernel/cpu/sgx/encl.h +3 -0
@@ -113,6 +113,9 @@ int sgx_encl_get_backing(struct sgx_encl *encl, unsigned long page_index,void sgx_encl_put_backing (struct sgx_backing *backing,bool do_write);int sgx_encl_test_and_clear_young (struct mm_struct *mm,struct sgx_encl_page *page); +struct sgx_encl_page *sgx_encl_page_alloc (struct sgx_encl *encl, +unsigned long offset, +u64 secinfo_flags);void sgx_zap_enclave_ptes (struct sgx_encl *encl,unsigned long addr);struct sgx_epc_page *sgx_alloc_va_page (void );unsigned int sgx_alloc_va_slot (struct sgx_va_page *va_page);
arch/x86/kernel/cpu/sgx/ioctl.c +0 -38
@@ -169,44 +169,6 @@ static long sgx_ioc_enclave_create(struct sgx_encl *encl, void __user *arg)return ret; } -static struct sgx_encl_page *sgx_encl_page_alloc (struct sgx_encl *encl, -unsigned long offset, -u64 secinfo_flags) -{ -struct sgx_encl_page *encl_page; -unsigned long prot; - - encl_page =kzalloc (sizeof (*encl_page),GFP_KERNEL ); -if (!encl_page) -return ERR_PTR (-ENOMEM ); - - encl_page->desc = encl->base + offset; - encl_page->encl = encl; - - prot =_calc_vm_trans (secinfo_flags,SGX_SECINFO_R ,PROT_READ ) | -_calc_vm_trans (secinfo_flags,SGX_SECINFO_W ,PROT_WRITE ) | -_calc_vm_trans (secinfo_flags,SGX_SECINFO_X ,PROT_EXEC ); - -/* -* TCS pages must always RW set for CPU access while the SECINFO -* permissions are *always* zero - the CPU ignores the user provided -* values and silently overwrites them with zero permissions. -*/ -if ((secinfo_flags &SGX_SECINFO_PAGE_TYPE_MASK ) ==SGX_SECINFO_TCS ) - prot |=PROT_READ |PROT_WRITE ; - -/* Calculate maximum of the VM flags for the page. */ - encl_page->vm_max_prot_bits =calc_vm_prot_bits (prot,0 ); - -/* -* At time of allocation, the runtime protection bits are the same -* as the maximum protection bits. -*/ - encl_page->vm_run_prot_bits = encl_page->vm_max_prot_bits; - -return encl_page; -} -static int sgx_validate_secinfo (struct sgx_secinfo *secinfo) {u64 perm = secinfo->flags &SGX_SECINFO_PERMISSION_MASK ;
Signature · 1 line
2.35.1
Jarkko Sakkinen
maintainerRFC PATCH 3/3 x86/sgx: Implement EAUG population with MAP_POPULATE
In reply to Jarkko SakkinenWith SGX1 an enclave needs to be created with its maximum memory demands pre-allocated. Pages cannot be added to an enclave after it is initialized. SGX2 introduces a new function, ENCLS[EAUG] for adding pages to an initialized enclave.
Add support for dynamically adding pages to an initialized enclave with mmap() by populating pages with EAUG. Use f_ops->populate() callback to achieve this behaviour.
--- arch/x86/kernel/cpu/sgx/driver.c | 129 +++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+)
arch/x86/kernel/cpu/sgx/driver.c +129 -0
@@ -9,6 +9,7 @@#include <asm/traps.h> #include "driver.h" #include "encl.h" +#include "encls.h" u64 sgx_attributes_reserved_mask;u64 sgx_xfrm_reserved_mask = ~0x3 ;
@@ -101,6 +102,133 @@ static int sgx_mmap(struct file *file, struct vm_area_struct *vma)return 0 ; } +static int sgx_encl_augment_page (struct sgx_encl *encl,unsigned long offset) +{ +struct sgx_pageinfo pginfo = {0 }; +struct sgx_encl_page *encl_page; +struct sgx_epc_page *epc_page; +struct sgx_va_page *va_page; +u64 secinfo_flags; +int ret; + +/* +* Ignore internal permission checking for dynamically added pages. +* They matter only for data added during the pre-initialization phase. +* The enclave decides the permissions by the means of EACCEPT, +* EACCEPTCOPY and EMODPE. +*/ + secinfo_flags =SGX_SECINFO_R |SGX_SECINFO_W |SGX_SECINFO_X ; + encl_page =sgx_encl_page_alloc (encl, offset, secinfo_flags); +if (IS_ERR (encl_page)) +return PTR_ERR (encl_page); + + epc_page =sgx_alloc_epc_page (encl_page, true); +if (IS_ERR (epc_page)) { + ret =PTR_ERR (epc_page); + goto err_alloc_epc_page; + } + + va_page =sgx_encl_grow (encl); +if (IS_ERR (va_page)) { + ret =PTR_ERR (va_page); + goto err_grow; + } + +mutex_lock (&encl->lock); + +/* +* Adding to encl->va_pages must be done under encl->lock. Ditto for +* deleting (via sgx_encl_shrink()) in the error path. +*/ +if (va_page) +list_add (&va_page->list, &encl->va_pages); + +/* +* Insert prior to EADD in case of OOM. EADD modifies MRENCLAVE, i.e. +* can't be gracefully unwound, while failure on EADD/EXTEND is limited +* to userspace errors (or kernel/hardware bugs). +*/ + ret =xa_insert (&encl->page_array,PFN_DOWN (encl_page->desc), + encl_page,GFP_KERNEL ); + +/* +* If ret == -EBUSY then page was created in another flow while +* running without encl->lock +*/ +if (ret) + goto err_xa_insert; + + pginfo.secs = (unsigned long )sgx_get_epc_virt_addr (encl->secs.epc_page); + pginfo.addr = encl_page->desc &PAGE_MASK ; + pginfo.metadata =0 ; + + ret =__eaug (&pginfo,sgx_get_epc_virt_addr (epc_page)); +if (ret) + goto err_eaug; + + encl_page->encl = encl; + encl_page->epc_page = epc_page; + encl_page->type =SGX_PAGE_TYPE_REG ; + encl->secs_child_cnt++; + +sgx_mark_page_reclaimable (encl_page->epc_page); + +mutex_unlock (&encl->lock); + +return 0 ; + +err_eaug: +xa_erase (&encl->page_array,PFN_DOWN (encl_page->desc)); + +err_xa_insert: +sgx_encl_shrink (encl, va_page); +mutex_unlock (&encl->lock); + +err_grow: +sgx_encl_free_epc_page (epc_page); + +err_alloc_epc_page: +kfree (encl_page); + +return VM_FAULT_SIGBUS ; +} + +/* +* Add new pages to the enclave sequentially with ENCLS[EAUG]. Note that +* sgx_mmap() validates that the given VMA is within the enclave range. Calling +* here sgx_encl_may_map() second time would too time consuming. +*/ +static int sgx_populate (struct file *file,struct vm_area_struct *vma) +{ +unsigned long length = vma->vm_end - vma->vm_start; +struct sgx_encl *encl = file->private_data; +unsigned long start = encl->base - vma->vm_start; +unsigned long pos; +int ret; + +/* EAUG works only for initialized enclaves. */ +if (!test_bit (SGX_ENCL_INITIALIZED , &encl->flags)) +return -EINVAL ; + +for (pos =0 ; pos < length; pos +=PAGE_SIZE ) { +if (signal_pending (current)) { +if (!pos) + ret = -ERESTARTSYS ; + +break ; + } + +if (need_resched ()) +cond_resched (); + + ret =sgx_encl_augment_page (encl, start + pos); +if (ret) +break ; + } + +return ret; +} + static unsigned long sgx_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
@@ -133,6 +261,7 @@ static const struct file_operations sgx_encl_fops = { .compat_ioctl = sgx_compat_ioctl, #endif .mmap = sgx_mmap, + .populate = sgx_populate, .get_unmapped_area = sgx_get_unmapped_area, };
Signature · 1 line
2.35.1
From: Jarkko Sakkinen
Quoted 11 lines
Sent: 06 March 2022 05:32
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
Is this all about trying to stop the vm_operations_struct.fault() function being called?
It is pretty easy to ensure the mappings are setup in the driver's mmap() function.
Then the fault() function can just return -VM_FAULT_SIGBUS;
If it is actually device memory you just need to call vm_iomap_memory() That quite nicely mmap()s PCIe memory space into a user process.
Mapping driver memory is slightly more difficult. For buffers allocated with dma_alloc_coherent() you can probably use dma_mmap_coherent().
But I have a loop calling remap_pfn_range() because the buffer area is made of multiple 16kB kernel buffers that need to be mapped to contiguous user pages.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
'Jarkko Sakkinen'
maintainerQuoted 7 lines · David Laight
From: Jarkko Sakkinen
Quoted 6 lines
Sent: 06 March 2022 05:32
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.
Quoted 8 lines
Quoted 5 lines
SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
Is this all about trying to stop the
vm_operations_struct.fault()function being called?
In SGX protected memory is actually encrypted normal memory and CPU access control semantics (marked as reserved, e.g. struct page's).
In SGX you need call ENCLS[EAUG] outside the protected memory to add new pages to the protected memory. Then when CPU is executing inside this protected memory, also known as enclaves, it commits the memory as part of the enclave either with ENCLU[EACCEPT] or ENCLU[EACCEPTCOPY].
So the point is not prevent page faults but to prepare the memory for pending state so that the enclave can then accept them without round-trips, and in some cases thus improve performance (in our case in enarx.dev platform that we are developing).
In fact, #PF handler in SGX driver in the current SGX2 patch set also does EAUG on-demand. Optimal is to have both routes available. And said, this can be of course also implemented as ioctl.
BR, Jarkko
Quoted 3 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.
As I said, NAK.
Quoted 6 lines · Matthew Wilcox
Quoted 3 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.As I said, NAK.
Agreed. This is an amazingly bad interface.
Jarkko Sakkinen
maintainerQuoted 9 lines · Christoph Hellwig
Quoted 6 lines · Matthew Wilcox
Quoted 3 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.As I said, NAK.
Agreed. This is an amazingly bad interface.
So what would you suggest to sort out the issue? I'm happy to go with ioctl if nothing else is acceptable.
BR, Jarkko
Quoted 2 lines · Jarkko Sakkinen
So what would you suggest to sort out the issue? I'm happy to go with ioctl if nothing else is acceptable.
PLenty of drivers treat all mmaps as if MAP_POPULATE was specified, typically by using (io_)remap_pfn_range. If there any reason to only optionally have the pre-fault semantics for sgx? If not this should be really simple. And if we have a real need for it to be optional we'll just need to find a sane way to pass that information to ->mmap.
Jarkko Sakkinen
maintainerQuoted 9 lines · Christoph Hellwig
Quoted 2 lines · Jarkko Sakkinen
So what would you suggest to sort out the issue? I'm happy to go with ioctl if nothing else is acceptable.
PLenty of drivers treat all mmaps as if
MAP_POPULATEwas specified, typically by using (io_)remap_pfn_range. If there any reason to only optionally have the pre-fault semantics for sgx? If not this should be really simple. And if we have a real need for it to be optional we'll just need to find a sane way to pass that information to ->mmap.
Dave, what if mmap() would just unconditionally EAUG after initialization?
It's an option, yes.
BR, Jarkko
From: Christoph Hellwig
Quoted 11 lines
Sent: 07 March 2022 15:57
Quoted 2 lines · Jarkko Sakkinen
So what would you suggest to sort out the issue? I'm happy to go with ioctl if nothing else is acceptable.
PLenty of drivers treat all mmaps as if
MAP_POPULATEwas specified, typically by using (io_)remap_pfn_range. If there any reason to only optionally have the pre-fault semantics for sgx? If not this should be really simple. And if we have a real need for it to be optional we'll just need to find a sane way to pass that information to ->mmap.
Is there any space in vma->vm_flags ?
That would be better than an extra argument or function.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
Jarkko Sakkinen
maintainerQuoted 13 lines · David Laight
From: Christoph Hellwig
Quoted 11 lines
Sent: 07 March 2022 15:57
Quoted 2 lines · Jarkko Sakkinen
So what would you suggest to sort out the issue? I'm happy to go with ioctl if nothing else is acceptable.
PLenty of drivers treat all mmaps as if
MAP_POPULATEwas specified, typically by using (io_)remap_pfn_range. If there any reason to only optionally have the pre-fault semantics for sgx? If not this should be really simple. And if we have a real need for it to be optional we'll just need to find a sane way to pass that information to ->mmap.
Quoted 3 lines
Is there any space in
vma->vm_flags?That would be better than an extra argument or function.
It's very dense but I'll give a shot for callback route based on Dave's comments in this thread. I.e. use it as filter inside __mm_populate() and populate_vma_page_range().
For Enarx, which we are implementing being able to use MAP_POPULATE and get the full range EAUG'd would be best way to optimize the performance of wasm JIT (Enarx is a wasm run-time capable of running inside an SGX enclave, AMD SEV-SNP VM etc.). More so than any predictor (ra_state, madvice etc.) inside #PF handler, which have been suggested in this thread.
After some research on how we implement user space, I'd rather keep the #PF handler working on a single page (EAUG a single page) and have either ioctl or MAP_POPULATE to do the batch fill.
We can still "not trust the user space" i.e. the populate does not have to guarantee to do the full length since the #PF handler will then fill the holes. This was one concern in this thread but it is not hard to address.
BR, Jarkko
Quoted 9 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
What about extending MADV_POPULATE_READ | MADV_POPULATE_WRITE to support VM_IO | VM_PFNMAP (as well?) ?
Signature · 3 lines
Thanks, David / dhildenb
Jarkko Sakkinen
maintainerQuoted 5 lines · David Hildenbrand
Quoted 4 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.
Quoted 8 lines
Quoted 5 lines
SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
What about extending
MADV_POPULATE_READ|MADV_POPULATE_WRITEto supportVM_IO|VM_PFNMAP(as well?) ?
What would be a proper point to bind that behaviour? For mmap/mprotect it'd be probably populate_vma_page_range() because that would span both mmap() and mprotect() (Dave's suggestion in this thread).
For MAP_POPULATE I did not have hard proof to show that it would be used by other drivers but for madvice() you can find at least a few ioctl based implementations:
$ git grep -e madv --and \( -e ioc \) drivers/ drivers/gpu/drm/i915/gem/i915_gem_ioctls.h:int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/i915/i915_driver.c: DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_RENDER_ALLOW), drivers/gpu/drm/i915/i915_gem.c:i915_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/msm/msm_drv.c:static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, drivers/gpu/drm/msm/msm_drv.c: DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_RENDER_ALLOW), drivers/gpu/drm/panfrost/panfrost_drv.c:static int panfrost_ioctl_madvise(struct drm_device *dev, void *data, drivers/gpu/drm/vc4/vc4_drv.c: DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW), drivers/gpu/drm/vc4/vc4_drv.h:int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/vc4/vc4_gem.c:int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data,
IMHO this also provides supportive claim for MAP_POPULATE, and yeah, I agree that to be consistent implementation, both madvice() and MAP_POPULATE should work.
Quoted 4 lines
Signature · 3 lines
Thanks, David / dhildenb
BR, Jarkko
Quoted 12 lines · Jarkko Sakkinen
Quoted 11 lines · David Hildenbrand
Quoted 9 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
Quoted 6 lines
Quoted 2 lines
What about extending
MADV_POPULATE_READ|MADV_POPULATE_WRITEto supportVM_IO|VM_PFNMAP(as well?) ?What would be a proper point to bind that behaviour? For mmap/mprotect it'd be probably
populate_vma_page_range()because that would span bothmmap()andmprotect()(Dave's suggestion in this thread).
MADV_POPULATE_* ends up in faultin_vma_page_range(), right next to populate_vma_page_range(). So it might require a similar way to hook into the driver I guess.
Quoted 16 lines
For
MAP_POPULATEI did not have hard proof to show that it would be used by other drivers but formadvice()you can find at least a few ioctl based implementations:$ git grep -e madv --and \( -e ioc \) drivers/ drivers/gpu/drm/i915/gem/i915_gem_ioctls.h:int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/i915/i915_driver.c: DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_RENDER_ALLOW), drivers/gpu/drm/i915/i915_gem.c:i915_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/msm/msm_drv.c:static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, drivers/gpu/drm/msm/msm_drv.c: DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_RENDER_ALLOW), drivers/gpu/drm/panfrost/panfrost_drv.c:static int panfrost_ioctl_madvise(struct drm_device *dev, void *data, drivers/gpu/drm/vc4/vc4_drv.c: DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW), drivers/gpu/drm/vc4/vc4_drv.h:int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/vc4/vc4_gem.c:int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data,
Quoted 3 lines
IMHO this also provides supportive claim for
MAP_POPULATE, and yeah, I agree that to be consistent implementation, bothmadvice()andMAP_POPULATEshould work.
MADV_POPULATE_WRITE + MADV_DONTNEED/FALLOC_FL_PUNCH_HOLE is one way to dynamically manage memory consumption inside a sparse memory mapping (preallocate/populate via MADV_POPULATE_WRITE, discard via MADV_DONTNEED/FALLOC_FL_PUNCH_HOLE). Extending that whole mechanism to deal with VM_IO | VM_PFNMAP mappings as well could be interesting.
At least I herd about some ideas where we might want to dynamically expose memory to a VM (via virtio-mem) inside a sparse memory mapping, and the memory in that sparse memory mapping is provided from a dedicated memory pool managed by a device driver -- not just using ordinary anonymous/file/hugetlb memory as we do right now.
Now, this is certainly stuff for the future, I just wanted to mention it.
Signature · 3 lines
Thanks, David / dhildenb
Jarkko Sakkinen
maintainerQuoted 50 lines · David Hildenbrand
Quoted 12 lines · Jarkko Sakkinen
Quoted 11 lines · David Hildenbrand
Quoted 9 lines · Jarkko Sakkinen
For device memory (aka
VM_IO|VM_PFNMAP)MAP_POPULATEdoes nothing. Allow to use that for initializing the device memory by providing a new callbackf_ops->populate()for the purpose.SGX patches are provided to show the callback in context.
An obvious alternative is a ioctl but it is less elegant and requires two syscalls (mmap + ioctl) per memory range, instead of just one (mmap).
Quoted 6 lines
Quoted 2 lines
What about extending
MADV_POPULATE_READ|MADV_POPULATE_WRITEto supportVM_IO|VM_PFNMAP(as well?) ?What would be a proper point to bind that behaviour? For mmap/mprotect it'd be probably
populate_vma_page_range()because that would span bothmmap()andmprotect()(Dave's suggestion in this thread).MADV_POPULATE_* ends up in
faultin_vma_page_range(), right next topopulate_vma_page_range(). So it might require a similar way to hook into the driver I guess.Quoted 16 lines
For
MAP_POPULATEI did not have hard proof to show that it would be used by other drivers but formadvice()you can find at least a few ioctl based implementations:$ git grep -e madv --and \( -e ioc \) drivers/ drivers/gpu/drm/i915/gem/i915_gem_ioctls.h:int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/i915/i915_driver.c: DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_RENDER_ALLOW), drivers/gpu/drm/i915/i915_gem.c:i915_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/msm/msm_drv.c:static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, drivers/gpu/drm/msm/msm_drv.c: DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_RENDER_ALLOW), drivers/gpu/drm/panfrost/panfrost_drv.c:static int panfrost_ioctl_madvise(struct drm_device *dev, void *data, drivers/gpu/drm/vc4/vc4_drv.c: DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW), drivers/gpu/drm/vc4/vc4_drv.h:int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, drivers/gpu/drm/vc4/vc4_gem.c:int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data,Quoted 3 lines
IMHO this also provides supportive claim for
MAP_POPULATE, and yeah, I agree that to be consistent implementation, bothmadvice()andMAP_POPULATEshould work.
MADV_POPULATE_WRITE+ MADV_DONTNEED/FALLOC_FL_PUNCH_HOLE is one way to dynamically manage memory consumption inside a sparse memory mapping (preallocate/populate viaMADV_POPULATE_WRITE, discard via MADV_DONTNEED/FALLOC_FL_PUNCH_HOLE). Extending that whole mechanism to deal withVM_IO|VM_PFNMAPmappings as well could be interesting.
Quoted 7 lines
At least I herd about some ideas where we might want to dynamically expose memory to a VM (via virtio-mem) inside a sparse memory mapping, and the memory in that sparse memory mapping is provided from a dedicated memory pool managed by a device driver -- not just using ordinary anonymous/file/hugetlb memory as we do right now.
Now, this is certainly stuff for the future, I just wanted to mention it.
For SGX purposes I'm now studying the possibly to use ra_state to get idea where do "prefetching" (EAUG's) in batches, as it is something that would not require any intrusive changes to mm but thank you for sharing this. Looking into implementing this properly is the 2nd option, if that does not work out.
Quoted 4 lines
Signature · 3 lines
Thanks, David / dhildenb
BR, Jarkko
Patchwork
CI[Intel-gfx] ✗ Fi.CI.BUILD: failure for MAP_POPULATE for device memory
In reply to Jarkko Sakkinen== Series Details ==
== Series Details ==
Series: MAP_POPULATE for device memory
URL : https://patchwork.freedesktop.org/series/101099/
State : failure
== Summary ==
Applying: mm: Add f_ops->populate()
Applying: x86/sgx: Export sgx_encl_page_alloc()
error: sha1 information is lacking or useless (arch/x86/kernel/cpu/sgx/encl.c).
error: could not build fake ancestor
hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0002 x86/sgx: Export sgx_encl_page_alloc()
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".