From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:34:54
Hello everyone,
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
The postcopy live migration feature on the qemu side is mostly ready
to be merged and it entirely depends on the userfaultfd syscall to be
merged as well. So it'd be great if this patchset could be reviewed
for merging in -mm.
Userfaults allow to implement on demand paging from userland and more
generally they allow userland to more efficiently take control of the
behavior of page faults than what was available before
(PROT_NONE + SIGSEGV trap).
The use cases are:
1) KVM postcopy live migration (one form of cloud memory
externalization).
KVM postcopy live migration is the primary driver of this work:
http://blog.zhaw.ch/icclab/setting-up-post-copy-live-migration-in-openstack/http://lists.gnu.org/archive/html/qemu-devel/2015-02/msg04873.html
2) postcopy live migration of binaries inside linux containers:
http://thread.gmane.org/gmane.linux.kernel.mm/132662
3) KVM postcopy live snapshotting (allowing to limit/throttle the
memory usage, unlike fork would, plus the avoidance of fork
overhead in the first place).
While the wrprotect tracking is not implemented yet, the syscall API is
already contemplating the wrprotect fault tracking and it's generic enough
to allow its later implementation in a backwards compatible fashion.
4) KVM userfaults on shared memory. The UFFDIO_COPY lowlevel method
should be extended to work also on tmpfs and then the
uffdio_register.ioctls will notify userland that UFFDIO_COPY is
available even when the registered virtual memory range is tmpfs
backed.
5) alternate mechanism to notify web browsers or apps on embedded
devices that volatile pages have been reclaimed. This basically
avoids the need to run a syscall before the app can access with the
CPU the virtual regions marked volatile. This depends on point 4)
to be fulfilled first, as volatile pages happily apply to tmpfs.
Even though there wasn't a real use case requesting it yet, it also
allows to implement distributed shared memory in a way that readonly
shared mappings can exist simultaneously in different hosts and they
can be become exclusive at the first wrprotect fault.
The development version can also be cloned here:
git clone --reference linux -b userfault git://git.kernel.org/pub/scm/linux/kernel/git/andrea/aa.git
Slides from LSF-MM summit (but beware that they're not uptodate):
https://www.kernel.org/pub/linux/kernel/people/andrea/userfaultfd/userfaultfd-LSFMM-2015.pdf
Comments welcome.
Thanks,
Andrea
Changelog of the major changes since the last RFC v3:
o The API has been slightly modified to avoid having to introduce a
second revision of the API, in order to support the non cooperative
usage.
o Various mixed fixes thanks to the feedback from Dave Hansen and
David Gilbert.
The most notable one is the use of mm_users instead of mm_count to
pin the mm to avoid crashes that assumed the vma still existed (in
the userfaultfd_release method and in the various ioctl). exit_mmap
doesn't even set mm->mmap to NULL, so unless I introduce a
userfaultfd_exit to call in mmput, I have to pin the mm_users to be
safe. This is a visible change mainly for the non-cooperative usage.
o userfaults are waken immediately even if they're not been "read"
yet, this can lead to POLLIN false positives (so I only allow poll
if the fd is open in nonblocking mode to be sure it won't hang).
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=f222d9de0a5302dc8ac62d6fab53a84251098751
o optimize read to return entries in O(1) and poll which was already
O(1) becomes lockless. This required to split the waitqueue in two,
one for pending faults and one for non pending faults, and the
faults are refiled across the two waitqueues when they're read. Both
waitqueues are protected by a single lock to be simpler and faster
at runtime (the fault_pending_wqh one).
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=9aa033ed43a1134c2223dac8c5d9e02e0100fca1
o Allocate the ctx with kmem_cache_alloc.
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=f5a8db16d2876eed8906a4d36f1d0e06ca5490f6
o Originally qemu had two bitflags for each page and kept 3 states (of
the 4 possible with two bits) for each page in order to deal with
the races that can happen if one thread is reading the userfaults
and another thread is calling the UFFDIO_COPY ioctl in the
background. This patch solves all races in the kernel so the two
bits per page can be dropped from qemu codebase. I started
documenting the races that can materialize by using 2 threads
(instead of running the workload single threaded with a single poll
event loop) and how userland had to solve them until I decided it
was simpler to fix the race in the kernel by running an ad-hoc
pagetable walk inside the wait_event()-kind-of-section. This
simplified qemu significantly (hundreds line of code involving a
mutex have been deleted and that mutex disappeared as well) and it
doesn't make the kernel much more complicated.
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=41efeae4e93f0296436f2a9fc6b28b6b0158512a
After this patch the only reason to call UFFDIO_WAKE is to handle
the userfaults in batches in combination with the DONT_WAKE flag of
UFFDIO_COPY.
o I removed the read recursion from mcopy_atomic. This avoids to
depend on the write-starvation behavior of rwsem to be safe. After
this change the rwsem is free to stop any further down_read if
there's a down_write waiting on the lock.
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=b1e3a08acc9e3f6c2614e89fc3b8e338daa58e18
o Extendeded the Documentation userfaultfd.txt file to explain how
QEMU/KVM uses userfaultfd to implement postcopy live migration.
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=016f9523b7b2238851533736e84452cb00b2ddcd
Andrea Arcangeli (22):
userfaultfd: linux/Documentation/vm/userfaultfd.txt
userfaultfd: waitqueue: add nr wake parameter to __wake_up_locked_key
userfaultfd: uAPI
userfaultfd: linux/userfaultfd_k.h
userfaultfd: add vm_userfaultfd_ctx to the vm_area_struct
userfaultfd: add VM_UFFD_MISSING and VM_UFFD_WP
userfaultfd: call handle_userfault() for userfaultfd_missing() faults
userfaultfd: teach vma_merge to merge across vma->vm_userfaultfd_ctx
userfaultfd: prevent khugepaged to merge if userfaultfd is armed
userfaultfd: add new syscall to provide memory externalization
userfaultfd: Rename uffd_api.bits into .features fixup
userfaultfd: change the read API to return a uffd_msg
userfaultfd: wake pending userfaults
userfaultfd: optimize read() and poll() to be O(1)
userfaultfd: allocate the userfaultfd_ctx cacheline aligned
userfaultfd: solve the race between UFFDIO_COPY|ZEROPAGE and read
userfaultfd: buildsystem activation
userfaultfd: activate syscall
userfaultfd: UFFDIO_COPY|UFFDIO_ZEROPAGE uAPI
userfaultfd: mcopy_atomic|mfill_zeropage: UFFDIO_COPY|UFFDIO_ZEROPAGE
preparation
userfaultfd: avoid mmap_sem read recursion in mcopy_atomic
userfaultfd: UFFDIO_COPY and UFFDIO_ZEROPAGE
Pavel Emelyanov (1):
userfaultfd: Rename uffd_api.bits into .features
Documentation/ioctl/ioctl-number.txt | 1 +
Documentation/vm/userfaultfd.txt | 142 ++++
arch/powerpc/include/asm/systbl.h | 1 +
arch/powerpc/include/uapi/asm/unistd.h | 1 +
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
fs/Makefile | 1 +
fs/proc/task_mmu.c | 2 +
fs/userfaultfd.c | 1236 ++++++++++++++++++++++++++++++++
include/linux/mm.h | 4 +-
include/linux/mm_types.h | 11 +
include/linux/syscalls.h | 1 +
include/linux/userfaultfd_k.h | 85 +++
include/linux/wait.h | 5 +-
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/userfaultfd.h | 161 +++++
init/Kconfig | 11 +
kernel/fork.c | 3 +-
kernel/sched/wait.c | 7 +-
kernel/sys_ni.c | 1 +
mm/Makefile | 1 +
mm/huge_memory.c | 75 +-
mm/madvise.c | 3 +-
mm/memory.c | 16 +
mm/mempolicy.c | 4 +-
mm/mlock.c | 3 +-
mm/mmap.c | 40 +-
mm/mprotect.c | 3 +-
mm/userfaultfd.c | 309 ++++++++
net/sunrpc/sched.c | 2 +-
30 files changed, 2082 insertions(+), 50 deletions(-)
create mode 100644 Documentation/vm/userfaultfd.txt
create mode 100644 fs/userfaultfd.c
create mode 100644 include/linux/userfaultfd_k.h
create mode 100644 include/uapi/linux/userfaultfd.h
create mode 100644 mm/userfaultfd.c
Credits: partially funded by the Orbit EU project.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:31:32
This adds the vm_userfaultfd_ctx to the vm_area_struct.
Signed-off-by: Andrea Arcangeli <redacted>
---
include/linux/mm_types.h | 11 +++++++++++
kernel/fork.c | 1 +
2 files changed, 12 insertions(+)
@@ -331,6 +341,7 @@ struct vm_area_struct {#ifdef CONFIG_NUMAstructmempolicy*vm_policy;/* NUMA policy for the VMA */#endif+structvm_userfaultfd_ctxvm_userfaultfd_ctx;};structcore_thread{
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:31:40
Solve in-kernel the race between UFFDIO_COPY|ZEROPAGE and
userfaultfd_read if they are run on different threads simultaneously.
Until now qemu solved the race in userland: the race was explicitly
and intentionally left for userland to solve. However we can also
solve it in kernel.
Requiring all users to solve this race if they use two threads (one
for the background transfer and one for the userfault reads) isn't
very attractive from an API prospective, furthermore this allows to
remove a whole bunch of mutex and bitmap code from qemu, making it
faster. The cost of __get_user_pages_fast should be insignificant
considering it scales perfectly and the pagetables are already hot in
the CPU cache, compared to the overhead in userland to maintain those
structures.
Applying this patch is backwards compatible with respect to the
userfaultfd userland API, however reverting this change wouldn't be
backwards compatible anymore.
Without this patch qemu in the background transfer thread, has to read
the old state, and do UFFDIO_WAKE if old_state is missing but it
become REQUESTED by the time it tries to set it to RECEIVED (signaling
the other side received an userfault).
vcpu background_thr userfault_thr
----- ----- -----
vcpu0 handle_mm_fault()
postcopy_place_page
read old_state -> MISSING
UFFDIO_COPY 0x7fb76a139000 (no wakeup, still pending)
vcpu0 fault at 0x7fb76a139000 enters handle_userfault
poll() is kicked
poll() -> POLLIN
read() -> 0x7fb76a139000
postcopy_pmi_change_state(MISSING, REQUESTED) -> REQUESTED
tmp_state = postcopy_pmi_change_state(old_state, RECEIVED) -> REQUESTED
/* check that no userfault raced with UFFDIO_COPY */
if (old_state == MISSING && tmp_state == REQUESTED)
UFFDIO_WAKE from background thread
And a second case where a UFFDIO_WAKE would be needed is in the userfault thread:
vcpu background_thr userfault_thr
----- ----- -----
vcpu0 handle_mm_fault()
postcopy_place_page
read old_state -> MISSING
UFFDIO_COPY 0x7fb76a139000 (no wakeup, still pending)
tmp_state = postcopy_pmi_change_state(old_state, RECEIVED) -> RECEIVED
vcpu0 fault at 0x7fb76a139000 enters handle_userfault
poll() is kicked
poll() -> POLLIN
read() -> 0x7fb76a139000
if (postcopy_pmi_change_state(MISSING, REQUESTED) == RECEIVED)
UFFDIO_WAKE from userfault thread
This patch removes the need of both UFFDIO_WAKE and of the associated
per-page tristate as well.
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/userfaultfd.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 66 insertions(+), 15 deletions(-)
@@ -188,6 +249,7 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,structuserfaultfd_ctx*ctx;structuserfaultfd_wait_queueuwq;intret;+boolmust_wait;BUG_ON(!rwsem_is_locked(&mm->mmap_sem));
@@ -247,9 +309,6 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,/* take the reference before dropping the mmap_sem */userfaultfd_ctx_get(ctx);-/* be gentle and immediately relinquish the mmap_sem */-up_read(&mm->mmap_sem);-init_waitqueue_func_entry(&uwq.wq,userfaultfd_wake_function);uwq.wq.private=current;uwq.msg=userfault_msg(address,flags,reason);
@@ -269,7 +328,10 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,set_current_state(TASK_KILLABLE);spin_unlock(&ctx->fault_pending_wqh.lock);-if(likely(!ACCESS_ONCE(ctx->released)&&+must_wait=userfaultfd_must_wait(ctx,address,flags,reason);+up_read(&mm->mmap_sem);++if(likely(must_wait&&!ACCESS_ONCE(ctx->released)&&!fatal_signal_pending(current))){wake_up_poll(&ctx->fd_wqh,POLLIN);schedule();
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:31:44
This implements mcopy_atomic and mfill_zeropage that are the lowlevel
VM methods that are invoked respectively by the UFFDIO_COPY and
UFFDIO_ZEROPAGE userfaultfd commands.
Signed-off-by: Andrea Arcangeli <redacted>
---
include/linux/userfaultfd_k.h | 6 +
mm/Makefile | 1 +
mm/userfaultfd.c | 269 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 276 insertions(+)
create mode 100644 mm/userfaultfd.c
@@ -0,0 +1,269 @@+/*+*mm/userfaultfd.c+*+*Copyright(C)2015RedHat,Inc.+*+*ThisworkislicensedunderthetermsoftheGNUGPL,version2.See+*theCOPYINGfileinthetop-leveldirectory.+*/++#include<linux/mm.h>+#include<linux/pagemap.h>+#include<linux/rmap.h>+#include<linux/swap.h>+#include<linux/swapops.h>+#include<linux/userfaultfd_k.h>+#include<linux/mmu_notifier.h>+#include<asm/tlbflush.h>+#include"internal.h"++staticintmcopy_atomic_pte(structmm_struct*dst_mm,+pmd_t*dst_pmd,+structvm_area_struct*dst_vma,+unsignedlongdst_addr,+unsignedlongsrc_addr)+{+structmem_cgroup*memcg;+pte_t_dst_pte,*dst_pte;+spinlock_t*ptl;+structpage*page;+void*page_kaddr;+intret;++ret=-ENOMEM;+page=alloc_page_vma(GFP_HIGHUSER_MOVABLE,dst_vma,dst_addr);+if(!page)+gotoout;++page_kaddr=kmap(page);+ret=-EFAULT;+if(copy_from_user(page_kaddr,(constvoid__user*)src_addr,+PAGE_SIZE))+gotoout_kunmap_release;+kunmap(page);++/*+*Thememorybarrierinside__SetPageUptodatemakessurethat+*preceedingstorestothepagecontentsbecomevisiblebefore+*theset_pte_at()write.+*/+__SetPageUptodate(page);++ret=-ENOMEM;+if(mem_cgroup_try_charge(page,dst_mm,GFP_KERNEL,&memcg))+gotoout_release;++_dst_pte=mk_pte(page,dst_vma->vm_page_prot);+if(dst_vma->vm_flags&VM_WRITE)+_dst_pte=pte_mkwrite(pte_mkdirty(_dst_pte));++ret=-EEXIST;+dst_pte=pte_offset_map_lock(dst_mm,dst_pmd,dst_addr,&ptl);+if(!pte_none(*dst_pte))+gotoout_release_uncharge_unlock;++inc_mm_counter(dst_mm,MM_ANONPAGES);+page_add_new_anon_rmap(page,dst_vma,dst_addr);+mem_cgroup_commit_charge(page,memcg,false);+lru_cache_add_active_or_unevictable(page,dst_vma);++set_pte_at(dst_mm,dst_addr,dst_pte,_dst_pte);++/* No need to invalidate - it was non-present before */+update_mmu_cache(dst_vma,dst_addr,dst_pte);++pte_unmap_unlock(dst_pte,ptl);+ret=0;+out:+returnret;+out_release_uncharge_unlock:+pte_unmap_unlock(dst_pte,ptl);+mem_cgroup_cancel_charge(page,memcg);+out_release:+page_cache_release(page);+gotoout;+out_kunmap_release:+kunmap(page);+gotoout_release;+}++staticintmfill_zeropage_pte(structmm_struct*dst_mm,+pmd_t*dst_pmd,+structvm_area_struct*dst_vma,+unsignedlongdst_addr)+{+pte_t_dst_pte,*dst_pte;+spinlock_t*ptl;+intret;++_dst_pte=pte_mkspecial(pfn_pte(my_zero_pfn(dst_addr),+dst_vma->vm_page_prot));+ret=-EEXIST;+dst_pte=pte_offset_map_lock(dst_mm,dst_pmd,dst_addr,&ptl);+if(!pte_none(*dst_pte))+gotoout_unlock;+set_pte_at(dst_mm,dst_addr,dst_pte,_dst_pte);+/* No need to invalidate - it was non-present before */+update_mmu_cache(dst_vma,dst_addr,dst_pte);+ret=0;+out_unlock:+pte_unmap_unlock(dst_pte,ptl);+returnret;+}++staticpmd_t*mm_alloc_pmd(structmm_struct*mm,unsignedlongaddress)+{+pgd_t*pgd;+pud_t*pud;+pmd_t*pmd=NULL;++pgd=pgd_offset(mm,address);+pud=pud_alloc(mm,pgd,address);+if(pud)+/*+*Notethatwedidn'trunthisbecausethepmdwas+*missing,the*pmdmaybealreadyestablishedandin+*turnitmayalsobeatrans_huge_pmd.+*/+pmd=pmd_alloc(mm,pud,address);+returnpmd;+}++static__always_inlinessize_t__mcopy_atomic(structmm_struct*dst_mm,+unsignedlongdst_start,+unsignedlongsrc_start,+unsignedlonglen,+boolzeropage)+{+structvm_area_struct*dst_vma;+ssize_terr;+pmd_t*dst_pmd;+unsignedlongsrc_addr,dst_addr;+longcopied=0;++/*+*Sanitizethecommandparameters:+*/+BUG_ON(dst_start&~PAGE_MASK);+BUG_ON(len&~PAGE_MASK);++/* Does the address range wrap, or is the span zero-sized? */+BUG_ON(src_start+len<=src_start);+BUG_ON(dst_start+len<=dst_start);++down_read(&dst_mm->mmap_sem);++/*+*Makesurethevmaisnotshared,thatthedstrangeis+*bothvalidandfullywithinasingleexistingvma.+*/+err=-EINVAL;+dst_vma=find_vma(dst_mm,dst_start);+if(!dst_vma||(dst_vma->vm_flags&VM_SHARED))+gotoout;+if(dst_start<dst_vma->vm_start||+dst_start+len>dst_vma->vm_end)+gotoout;++/*+*Bestrictandonlyallow__mcopy_atomiconuserfaultfd+*registeredrangestopreventuserlanderrorsgoing+*unnoticed.AsfarastheVMconsistencyisconcerned,it+*wouldbeperfectlysafetoremovethischeck,butthere's+*nousefulusagefor__mcopy_atomicousideofuserfaultfd+*registeredranges.Thisisafterallwhytheseareioctls+*belongingtotheuserfaultfdandnotsyscalls.+*/+if(!dst_vma->vm_userfaultfd_ctx.ctx)+gotoout;++/*+*FIXME:onlyallowcopyingonanonymousvmas,tmpfsshould+*beadded.+*/+if(dst_vma->vm_ops)+gotoout;++/*+*Ensurethedst_vmahasaanon_vmaorthispage+*wouldgetaNULLanon_vmawhenmovedinthe+*dst_vma.+*/+err=-ENOMEM;+if(unlikely(anon_vma_prepare(dst_vma)))+gotoout;++for(src_addr=src_start,dst_addr=dst_start;+src_addr<src_start+len;){+pmd_tdst_pmdval;+BUG_ON(dst_addr>=dst_start+len);+dst_pmd=mm_alloc_pmd(dst_mm,dst_addr);+if(unlikely(!dst_pmd)){+err=-ENOMEM;+break;+}++dst_pmdval=pmd_read_atomic(dst_pmd);+/*+*Ifthedst_pmdismappedasTHPdon't+*overrideitandjustbestrict.+*/+if(unlikely(pmd_trans_huge(dst_pmdval))){+err=-EEXIST;+break;+}+if(unlikely(pmd_none(dst_pmdval))&&+unlikely(__pte_alloc(dst_mm,dst_vma,dst_pmd,+dst_addr))){+err=-ENOMEM;+break;+}+/* If an huge pmd materialized from under us fail */+if(unlikely(pmd_trans_huge(*dst_pmd))){+err=-EFAULT;+break;+}++BUG_ON(pmd_none(*dst_pmd));+BUG_ON(pmd_trans_huge(*dst_pmd));++if(!zeropage)+err=mcopy_atomic_pte(dst_mm,dst_pmd,dst_vma,+dst_addr,src_addr);+else+err=mfill_zeropage_pte(dst_mm,dst_pmd,dst_vma,+dst_addr);++cond_resched();++if(!err){+dst_addr+=PAGE_SIZE;+src_addr+=PAGE_SIZE;+copied+=PAGE_SIZE;++if(fatal_signal_pending(current))+err=-EINTR;+}+if(err)+break;+}++out:+up_read(&dst_mm->mmap_sem);+BUG_ON(copied<0);+BUG_ON(err>0);+BUG_ON(!copied&&!err);+returncopied?copied:err;+}++ssize_tmcopy_atomic(structmm_struct*dst_mm,unsignedlongdst_start,+unsignedlongsrc_start,unsignedlonglen)+{+return__mcopy_atomic(dst_mm,dst_start,src_start,len,false);+}++ssize_tmfill_zeropage(structmm_struct*dst_mm,unsignedlongstart,+unsignedlonglen)+{+return__mcopy_atomic(dst_mm,start,0,len,true);+}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:31:53
These two flags gets set in vma->vm_flags to tell the VM common code
if the userfaultfd is armed and in which mode (only tracking missing
faults, only tracking wrprotect faults or both). If neither flags is
set it means the userfaultfd is not armed on the vma.
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/proc/task_mmu.c | 2 ++
include/linux/mm.h | 2 ++
kernel/fork.c | 2 +-
3 files changed, 5 insertions(+), 1 deletion(-)
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:31:59
userfaultfd needs to wake all waitqueues (pass 0 as nr parameter),
instead of the current hardcoded 1 (that would wake just the first
waitqueue in the head list).
Signed-off-by: Andrea Arcangeli <redacted>
---
include/linux/wait.h | 5 +++--
kernel/sched/wait.c | 7 ++++---
net/sunrpc/sched.c | 2 +-
3 files changed, 8 insertions(+), 6 deletions(-)
@@ -179,7 +180,7 @@ wait_queue_head_t *bit_waitqueue(void *, int);#define wake_up_poll(x, m) \__wake_up(x,TASK_NORMAL,1,(void*)(m))#define wake_up_locked_poll(x, m) \-__wake_up_locked_key((x),TASK_NORMAL,(void*)(m))+__wake_up_locked_key((x),TASK_NORMAL,1,(void*)(m))#define wake_up_interruptible_poll(x, m) \__wake_up(x,TASK_INTERRUPTIBLE,1,(void*)(m))#define wake_up_interruptible_sync_poll(x, m) \
@@ -297,7 +297,7 @@ static int rpc_complete_task(struct rpc_task *task)clear_bit(RPC_TASK_ACTIVE,&task->tk_runstate);ret=atomic_dec_and_test(&task->tk_count);if(waitqueue_active(wq))-__wake_up_locked_key(wq,TASK_NORMAL,&k);+__wake_up_locked_key(wq,TASK_NORMAL,1,&k);spin_unlock_irqrestore(&wq->lock,flags);returnret;}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:32:05
vma->vm_userfaultfd_ctx is yet another vma parameter that vma_merge
must be aware about so that we can merge vmas back like they were
originally before arming the userfaultfd on some memory range.
Signed-off-by: Andrea Arcangeli <redacted>
---
include/linux/mm.h | 2 +-
mm/madvise.c | 3 ++-
mm/mempolicy.c | 4 ++--
mm/mlock.c | 3 ++-
mm/mmap.c | 40 +++++++++++++++++++++++++++-------------
mm/mprotect.c | 3 ++-
6 files changed, 36 insertions(+), 19 deletions(-)
@@ -1570,8 +1583,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,/**Canwejustexpandanoldmapping?*/-vma=vma_merge(mm,prev,addr,addr+len,vm_flags,NULL,file,pgoff,-NULL);+vma=vma_merge(mm,prev,addr,addr+len,vm_flags,+NULL,file,pgoff,NULL,NULL_VM_UFFD_CTX);if(vma)gotoout;
@@ -2757,7 +2770,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)/* Can we just expand an old private anonymous mapping? */vma=vma_merge(mm,prev,addr,addr+len,flags,-NULL,NULL,pgoff,NULL);+NULL,NULL,pgoff,NULL,NULL_VM_UFFD_CTX);if(vma)gotoout;
@@ -2913,7 +2926,8 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,if(find_vma_links(mm,addr,addr+len,&prev,&rb_link,&rb_parent))returnNULL;/* should never get here */new_vma=vma_merge(mm,prev,addr,addr+len,vma->vm_flags,-vma->anon_vma,vma->vm_file,pgoff,vma_policy(vma));+vma->anon_vma,vma->vm_file,pgoff,vma_policy(vma),+vma->vm_userfaultfd_ctx);if(new_vma){/**Sourcevmamayhavebeenmergedintonew_vma
@@ -27,20 +27,26 @@#include<linux/ioctl.h>#include<linux/security.h>+staticstructkmem_cache*userfaultfd_ctx_cachep__read_mostly;+enumuserfaultfd_state{UFFD_STATE_WAIT_API,UFFD_STATE_RUNNING,};+/*+*Startwithfault_pending_wqhandfault_wqhsothey'remorelikely+*tobeinthesamecacheline.+*/structuserfaultfd_ctx{-/* pseudo fd refcounting */-atomic_trefcount;/* waitqueue head for the pending (i.e. not read) userfaults */wait_queue_head_tfault_pending_wqh;/* waitqueue head for the userfaults */wait_queue_head_tfault_wqh;/* waitqueue head for the pseudo fd to wakeup poll/read */wait_queue_head_tfd_wqh;+/* pseudo fd refcounting */+atomic_trefcount;/* userfaultfd syscall flags */unsignedintflags;/* state machine */
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:32:44
Once an userfaultfd has been created and certain region of the process
virtual address space have been registered into it, the thread
responsible for doing the memory externalization can manage the page
faults in userland by talking to the kernel using the userfaultfd
protocol.
poll() can be used to know when there are new pending userfaults to be
read (POLLIN).
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/userfaultfd.c | 1008 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1008 insertions(+)
create mode 100644 fs/userfaultfd.c
@@ -0,0 +1,1008 @@+/*+*fs/userfaultfd.c+*+*Copyright(C)2007DavideLibenzi<davidel@xmailserver.org>+*Copyright(C)2008-2009RedHat,Inc.+*Copyright(C)2015RedHat,Inc.+*+*ThisworkislicensedunderthetermsoftheGNUGPL,version2.See+*theCOPYINGfileinthetop-leveldirectory.+*+*Somepartderivedfromfs/eventfd.c(anoninodesetup)and+*mm/ksm.c(mmhashing).+*/++#include<linux/hashtable.h>+#include<linux/sched.h>+#include<linux/mm.h>+#include<linux/poll.h>+#include<linux/slab.h>+#include<linux/seq_file.h>+#include<linux/file.h>+#include<linux/bug.h>+#include<linux/anon_inodes.h>+#include<linux/syscalls.h>+#include<linux/userfaultfd_k.h>+#include<linux/mempolicy.h>+#include<linux/ioctl.h>+#include<linux/security.h>++enumuserfaultfd_state{+UFFD_STATE_WAIT_API,+UFFD_STATE_RUNNING,+};++structuserfaultfd_ctx{+/* pseudo fd refcounting */+atomic_trefcount;+/* waitqueue head for the userfaultfd page faults */+wait_queue_head_tfault_wqh;+/* waitqueue head for the pseudo fd to wakeup poll/read */+wait_queue_head_tfd_wqh;+/* userfaultfd syscall flags */+unsignedintflags;+/* state machine */+enumuserfaultfd_statestate;+/* released */+boolreleased;+/* mm with one ore more vmas attached to this userfaultfd_ctx */+structmm_struct*mm;+};++structuserfaultfd_wait_queue{+unsignedlongaddress;+wait_queue_twq;+boolpending;+structuserfaultfd_ctx*ctx;+};++structuserfaultfd_wake_range{+unsignedlongstart;+unsignedlonglen;+};++staticintuserfaultfd_wake_function(wait_queue_t*wq,unsignedmode,+intwake_flags,void*key)+{+structuserfaultfd_wake_range*range=key;+intret;+structuserfaultfd_wait_queue*uwq;+unsignedlongstart,len;++uwq=container_of(wq,structuserfaultfd_wait_queue,wq);+ret=0;+/* don't wake the pending ones to avoid reads to block */+if(uwq->pending&&!ACCESS_ONCE(uwq->ctx->released))+gotoout;+/* len == 0 means wake all */+start=range->start;+len=range->len;+if(len&&(start>uwq->address||start+len<=uwq->address))+gotoout;+ret=wake_up_state(wq->private,mode);+if(ret)+/* wake only once, autoremove behavior */+list_del_init(&wq->task_list);+out:+returnret;+}++/**+*userfaultfd_ctx_get-Acquiresareferencetotheinternaluserfaultfd+*context.+*@ctx:[in]Pointertotheuserfaultfdcontext.+*+*Returns:Incaseofsuccess,returnsnotzero.+*/+staticvoiduserfaultfd_ctx_get(structuserfaultfd_ctx*ctx)+{+if(!atomic_inc_not_zero(&ctx->refcount))+BUG();+}++/**+*userfaultfd_ctx_put-Releasesareferencetotheinternaluserfaultfd+*context.+*@ctx:[in]Pointertouserfaultfdcontext.+*+*Theuserfaultfdcontextreferencemusthavebeenpreviouslyacquiredeither+*withuserfaultfd_ctx_get()oruserfaultfd_ctx_fdget().+*/+staticvoiduserfaultfd_ctx_put(structuserfaultfd_ctx*ctx)+{+if(atomic_dec_and_test(&ctx->refcount)){+VM_BUG_ON(spin_is_locked(&ctx->fault_pending_wqh.lock));+VM_BUG_ON(waitqueue_active(&ctx->fault_pending_wqh));+VM_BUG_ON(spin_is_locked(&ctx->fault_wqh.lock));+VM_BUG_ON(waitqueue_active(&ctx->fault_wqh));+VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock));+VM_BUG_ON(waitqueue_active(&ctx->fd_wqh));+mmput(ctx->mm);+kfree(ctx);+}+}++staticinlineunsignedlonguserfault_address(unsignedlongaddress,+unsignedintflags,+unsignedlongreason)+{+BUILD_BUG_ON(PAGE_SHIFT<UFFD_BITS);+address&=PAGE_MASK;+if(flags&FAULT_FLAG_WRITE)+/*+*Encode"write"faultinformationintheLSBofthe+*addressreadbyuserland,withoutdependingon+*FAULT_FLAG_WRITEkernelinternalvalue.+*/+address|=UFFD_BIT_WRITE;+if(reason&VM_UFFD_WP)+/*+*Encode"reason"faultinformationasbitnumber1+*intheaddressreadbyuserland.Ifbitnumber1is+*clearitmeansthereasonisaVM_FAULT_MISSING+*fault.+*/+address|=UFFD_BIT_WP;+returnaddress;+}++/*+*ThelockingrulesinvolvedinreturningVM_FAULT_RETRYdependingon+*FAULT_FLAG_ALLOW_RETRY,FAULT_FLAG_RETRY_NOWAITand+*FAULT_FLAG_KILLABLEarenotstraightforward.The"Caution"+*recommendationin__lock_page_or_retryisnotanunderstatement.+*+*IfFAULT_FLAG_ALLOW_RETRYisset,themmap_semmustbereleased+*beforereturningVM_FAULT_RETRYonlyifFAULT_FLAG_RETRY_NOWAITis+*notset.+*+*IfFAULT_FLAG_ALLOW_RETRYissetbutFAULT_FLAG_KILLABLEisnot+*set,VM_FAULT_RETRYcanstillbereturnedifandonlyifthereare+*fatal_signal_pending()s,andthemmap_semmustbereleasedbefore+*returningit.+*/+inthandle_userfault(structvm_area_struct*vma,unsignedlongaddress,+unsignedintflags,unsignedlongreason)+{+structmm_struct*mm=vma->vm_mm;+structuserfaultfd_ctx*ctx;+structuserfaultfd_wait_queueuwq;++BUG_ON(!rwsem_is_locked(&mm->mmap_sem));++ctx=vma->vm_userfaultfd_ctx.ctx;+if(!ctx)+returnVM_FAULT_SIGBUS;++BUG_ON(ctx->mm!=mm);++VM_BUG_ON(reason&~(VM_UFFD_MISSING|VM_UFFD_WP));+VM_BUG_ON(!(reason&VM_UFFD_MISSING)^!!(reason&VM_UFFD_WP));++/*+*Ifit'salreadyreleaseddon'tgetit.Thisavoidstoloop+*in__get_user_pagesifuserfaultfd_releasewaitsonthe+*callerofhandle_userfaulttoreleasethemmap_sem.+*/+if(unlikely(ACCESS_ONCE(ctx->released)))+returnVM_FAULT_SIGBUS;++/*+*CheckthatwecanreturnVM_FAULT_RETRY.+*+*NOTE:itshouldbecomepossibletoreturnVM_FAULT_RETRY+*evenifFAULT_FLAG_TRIEDissetwithoutleadingtogup()+*-EBUSYfailures,iftheuserfaultfdistobeextendedfor+*VM_UFFD_WPtrackingandweintendtoarmtheuserfault+*withoutfirststoppinguserlandaccesstothememory.For+*VM_UFFD_MISSINGuserfaultsthisisenoughfornow.+*/+if(unlikely(!(flags&FAULT_FLAG_ALLOW_RETRY))){+/*+*Validatetheinvariantthatnowaitmustallowretry+*tobesurenottoreturnSIGBUSerroneouslyon+*nowaitinvocations.+*/+BUG_ON(flags&FAULT_FLAG_RETRY_NOWAIT);+#ifdef CONFIG_DEBUG_VM+if(printk_ratelimit()){+printk(KERN_WARNING+"FAULT_FLAG_ALLOW_RETRY missing %x\n",flags);+dump_stack();+}+#endif+returnVM_FAULT_SIGBUS;+}++/*+*Handlenowait,notmuchtodootherthantellittoretry+*andwait.+*/+if(flags&FAULT_FLAG_RETRY_NOWAIT)+returnVM_FAULT_RETRY;++/* take the reference before dropping the mmap_sem */+userfaultfd_ctx_get(ctx);++/* be gentle and immediately relinquish the mmap_sem */+up_read(&mm->mmap_sem);++init_waitqueue_func_entry(&uwq.wq,userfaultfd_wake_function);+uwq.wq.private=current;+uwq.address=userfault_address(address,flags,reason);+uwq.pending=true;+uwq.ctx=ctx;++spin_lock(&ctx->fault_wqh.lock);+/*+*Afterthe__add_wait_queuetheuwqisvisibletouserland+*throughpoll/read().+*/+__add_wait_queue(&ctx->fault_wqh,&uwq.wq);+for(;;){+set_current_state(TASK_KILLABLE);+if(!uwq.pending||ACCESS_ONCE(ctx->released)||+fatal_signal_pending(current))+break;+spin_unlock(&ctx->fault_wqh.lock);++wake_up_poll(&ctx->fd_wqh,POLLIN);+schedule();++spin_lock(&ctx->fault_wqh.lock);+}+__remove_wait_queue(&ctx->fault_wqh,&uwq.wq);+__set_current_state(TASK_RUNNING);+spin_unlock(&ctx->fault_wqh.lock);++/*+*ctxmaygoawayafterthisiftheuserfaultpseudofdis+*alreadyreleased.+*/+userfaultfd_ctx_put(ctx);++returnVM_FAULT_RETRY;+}++staticintuserfaultfd_release(structinode*inode,structfile*file)+{+structuserfaultfd_ctx*ctx=file->private_data;+structmm_struct*mm=ctx->mm;+structvm_area_struct*vma,*prev;+/* len == 0 means wake all */+structuserfaultfd_wake_rangerange={.len=0,};+unsignedlongnew_flags;++ACCESS_ONCE(ctx->released)=true;++/*+*FlushpagefaultsoutofallCPUs.NOTE:allpagefaults+*mustberetriedwithoutreturningVM_FAULT_SIGBUSif+*userfaultfd_ctx_get()succeedsbutvma->vma_userfault_ctx+*changeswhilehandle_userfaultreleasedthemmap_sem.So+*it'scriticalthatreleasedissettotrue(above),before+*takingthemmap_semforwriting.+*/+down_write(&mm->mmap_sem);+prev=NULL;+for(vma=mm->mmap;vma;vma=vma->vm_next){+cond_resched();+BUG_ON(!!vma->vm_userfaultfd_ctx.ctx^+!!(vma->vm_flags&(VM_UFFD_MISSING|VM_UFFD_WP)));+if(vma->vm_userfaultfd_ctx.ctx!=ctx){+prev=vma;+continue;+}+new_flags=vma->vm_flags&~(VM_UFFD_MISSING|VM_UFFD_WP);+prev=vma_merge(mm,prev,vma->vm_start,vma->vm_end,+new_flags,vma->anon_vma,+vma->vm_file,vma->vm_pgoff,+vma_policy(vma),+NULL_VM_UFFD_CTX);+if(prev)+vma=prev;+else+prev=vma;+vma->vm_flags=new_flags;+vma->vm_userfaultfd_ctx=NULL_VM_UFFD_CTX;+}+up_write(&mm->mmap_sem);++/*+*Afternonewpagefaultscanwaitonthisfault_wqh,flush+*thelastpagefaultsthatmayhavebeenalreadywaitingon+*thefault_wqh.+*/+spin_lock(&ctx->fault_wqh.lock);+__wake_up_locked_key(&ctx->fault_wqh,TASK_NORMAL,0,&range);+spin_unlock(&ctx->fault_wqh.lock);++wake_up_poll(&ctx->fd_wqh,POLLHUP);+userfaultfd_ctx_put(ctx);+return0;+}++/* fault_wqh.lock must be hold by the caller */+staticinlineunsignedintfind_userfault(structuserfaultfd_ctx*ctx,+structuserfaultfd_wait_queue**uwq)+{+wait_queue_t*wq;+structuserfaultfd_wait_queue*_uwq;+unsignedintret=0;++VM_BUG_ON(!spin_is_locked(&ctx->fault_wqh.lock));++list_for_each_entry(wq,&ctx->fault_wqh.task_list,task_list){+_uwq=container_of(wq,structuserfaultfd_wait_queue,wq);+if(_uwq->pending){+ret=POLLIN;+if(!uwq)+/*+*Ifthere'satleastapendingand+*wedon'tcarewhichoneitis,+*breakimmediatelyandleveragethe+*efficiencyoftheLIFOwalk.+*/+break;+/*+*Ifweneedtofindwhichonewaspendingwe+*keepwalkinguntilwefindthefirstnot+*pendingone,soweread()theminFIFOorder.+*/+*uwq=_uwq;+}else+/*+*breaktheloopatthefirstnotpending+*one,therecannotbependinguserfaults+*afterthefirstnotpendingone,because+*allnewpendingonesareinsertedatthe+*headandwewalkitinLIFO.+*/+break;+}++returnret;+}++staticunsignedintuserfaultfd_poll(structfile*file,poll_table*wait)+{+structuserfaultfd_ctx*ctx=file->private_data;+unsignedintret;++poll_wait(file,&ctx->fd_wqh,wait);++switch(ctx->state){+caseUFFD_STATE_WAIT_API:+returnPOLLERR;+caseUFFD_STATE_RUNNING:+spin_lock(&ctx->fault_wqh.lock);+ret=find_userfault(ctx,NULL);+spin_unlock(&ctx->fault_wqh.lock);+returnret;+default:+BUG();+}+}++staticssize_tuserfaultfd_ctx_read(structuserfaultfd_ctx*ctx,intno_wait,+__u64*addr)+{+ssize_tret;+DECLARE_WAITQUEUE(wait,current);+structuserfaultfd_wait_queue*uwq=NULL;++/* always take the fd_wqh lock before the fault_wqh lock */+spin_lock(&ctx->fd_wqh.lock);+__add_wait_queue(&ctx->fd_wqh,&wait);+for(;;){+set_current_state(TASK_INTERRUPTIBLE);+spin_lock(&ctx->fault_wqh.lock);+if(find_userfault(ctx,&uwq)){+/*+*Thefault_wqh.lockpreventstheuwqto+*disappearfromunderus.+*/+uwq->pending=false;+/* careful to always initialize addr if ret == 0 */+*addr=uwq->address;+spin_unlock(&ctx->fault_wqh.lock);+ret=0;+break;+}+spin_unlock(&ctx->fault_wqh.lock);+if(signal_pending(current)){+ret=-ERESTARTSYS;+break;+}+if(no_wait){+ret=-EAGAIN;+break;+}+spin_unlock(&ctx->fd_wqh.lock);+schedule();+spin_lock_irq(&ctx->fd_wqh.lock);+}+__remove_wait_queue(&ctx->fd_wqh,&wait);+__set_current_state(TASK_RUNNING);+spin_unlock_irq(&ctx->fd_wqh.lock);++returnret;+}++staticssize_tuserfaultfd_read(structfile*file,char__user*buf,+size_tcount,loff_t*ppos)+{+structuserfaultfd_ctx*ctx=file->private_data;+ssize_t_ret,ret=0;+/* careful to always initialize addr if ret == 0 */+__u64uninitialized_var(addr);+intno_wait=file->f_flags&O_NONBLOCK;++if(ctx->state==UFFD_STATE_WAIT_API)+return-EINVAL;+BUG_ON(ctx->state!=UFFD_STATE_RUNNING);++for(;;){+if(count<sizeof(addr))+returnret?ret:-EINVAL;+_ret=userfaultfd_ctx_read(ctx,no_wait,&addr);+if(_ret<0)+returnret?ret:_ret;+if(put_user(addr,(__u64__user*)buf))+returnret?ret:-EFAULT;+ret+=sizeof(addr);+buf+=sizeof(addr);+count-=sizeof(addr);+/*+*Allowtoreadmorethanonefaultattimebutonly+*blockifwaitingfortheveryfirstone.+*/+no_wait=O_NONBLOCK;+}+}++staticvoid__wake_userfault(structuserfaultfd_ctx*ctx,+structuserfaultfd_wake_range*range)+{+unsignedlongstart,end;++start=range->start;+end=range->start+range->len;++spin_lock(&ctx->fault_wqh.lock);+/* wake all in the range and autoremove */+__wake_up_locked_key(&ctx->fault_wqh,TASK_NORMAL,0,range);+spin_unlock(&ctx->fault_wqh.lock);+}++static__always_inlinevoidwake_userfault(structuserfaultfd_ctx*ctx,+structuserfaultfd_wake_range*range)+{+if(waitqueue_active(&ctx->fault_wqh))+__wake_userfault(ctx,range);+}++static__always_inlineintvalidate_range(structmm_struct*mm,+__u64start,__u64len)+{+__u64task_size=mm->task_size;++if(start&~PAGE_MASK)+return-EINVAL;+if(len&~PAGE_MASK)+return-EINVAL;+if(!len)+return-EINVAL;+if(start<mmap_min_addr)+return-EINVAL;+if(start>=task_size)+return-EINVAL;+if(len>task_size-start)+return-EINVAL;+return0;+}++staticintuserfaultfd_register(structuserfaultfd_ctx*ctx,+unsignedlongarg)+{+structmm_struct*mm=ctx->mm;+structvm_area_struct*vma,*prev,*cur;+intret;+structuffdio_registeruffdio_register;+structuffdio_register__user*user_uffdio_register;+unsignedlongvm_flags,new_flags;+boolfound;+unsignedlongstart,end,vma_end;++user_uffdio_register=(structuffdio_register__user*)arg;++ret=-EFAULT;+if(copy_from_user(&uffdio_register,user_uffdio_register,+sizeof(uffdio_register)-sizeof(__u64)))+gotoout;++ret=-EINVAL;+if(!uffdio_register.mode)+gotoout;+if(uffdio_register.mode&~(UFFDIO_REGISTER_MODE_MISSING|+UFFDIO_REGISTER_MODE_WP))+gotoout;+vm_flags=0;+if(uffdio_register.mode&UFFDIO_REGISTER_MODE_MISSING)+vm_flags|=VM_UFFD_MISSING;+if(uffdio_register.mode&UFFDIO_REGISTER_MODE_WP){+vm_flags|=VM_UFFD_WP;+/*+*FIXME:removethebelowerrorconstraintby+*implementingthewprotecttrackingmode.+*/+ret=-EINVAL;+gotoout;+}++ret=validate_range(mm,uffdio_register.range.start,+uffdio_register.range.len);+if(ret)+gotoout;++start=uffdio_register.range.start;+end=start+uffdio_register.range.len;++down_write(&mm->mmap_sem);+vma=find_vma_prev(mm,start,&prev);++ret=-ENOMEM;+if(!vma)+gotoout_unlock;++/* check that there's at least one vma in the range */+ret=-EINVAL;+if(vma->vm_start>=end)+gotoout_unlock;++/*+*Searchfornotcompatiblevmas.+*+*FIXME:thisshallberelaxedlatersothatitdoesn'tfail+*ontmpfsbackedvmas(inadditiontothecurrentallowance+*onanonymousvmas).+*/+found=false;+for(cur=vma;cur&&cur->vm_start<end;cur=cur->vm_next){+cond_resched();++BUG_ON(!!cur->vm_userfaultfd_ctx.ctx^+!!(cur->vm_flags&(VM_UFFD_MISSING|VM_UFFD_WP)));++/* check not compatible vmas */+ret=-EINVAL;+if(cur->vm_ops)+gotoout_unlock;++/*+*Checkthatthisvmaisn'talreadyownedbya+*differentuserfaultfd.Wecan'tallowmorethanone+*userfaultfdtoownasinglevmasimultaneouslyorwe+*wouldn'tknowwhichonetodelivertheuserfaultsto.+*/+ret=-EBUSY;+if(cur->vm_userfaultfd_ctx.ctx&&+cur->vm_userfaultfd_ctx.ctx!=ctx)+gotoout_unlock;++found=true;+}+BUG_ON(!found);++if(vma->vm_start<start)+prev=vma;++ret=0;+do{+cond_resched();++BUG_ON(vma->vm_ops);+BUG_ON(vma->vm_userfaultfd_ctx.ctx&&+vma->vm_userfaultfd_ctx.ctx!=ctx);++/*+*Nothingtodo:thisvmaisalreadyregisteredintothis+*userfaultfdandwiththerighttrackingmodetoo.+*/+if(vma->vm_userfaultfd_ctx.ctx==ctx&&+(vma->vm_flags&vm_flags)==vm_flags)+gotoskip;++if(vma->vm_start>start)+start=vma->vm_start;+vma_end=min(end,vma->vm_end);++new_flags=(vma->vm_flags&~vm_flags)|vm_flags;+prev=vma_merge(mm,prev,start,vma_end,new_flags,+vma->anon_vma,vma->vm_file,vma->vm_pgoff,+vma_policy(vma),+((structvm_userfaultfd_ctx){ctx}));+if(prev){+vma=prev;+gotonext;+}+if(vma->vm_start<start){+ret=split_vma(mm,vma,start,1);+if(ret)+break;+}+if(vma->vm_end>end){+ret=split_vma(mm,vma,end,0);+if(ret)+break;+}+next:+/*+*Inthevma_merge()successfulmprotect-likecase8:+*thenextvmawasmergedintothecurrentoneand+*thecurrentonehasnotbeenupdatedyet.+*/+vma->vm_flags=new_flags;+vma->vm_userfaultfd_ctx.ctx=ctx;++skip:+prev=vma;+start=vma->vm_end;+vma=vma->vm_next;+}while(vma&&vma->vm_start<end);+out_unlock:+up_write(&mm->mmap_sem);+if(!ret){+/*+*Nowthatwescannedallvmaswecanalreadytell+*userlandwhichioctlsmethodsareguaranteedto+*succeedonthisrange.+*/+if(put_user(UFFD_API_RANGE_IOCTLS,+&user_uffdio_register->ioctls))+ret=-EFAULT;+}+out:+returnret;+}++staticintuserfaultfd_unregister(structuserfaultfd_ctx*ctx,+unsignedlongarg)+{+structmm_struct*mm=ctx->mm;+structvm_area_struct*vma,*prev,*cur;+intret;+structuffdio_rangeuffdio_unregister;+unsignedlongnew_flags;+boolfound;+unsignedlongstart,end,vma_end;+constvoid__user*buf=(void__user*)arg;++ret=-EFAULT;+if(copy_from_user(&uffdio_unregister,buf,sizeof(uffdio_unregister)))+gotoout;++ret=validate_range(mm,uffdio_unregister.start,+uffdio_unregister.len);+if(ret)+gotoout;++start=uffdio_unregister.start;+end=start+uffdio_unregister.len;++down_write(&mm->mmap_sem);+vma=find_vma_prev(mm,start,&prev);++ret=-ENOMEM;+if(!vma)+gotoout_unlock;++/* check that there's at least one vma in the range */+ret=-EINVAL;+if(vma->vm_start>=end)+gotoout_unlock;++/*+*Searchfornotcompatiblevmas.+*+*FIXME:thisshallberelaxedlatersothatitdoesn'tfail+*ontmpfsbackedvmas(inadditiontothecurrentallowance+*onanonymousvmas).+*/+found=false;+ret=-EINVAL;+for(cur=vma;cur&&cur->vm_start<end;cur=cur->vm_next){+cond_resched();++BUG_ON(!!cur->vm_userfaultfd_ctx.ctx^+!!(cur->vm_flags&(VM_UFFD_MISSING|VM_UFFD_WP)));++/*+*Checknotcompatiblevmas,notstrictlyrequired+*hereasnotcompatiblevmascannothavean+*userfaultfd_ctxregisteredonthem,butthis+*providesformorestrictbehaviortonotice+*unregistrationerrors.+*/+if(cur->vm_ops)+gotoout_unlock;++found=true;+}+BUG_ON(!found);++if(vma->vm_start<start)+prev=vma;++ret=0;+do{+cond_resched();++BUG_ON(vma->vm_ops);++/*+*Nothingtodo:thisvmaisalreadyregisteredintothis+*userfaultfdandwiththerighttrackingmodetoo.+*/+if(!vma->vm_userfaultfd_ctx.ctx)+gotoskip;++if(vma->vm_start>start)+start=vma->vm_start;+vma_end=min(end,vma->vm_end);++new_flags=vma->vm_flags&~(VM_UFFD_MISSING|VM_UFFD_WP);+prev=vma_merge(mm,prev,start,vma_end,new_flags,+vma->anon_vma,vma->vm_file,vma->vm_pgoff,+vma_policy(vma),+NULL_VM_UFFD_CTX);+if(prev){+vma=prev;+gotonext;+}+if(vma->vm_start<start){+ret=split_vma(mm,vma,start,1);+if(ret)+break;+}+if(vma->vm_end>end){+ret=split_vma(mm,vma,end,0);+if(ret)+break;+}+next:+/*+*Inthevma_merge()successfulmprotect-likecase8:+*thenextvmawasmergedintothecurrentoneand+*thecurrentonehasnotbeenupdatedyet.+*/+vma->vm_flags=new_flags;+vma->vm_userfaultfd_ctx=NULL_VM_UFFD_CTX;++skip:+prev=vma;+start=vma->vm_end;+vma=vma->vm_next;+}while(vma&&vma->vm_start<end);+out_unlock:+up_write(&mm->mmap_sem);+out:+returnret;+}++/*+*Thisismostlyneededtore-wakeupthoseuserfaultsthatwerestill+*pendingwhenuserlandwakethemupthefirsttime.Wedon'twake+*thependingonetoavoidblockingreadstoblock,ornonblocking+*readtoreturn-EAGAIN,ifusedwithPOLLIN,toavoiduserland+*doubtsonwhyPOLLINwasn'treliable.+*/+staticintuserfaultfd_wake(structuserfaultfd_ctx*ctx,+unsignedlongarg)+{+intret;+structuffdio_rangeuffdio_wake;+structuserfaultfd_wake_rangerange;+constvoid__user*buf=(void__user*)arg;++ret=-EFAULT;+if(copy_from_user(&uffdio_wake,buf,sizeof(uffdio_wake)))+gotoout;++ret=validate_range(ctx->mm,uffdio_wake.start,uffdio_wake.len);+if(ret)+gotoout;++range.start=uffdio_wake.start;+range.len=uffdio_wake.len;++/*+*len==0meanswakeallandwedon'twanttowakeallhere,+*socheckitagaintobesure.+*/+VM_BUG_ON(!range.len);++wake_userfault(ctx,&range);+ret=0;++out:+returnret;+}++/*+*userlandasksforacertainAPIversionandwereturnwhichbits+*andioctlcommandsareimplementedinthiskernelforsuchAPI+*versionor-EINVALifunknown.+*/+staticintuserfaultfd_api(structuserfaultfd_ctx*ctx,+unsignedlongarg)+{+structuffdio_apiuffdio_api;+void__user*buf=(void__user*)arg;+intret;++ret=-EINVAL;+if(ctx->state!=UFFD_STATE_WAIT_API)+gotoout;+ret=-EFAULT;+if(copy_from_user(&uffdio_api,buf,sizeof(__u64)))+gotoout;+if(uffdio_api.api!=UFFD_API){+/* careful not to leak info, we only read the first 8 bytes */+memset(&uffdio_api,0,sizeof(uffdio_api));+if(copy_to_user(buf,&uffdio_api,sizeof(uffdio_api)))+gotoout;+ret=-EINVAL;+gotoout;+}+/* careful not to leak info, we only read the first 8 bytes */+uffdio_api.bits=UFFD_API_BITS;+uffdio_api.ioctls=UFFD_API_IOCTLS;+ret=-EFAULT;+if(copy_to_user(buf,&uffdio_api,sizeof(uffdio_api)))+gotoout;+ctx->state=UFFD_STATE_RUNNING;+ret=0;+out:+returnret;+}++staticlonguserfaultfd_ioctl(structfile*file,unsignedcmd,+unsignedlongarg)+{+intret=-EINVAL;+structuserfaultfd_ctx*ctx=file->private_data;++switch(cmd){+caseUFFDIO_API:+ret=userfaultfd_api(ctx,arg);+break;+caseUFFDIO_REGISTER:+ret=userfaultfd_register(ctx,arg);+break;+caseUFFDIO_UNREGISTER:+ret=userfaultfd_unregister(ctx,arg);+break;+caseUFFDIO_WAKE:+ret=userfaultfd_wake(ctx,arg);+break;+}+returnret;+}++#ifdef CONFIG_PROC_FS+staticvoiduserfaultfd_show_fdinfo(structseq_file*m,structfile*f)+{+structuserfaultfd_ctx*ctx=f->private_data;+wait_queue_t*wq;+structuserfaultfd_wait_queue*uwq;+unsignedlongpending=0,total=0;++spin_lock(&ctx->fault_wqh.lock);+list_for_each_entry(wq,&ctx->fault_wqh.task_list,task_list){+uwq=container_of(wq,structuserfaultfd_wait_queue,wq);+if(uwq->pending)+pending++;+total++;+}+spin_unlock(&ctx->fault_wqh.lock);++/*+*Ifmoreprotocolswillbeadded,therewillbeallshown+*separatedbyaspace.Likethis:+*protocols:aa:...bb:...+*/+seq_printf(m,"pending:\t%lu\ntotal:\t%lu\nAPI:\t%Lx:%x:%Lx\n",+pending,total,UFFD_API,UFFD_API_BITS,+UFFD_API_IOCTLS|UFFD_API_RANGE_IOCTLS);+}+#endif++staticconststructfile_operationsuserfaultfd_fops={+#ifdef CONFIG_PROC_FS+.show_fdinfo=userfaultfd_show_fdinfo,+#endif+.release=userfaultfd_release,+.poll=userfaultfd_poll,+.read=userfaultfd_read,+.unlocked_ioctl=userfaultfd_ioctl,+.compat_ioctl=userfaultfd_ioctl,+.llseek=noop_llseek,+};++/**+*userfaultfd_file_create-Createsanuserfaultfdfilepointer.+*@flags:Flagsfortheuserfaultfdfile.+*+*Thisfunctioncreatesanuserfaultfdfilepointer,w/outinstalling+*itintothefdtable.Thisisusefulwhentheuserfaultfdfileis+*usedduringtheinitializationofdatastructuresthatrequire+*extrasetupaftertheuserfaultfdcreation.Sotheuserfaultfd+*creationissplitintothefilepointercreationphase,andthe+*filedescriptorinstallationphase.Inthiswayraceswith+*userspaceclosingthenewlyinstalledfiledescriptorcanbe+*avoided.Returnsanuserfaultfdfilepointer,orapropererror+*pointer.+*/+staticstructfile*userfaultfd_file_create(intflags)+{+structfile*file;+structuserfaultfd_ctx*ctx;++BUG_ON(!current->mm);++/* Check the UFFD_* constants for consistency. */+BUILD_BUG_ON(UFFD_CLOEXEC!=O_CLOEXEC);+BUILD_BUG_ON(UFFD_NONBLOCK!=O_NONBLOCK);++file=ERR_PTR(-EINVAL);+if(flags&~UFFD_SHARED_FCNTL_FLAGS)+gotoout;++file=ERR_PTR(-ENOMEM);+ctx=kmalloc(sizeof(*ctx),GFP_KERNEL);+if(!ctx)+gotoout;++atomic_set(&ctx->refcount,1);+init_waitqueue_head(&ctx->fault_wqh);+init_waitqueue_head(&ctx->fd_wqh);+ctx->flags=flags;+ctx->state=UFFD_STATE_WAIT_API;+ctx->released=false;+ctx->mm=current->mm;+/* prevent the mm struct to be freed */+atomic_inc(&ctx->mm->mm_users);++file=anon_inode_getfile("[userfaultfd]",&userfaultfd_fops,ctx,+O_RDWR|(flags&UFFD_SHARED_FCNTL_FLAGS));+if(IS_ERR(file))+kfree(ctx);+out:+returnfile;+}++SYSCALL_DEFINE1(userfaultfd,int,flags)+{+intfd,error;+structfile*file;++error=get_unused_fd_flags(flags&UFFD_SHARED_FCNTL_FLAGS);+if(error<0)+returnerror;+fd=error;++file=userfaultfd_file_create(flags);+if(IS_ERR(file)){+error=PTR_ERR(file);+gotoerr_put_unused_fd;+}+fd_install(fd,file);++returnfd;++err_put_unused_fd:+put_unused_fd(fd);++returnerror;+}
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:33:03
These two ioctl allows to either atomically copy or to map zeropages
into the virtual address space. This is used by the thread that opened
the userfaultfd to resolve the userfaults.
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/userfaultfd.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
@@ -942,6 +942,96 @@ out:returnret;}+staticintuserfaultfd_copy(structuserfaultfd_ctx*ctx,+unsignedlongarg)+{+__s64ret;+structuffdio_copyuffdio_copy;+structuffdio_copy__user*user_uffdio_copy;+structuserfaultfd_wake_rangerange;++user_uffdio_copy=(structuffdio_copy__user*)arg;++ret=-EFAULT;+if(copy_from_user(&uffdio_copy,user_uffdio_copy,+/* don't copy "copy" last field */+sizeof(uffdio_copy)-sizeof(__s64)))+gotoout;++ret=validate_range(ctx->mm,uffdio_copy.dst,uffdio_copy.len);+if(ret)+gotoout;+/*+*doublecheckforwraparoundjustincase.copy_from_user()+*willlatercheckuffdio_copy.src+uffdio_copy.lentofit+*intheuserlandrange.+*/+ret=-EINVAL;+if(uffdio_copy.src+uffdio_copy.len<=uffdio_copy.src)+gotoout;+if(uffdio_copy.mode&~UFFDIO_COPY_MODE_DONTWAKE)+gotoout;++ret=mcopy_atomic(ctx->mm,uffdio_copy.dst,uffdio_copy.src,+uffdio_copy.len);+if(unlikely(put_user(ret,&user_uffdio_copy->copy)))+return-EFAULT;+if(ret<0)+gotoout;+BUG_ON(!ret);+/* len == 0 would wake all */+range.len=ret;+if(!(uffdio_copy.mode&UFFDIO_COPY_MODE_DONTWAKE)){+range.start=uffdio_copy.dst;+wake_userfault(ctx,&range);+}+ret=range.len==uffdio_copy.len?0:-EAGAIN;+out:+returnret;+}++staticintuserfaultfd_zeropage(structuserfaultfd_ctx*ctx,+unsignedlongarg)+{+__s64ret;+structuffdio_zeropageuffdio_zeropage;+structuffdio_zeropage__user*user_uffdio_zeropage;+structuserfaultfd_wake_rangerange;++user_uffdio_zeropage=(structuffdio_zeropage__user*)arg;++ret=-EFAULT;+if(copy_from_user(&uffdio_zeropage,user_uffdio_zeropage,+/* don't copy "zeropage" last field */+sizeof(uffdio_zeropage)-sizeof(__s64)))+gotoout;++ret=validate_range(ctx->mm,uffdio_zeropage.range.start,+uffdio_zeropage.range.len);+if(ret)+gotoout;+ret=-EINVAL;+if(uffdio_zeropage.mode&~UFFDIO_ZEROPAGE_MODE_DONTWAKE)+gotoout;++ret=mfill_zeropage(ctx->mm,uffdio_zeropage.range.start,+uffdio_zeropage.range.len);+if(unlikely(put_user(ret,&user_uffdio_zeropage->zeropage)))+return-EFAULT;+if(ret<0)+gotoout;+/* len == 0 would wake all */+BUG_ON(!ret);+range.len=ret;+if(!(uffdio_zeropage.mode&UFFDIO_ZEROPAGE_MODE_DONTWAKE)){+range.start=uffdio_zeropage.range.start;+wake_userfault(ctx,&range);+}+ret=range.len==uffdio_zeropage.range.len?0:-EAGAIN;+out:+returnret;+}+/**userlandasksforacertainAPIversionandwereturnwhichbits*andioctlcommandsareimplementedinthiskernelforsuchAPI
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -0,0 +1,140 @@+= Userfaultfd =++== Objective ==++Userfaults allow the implementation of on-demand paging from userland+and more generally they allow userland to take control various memory+page faults, something otherwise only the kernel code could do.++For example userfaults allows a proper and more optimal implementation+of the PROT_NONE+SIGSEGV trick.++== Design ==++Userfaults are delivered and resolved through the userfaultfd syscall.++The userfaultfd (aside from registering and unregistering virtual+memory ranges) provides two primary functionalities:++1) read/POLLIN protocol to notify a userland thread of the faults+ happening++2) various UFFDIO_* ioctls that can manage the virtual memory regions+ registered in the userfaultfd that allows userland to efficiently+ resolve the userfaults it receives via 1) or to manage the virtual+ memory in the background++The real advantage of userfaults if compared to regular virtual memory+management of mremap/mprotect is that the userfaults in all their+operations never involve heavyweight structures like vmas (in fact the+userfaultfd runtime load never takes the mmap_sem for writing).++Vmas are not suitable for page- (or hugepage) granular fault tracking+when dealing with virtual address spaces that could span+Terabytes. Too many vmas would be needed for that.++The userfaultfd once opened by invoking the syscall, can also be+passed using unix domain sockets to a manager process, so the same+manager process could handle the userfaults of a multitude of+different processes without them being aware about what is going on+(well of course unless they later try to use the userfaultfd+themselves on the same region the manager is already tracking, which+is a corner case that would currently return -EBUSY).++== API ==++When first opened the userfaultfd must be enabled invoking the+UFFDIO_API ioctl specifying a uffdio_api.api value set to UFFD_API (or+a later API version) which will specify the read/POLLIN protocol+userland intends to speak on the UFFD. The UFFDIO_API ioctl if+successful (i.e. if the requested uffdio_api.api is spoken also by the+running kernel), will return into uffdio_api.features and+uffdio_api.ioctls two 64bit bitmasks of respectively the activated+feature of the read(2) protocol and the generic ioctl available.++Once the userfaultfd has been enabled the UFFDIO_REGISTER ioctl should+be invoked (if present in the returned uffdio_api.ioctls bitmask) to+register a memory range in the userfaultfd by setting the+uffdio_register structure accordingly. The uffdio_register.mode+bitmask will specify to the kernel which kind of faults to track for+the range (UFFDIO_REGISTER_MODE_MISSING would track missing+pages). The UFFDIO_REGISTER ioctl will return the+uffdio_register.ioctls bitmask of ioctls that are suitable to resolve+userfaults on the range registered. Not all ioctls will necessarily be+supported for all memory types depending on the underlying virtual+memory backend (anonymous memory vs tmpfs vs real filebacked+mappings).++Userland can use the uffdio_register.ioctls to manage the virtual+address space in the background (to add or potentially also remove+memory from the userfaultfd registered range). This means a userfault+could be triggering just before userland maps in the background the+user-faulted page.++The primary ioctl to resolve userfaults is UFFDIO_COPY. That+atomically copies a page into the userfault registered range and wakes+up the blocked userfaults (unless uffdio_copy.mode &+UFFDIO_COPY_MODE_DONTWAKE is set). Other ioctl works similarly to+UFFDIO_COPY.++== QEMU/KVM ==++QEMU/KVM is using the userfaultfd syscall to implement postcopy live+migration. Postcopy live migration is one form of memory+externalization consisting of a virtual machine running with part or+all of its memory residing on a different node in the cloud. The+userfaultfd abstraction is generic enough that not a single line of+KVM kernel code had to be modified in order to add postcopy live+migration to QEMU.++Guest async page faults, FOLL_NOWAIT and all other GUP features work+just fine in combination with userfaults. Userfaults trigger async+page faults in the guest scheduler so those guest processes that+aren't waiting for userfaults (i.e. network bound) can keep running in+the guest vcpus.++It is generally beneficial to run one pass of precopy live migration+just before starting postcopy live migration, in order to avoid+generating userfaults for readonly guest regions.++The implementation of postcopy live migration currently uses one+single bidirectional socket but in the future two different sockets+will be used (to reduce the latency of the userfaults to the minimum+possible without having to decrease /proc/sys/net/ipv4/tcp_wmem).++The QEMU in the source node writes all pages that it knows are missing+in the destination node, into the socket, and the migration thread of+the QEMU running in the destination node runs UFFDIO_COPY|ZEROPAGE+ioctls on the userfaultfd in order to map the received pages into the+guest (UFFDIO_ZEROCOPY is used if the source page was a zero page).++A different postcopy thread in the destination node listens with+poll() to the userfaultfd in parallel. When a POLLIN event is+generated after a userfault triggers, the postcopy thread read() from+the userfaultfd and receives the fault address (or -EAGAIN in case the+userfault was already resolved and waken by a UFFDIO_COPY|ZEROPAGE run+by the parallel QEMU migration thread).++After the QEMU postcopy thread (running in the destination node) gets+the userfault address it writes the information about the missing page+into the socket. The QEMU source node receives the information and+roughly "seeks" to that page address and continues sending all+remaining missing pages from that new page offset. Soon after that+(just the time to flush the tcp_wmem queue through the network) the+migration thread in the QEMU running in the destination node will+receive the page that triggered the userfault and it'll map it as+usual with the UFFDIO_COPY|ZEROPAGE (without actually knowing if it+was spontaneously sent by the source or if it was an urgent page+requested through an userfault).++By the time the userfaults start, the QEMU in the destination node+doesn't need to keep any per-page state bitmap relative to the live+migration around and a single per-page bitmap has to be maintained in+the QEMU running in the source node to know which pages are still+missing in the destination node. The bitmap in the source node is+checked to find which missing pages to send in round robin and we seek+over it when receiving incoming userfaults. After sending each page of+course the bitmap is updated accordingly. It's also useful to avoid+sending the same page twice (in case the userfault is read by the+postcopy thread just before UFFDIO_COPY|ZEROPAGE runs in the migration+thread).--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:33:56
I had requests to return the full address (not the page aligned one)
to userland.
It's not entirely clear how the page offset could be relevant because
userfaults aren't like SIGBUS that can sigjump to a different place
and it actually skip resolving the fault depending on a page
offset. There's currently no real way to skip the fault especially
because after a UFFDIO_COPY|ZEROPAGE, the fault is optimized to be
retried within the kernel without having to return to userland first
(not even self modifying code replacing the .text that touched the
faulting address would prevent the fault to be repeated). Userland
cannot skip repeating the fault even more so if the fault was
triggered by a KVM secondary page fault or any get_user_pages or any
copy-user inside some syscall which will return to kernel code. The
second time FAULT_FLAG_RETRY_NOWAIT won't be set leading to a SIGBUS
being raised because the userfault can't wait if it cannot release the
mmap_map first (and FAULT_FLAG_RETRY_NOWAIT is required for that).
Still returning userland a proper structure during the read() on the
uffd, can allow to use the current UFFD_API for the future
non-cooperative extensions too and it looks cleaner as well. Once we
get additional fields there's no point to return the fault address
page aligned anymore to reuse the bits below PAGE_SHIFT.
The only downside is that the read() syscall will read 32bytes instead
of 8bytes but that's not going to be measurable overhead.
The total number of new events that can be extended or of new future
bits for already shipped events, is limited to 64 by the features
field of the uffdio_api structure. If more will be needed a bump of
UFFD_API will be required.
Signed-off-by: Andrea Arcangeli <redacted>
---
Documentation/vm/userfaultfd.txt | 12 +++---
fs/userfaultfd.c | 79 +++++++++++++++++++++++-----------------
include/uapi/linux/userfaultfd.h | 64 ++++++++++++++++++++++++--------
3 files changed, 102 insertions(+), 53 deletions(-)
@@ -46,11 +46,13 @@ is a corner case that would currently return -EBUSY). When first opened the userfaultfd must be enabled invoking the UFFDIO_API ioctl specifying a uffdio_api.api value set to UFFD_API (or a later API version) which will specify the read/POLLIN protocol-userland intends to speak on the UFFD. The UFFDIO_API ioctl if-successful (i.e. if the requested uffdio_api.api is spoken also by the-running kernel), will return into uffdio_api.features and-uffdio_api.ioctls two 64bit bitmasks of respectively the activated-feature of the read(2) protocol and the generic ioctl available.+userland intends to speak on the UFFD and the uffdio_api.features+userland needs to be enabled. The UFFDIO_API ioctl if successful+(i.e. if the requested uffdio_api.api is spoken also by the running+kernel and the requested features are going to be enabled) will return+into uffdio_api.features and uffdio_api.ioctls two 64bit bitmasks of+respectively all the available features of the read(2) protocol and+the generic ioctl available. Once the userfaultfd has been enabled the UFFDIO_REGISTER ioctl should be invoked (if present in the returned uffdio_api.ioctls bitmask) to
@@ -77,7 +77,8 @@ static int userfaultfd_wake_function(wait_queue_t *wq, unsigned mode,/* len == 0 means wake all */start=range->start;len=range->len;-if(len&&(start>uwq->address||start+len<=uwq->address))+if(len&&(start>uwq->msg.arg.pagefault.address||+start+len<=uwq->msg.arg.pagefault.address))gotoout;ret=wake_up_state(wq->private,mode);if(ret)
@@ -403,8 +419,8 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,*disappearfromunderus.*/uwq->pending=false;-/* careful to always initialize addr if ret == 0 */-*addr=uwq->address;+/* careful to always initialize msg if ret == 0 */+*msg=uwq->msg;spin_unlock(&ctx->fault_wqh.lock);ret=0;break;
@@ -434,8 +450,7 @@ static ssize_t userfaultfd_read(struct file *file, char __user *buf,{structuserfaultfd_ctx*ctx=file->private_data;ssize_t_ret,ret=0;-/* careful to always initialize addr if ret == 0 */-__u64uninitialized_var(addr);+structuffd_msgmsg;intno_wait=file->f_flags&O_NONBLOCK;if(ctx->state==UFFD_STATE_WAIT_API)
@@ -845,17 +860,15 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx,if(ctx->state!=UFFD_STATE_WAIT_API)gotoout;ret=-EFAULT;-if(copy_from_user(&uffdio_api,buf,sizeof(__u64)))+if(copy_from_user(&uffdio_api,buf,sizeof(uffdio_api)))gotoout;-if(uffdio_api.api!=UFFD_API){-/* careful not to leak info, we only read the first 8 bytes */+if(uffdio_api.api!=UFFD_API||uffdio_api.features){memset(&uffdio_api,0,sizeof(uffdio_api));if(copy_to_user(buf,&uffdio_api,sizeof(uffdio_api)))gotoout;ret=-EINVAL;gotoout;}-/* careful not to leak info, we only read the first 8 bytes */uffdio_api.features=UFFD_API_FEATURES;uffdio_api.ioctls=UFFD_API_IOCTLS;ret=-EFAULT;
@@ -10,8 +10,12 @@#define _LINUX_USERFAULTFD_H#define UFFD_API ((__u64)0xAA)-/* FIXME: add "|UFFD_FEATURE_WP" to UFFD_API_FEATURES after implementing it */-#define UFFD_API_FEATURES (UFFD_FEATURE_WRITE_BIT)+/*+*Afterimplementingtherespectivefeaturesitwillbecome:+*#defineUFFD_API_FEATURES(UFFD_FEATURE_PAGEFAULT_FLAG_WP|\+*UFFD_FEATURE_EVENT_FORK)+*/+#define UFFD_API_FEATURES (0)#define UFFD_API_IOCTLS \((__u64)1<<_UFFDIO_REGISTER|\(__u64)1<<_UFFDIO_UNREGISTER|\
@@ -43,26 +47,56 @@#define UFFDIO_WAKE _IOR(UFFDIO, _UFFDIO_WAKE, \structuffdio_range)-/*-*ValidbitsbelowPAGE_SHIFTintheuserfaultaddressreadthrough-*theread()syscall.-*/-#define UFFD_BIT_WRITE (1<<0) /* this was a write fault, MISSING or WP */-#define UFFD_BIT_WP (1<<1) /* handle_userfault() reason VM_UFFD_WP */-#define UFFD_BITS 2 /* two above bits used for UFFD_BIT_* mask */+/* read() structure */+structuffd_msg{+__u8event;++union{+struct{+__u32flags;+__u64address;+}pagefault;++struct{+/* unused reserved fields */+__u64reserved1;+__u64reserved2;+__u64reserved3;+}reserved;+}arg;+};/*-*Featuresreportedinuffdio_api.featuresfield+*Startat0x12andnotat0tobemorestrictagainstbugs.*/-#define UFFD_FEATURE_WRITE_BIT (1<<0) /* Corresponds to UFFD_BIT_WRITE */-#define UFFD_FEATURE_WP_BIT (1<<1) /* Corresponds to UFFD_BIT_WP */+#define UFFD_EVENT_PAGEFAULT 0x12+#if 0 /* not available yet */+#define UFFD_EVENT_FORK 0x13+#endif++/* flags for UFFD_EVENT_PAGEFAULT */+#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */+#define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */structuffdio_api{-/* userland asks for an API number */+/* userland asks for an API number and the features to enable */__u64api;--/* kernel answers below with the available features for the API */+/*+*Kernelanswersbelowwiththeallavailablefeaturesfor+*theAPI,thisnotifiesuserlandofwhicheventsand/or+*whichflagsforeacheventareenabledinthecurrent+*kernel.+*+*Note:UFFD_EVENT_PAGEFAULTandUFFD_PAGEFAULT_FLAG_WRITE+*aretobeconsideredimplicitlyalwaysenabledinallkernelsas+*longastheuffdio_api.apirequestedmatchesUFFD_API.+*/+#if 0 /* not available yet */+#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0)+#define UFFD_FEATURE_EVENT_FORK (1<<1)+#endif__u64features;+__u64ioctls;};--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:33:57
This is where the page faults must be modified to call
handle_userfault() if userfaultfd_missing() is true (so if the
vma->vm_flags had VM_UFFD_MISSING set).
handle_userfault() then takes care of blocking the page fault and
delivering it to userland.
The fault flags must also be passed as parameter so the "read|write"
kind of fault can be passed to userland.
Signed-off-by: Andrea Arcangeli <redacted>
---
mm/huge_memory.c | 69 ++++++++++++++++++++++++++++++++++++++------------------
mm/memory.c | 16 +++++++++++++
2 files changed, 63 insertions(+), 22 deletions(-)
@@ -2680,6 +2681,12 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,page_table=pte_offset_map_lock(mm,pmd,address,&ptl);if(!pte_none(*page_table))gotounlock;+/* Deliver the page fault to userland, check inside PT lock */+if(userfaultfd_missing(vma)){+pte_unmap_unlock(page_table,ptl);+returnhandle_userfault(vma,address,flags,+VM_UFFD_MISSING);+}gotosetpte;}
@@ -2707,6 +2714,15 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma,if(!pte_none(*page_table))gotorelease;+/* Deliver the page fault to userland, check inside PT lock */+if(userfaultfd_missing(vma)){+pte_unmap_unlock(page_table,ptl);+mem_cgroup_cancel_charge(page,memcg);+page_cache_release(page);+returnhandle_userfault(vma,address,flags,+VM_UFFD_MISSING);+}+inc_mm_counter_fast(mm,MM_ANONPAGES);page_add_new_anon_rmap(page,vma,address);mem_cgroup_commit_charge(page,memcg,false);--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:34:57
If userfaultfd is armed on a certain vma we can't "fill" the holes
with zeroes or we'll break the userland on demand paging. The holes if
the userfault is armed, are really missing information (not zeroes)
that the userland has to load from network or elsewhere.
The same issue happens for wrprotected ptes that we can't just convert
into a single writable pmd_trans_huge.
We could however in theory still merge across zeropages if only
VM_UFFD_MISSING is set (so if VM_UFFD_WP is not set)... that could be
slightly improved but it'd be much more complex code for a tiny corner
case.
Signed-off-by: Andrea Arcangeli <redacted>
---
mm/huge_memory.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
@@ -2198,7 +2198,8 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma,_pte++,address+=PAGE_SIZE){pte_tpteval=*_pte;if(pte_none(pteval)||is_zero_pfn(pte_pfn(pteval))){-if(++none_or_zero<=khugepaged_max_ptes_none)+if(!userfaultfd_armed(vma)&&+++none_or_zero<=khugepaged_max_ptes_none)continue;elsegotoout;
@@ -2651,7 +2652,8 @@ static int khugepaged_scan_pmd(struct mm_struct *mm,_pte++,_address+=PAGE_SIZE){pte_tpteval=*_pte;if(pte_none(pteval)||is_zero_pfn(pte_pfn(pteval))){-if(++none_or_zero<=khugepaged_max_ptes_none)+if(!userfaultfd_armed(vma)&&+++none_or_zero<=khugepaged_max_ptes_none)continue;elsegotoout_unmap;--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:35:00
This is an optimization but it's a userland visible one and it affects
the API.
The downside of this optimization is that if you call poll() and you
get POLLIN, read(ufd) may still return -EAGAIN. The blocked userfault
may be waken by a different thread, before read(ufd) comes
around. This in short means that poll() isn't really usable if the
userfaultfd is opened in blocking mode.
userfaults won't wait in "pending" state to be read anymore and any
UFFDIO_WAKE or similar operations that has the objective of waking
userfaults after their resolution, will wake all blocked userfaults
for the resolved range, including those that haven't been read() by
userland yet.
The behavior of poll() becomes not standard, but this obviates the
need of "spurious" UFFDIO_WAKE and it lets the userland threads to
restart immediately without requiring an UFFDIO_WAKE. This is even
more significant in case of repeated faults on the same address from
multiple threads.
This optimization is justified by the measurement that the number of
spurious UFFDIO_WAKE accounts for 5% and 10% of the total
userfaults for heavy workloads, so it's worth optimizing those away.
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/userfaultfd.c | 65 +++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 43 insertions(+), 22 deletions(-)
@@ -71,9 +75,6 @@ static int userfaultfd_wake_function(wait_queue_t *wq, unsigned mode,uwq=container_of(wq,structuserfaultfd_wait_queue,wq);ret=0;-/* don't wake the pending ones to avoid reads to block */-if(uwq->pending&&!ACCESS_ONCE(uwq->ctx->released))-gotoout;/* len == 0 means wake all */start=range->start;len=range->len;
@@ -183,12 +184,14 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,structmm_struct*mm=vma->vm_mm;structuserfaultfd_ctx*ctx;structuserfaultfd_wait_queueuwq;+intret;BUG_ON(!rwsem_is_locked(&mm->mmap_sem));+ret=VM_FAULT_SIGBUS;ctx=vma->vm_userfaultfd_ctx.ctx;if(!ctx)-returnVM_FAULT_SIGBUS;+gotoout;BUG_ON(ctx->mm!=mm);
@@ -201,7 +204,7 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,*callerofhandle_userfaulttoreleasethemmap_sem.*/if(unlikely(ACCESS_ONCE(ctx->released)))-returnVM_FAULT_SIGBUS;+gotoout;/**CheckthatwecanreturnVM_FAULT_RETRY.
@@ -227,15 +230,16 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,dump_stack();}#endif-returnVM_FAULT_SIGBUS;+gotoout;}/**Handlenowait,notmuchtodootherthantellittoretry*andwait.*/+ret=VM_FAULT_RETRY;if(flags&FAULT_FLAG_RETRY_NOWAIT)-returnVM_FAULT_RETRY;+gotoout;/* take the reference before dropping the mmap_sem */userfaultfd_ctx_get(ctx);
@@ -255,21 +259,23 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,*throughpoll/read().*/__add_wait_queue(&ctx->fault_wqh,&uwq.wq);-for(;;){-set_current_state(TASK_KILLABLE);-if(!uwq.pending||ACCESS_ONCE(ctx->released)||-fatal_signal_pending(current))-break;-spin_unlock(&ctx->fault_wqh.lock);+set_current_state(TASK_KILLABLE);+spin_unlock(&ctx->fault_wqh.lock);+if(likely(!ACCESS_ONCE(ctx->released)&&+!fatal_signal_pending(current))){wake_up_poll(&ctx->fd_wqh,POLLIN);schedule();+ret|=VM_FAULT_MAJOR;+}+__set_current_state(TASK_RUNNING);+/* see finish_wait() comment for why list_empty_careful() */+if(!list_empty_careful(&uwq.wq.task_list)){spin_lock(&ctx->fault_wqh.lock);+list_del_init(&uwq.wq.task_list);+spin_unlock(&ctx->fault_wqh.lock);}-__remove_wait_queue(&ctx->fault_wqh,&uwq.wq);-__set_current_state(TASK_RUNNING);-spin_unlock(&ctx->fault_wqh.lock);/**ctxmaygoawayafterthisiftheuserfaultpseudofdis
@@ -277,7 +283,8 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,*/userfaultfd_ctx_put(ctx);-returnVM_FAULT_RETRY;+out:+returnret;}staticintuserfaultfd_release(structinode*inode,structfile*file)
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -10,7 +10,7 @@#define _LINUX_USERFAULTFD_H#define UFFD_API ((__u64)0xAA)-/* FIXME: add "|UFFD_BIT_WP" to UFFD_API_BITS after implementing it */+/* FIXME: add "|UFFD_FEATURE_WP" to UFFD_API_FEATURES after implementing it */#define UFFD_API_FEATURES (UFFD_FEATURE_WRITE_BIT)#define UFFD_API_IOCTLS \((__u64)1<<_UFFDIO_REGISTER|\--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:36:19
From: Pavel Emelyanov <redacted>
This is (seem to be) the minimal thing that is required to unblock
standard uffd usage from the non-cooperative one. Now more bits can
be added to the features field indicating e.g. UFFD_FEATURE_FORK and
others needed for the latter use-case.
Signed-off-by: Pavel Emelyanov <redacted>
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/userfaultfd.c | 4 ++--
include/uapi/linux/userfaultfd.h | 10 ++++++++--
2 files changed, 10 insertions(+), 4 deletions(-)
@@ -856,7 +856,7 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx,gotoout;}/* careful not to leak info, we only read the first 8 bytes */-uffdio_api.bits=UFFD_API_BITS;+uffdio_api.features=UFFD_API_FEATURES;uffdio_api.ioctls=UFFD_API_IOCTLS;ret=-EFAULT;if(copy_to_user(buf,&uffdio_api,sizeof(uffdio_api)))
@@ -11,7 +11,7 @@#define UFFD_API ((__u64)0xAA)/* FIXME: add "|UFFD_BIT_WP" to UFFD_API_BITS after implementing it */-#define UFFD_API_BITS (UFFD_BIT_WRITE)+#define UFFD_API_FEATURES (UFFD_FEATURE_WRITE_BIT)#define UFFD_API_IOCTLS \((__u64)1<<_UFFDIO_REGISTER|\(__u64)1<<_UFFDIO_UNREGISTER|\
@@ -51,12 +51,18 @@#define UFFD_BIT_WP (1<<1) /* handle_userfault() reason VM_UFFD_WP */#define UFFD_BITS 2 /* two above bits used for UFFD_BIT_* mask */+/*+*Featuresreportedinuffdio_api.featuresfield+*/+#define UFFD_FEATURE_WRITE_BIT (1<<0) /* Corresponds to UFFD_BIT_WRITE */+#define UFFD_FEATURE_WP_BIT (1<<1) /* Corresponds to UFFD_BIT_WP */+structuffdio_api{/* userland asks for an API number */__u64api;/* kernel answers below with the available features for the API */-__u64bits;+__u64features;__u64ioctls;};--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:36:24
Kernel header defining the methods needed by the VM common code to
interact with the userfaultfd.
Signed-off-by: Andrea Arcangeli <redacted>
---
include/linux/userfaultfd_k.h | 79 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
create mode 100644 include/linux/userfaultfd_k.h
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:36:29
If the rwsem starves writers it wasn't strictly a bug but lockdep
doesn't like it and this avoids depending on lowlevel implementation
details of the lock.
Signed-off-by: Andrea Arcangeli <redacted>
---
mm/userfaultfd.c | 92 ++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 66 insertions(+), 26 deletions(-)
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:39:47
This implements the uABI of UFFDIO_COPY and UFFDIO_ZEROPAGE.
Signed-off-by: Andrea Arcangeli <redacted>
---
include/uapi/linux/userfaultfd.h | 42 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:39:54
This makes read O(1) and poll that was already O(1) becomes lockless.
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/userfaultfd.c | 172 +++++++++++++++++++++++++++++++------------------------
1 file changed, 98 insertions(+), 74 deletions(-)
@@ -35,7 +35,9 @@ enum userfaultfd_state {structuserfaultfd_ctx{/* pseudo fd refcounting */atomic_trefcount;-/* waitqueue head for the userfaultfd page faults */+/* waitqueue head for the pending (i.e. not read) userfaults */+wait_queue_head_tfault_pending_wqh;+/* waitqueue head for the userfaults */wait_queue_head_tfault_wqh;/* waitqueue head for the pseudo fd to wakeup poll/read */wait_queue_head_tfd_wqh;
@@ -250,17 +247,21 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,init_waitqueue_func_entry(&uwq.wq,userfaultfd_wake_function);uwq.wq.private=current;uwq.msg=userfault_msg(address,flags,reason);-uwq.pending=true;uwq.ctx=ctx;-spin_lock(&ctx->fault_wqh.lock);+spin_lock(&ctx->fault_pending_wqh.lock);/**Afterthe__add_wait_queuetheuwqisvisibletouserland*throughpoll/read().*/-__add_wait_queue(&ctx->fault_wqh,&uwq.wq);+__add_wait_queue(&ctx->fault_pending_wqh,&uwq.wq);+/*+*Thesmp_mb()after__set_current_statepreventsthereads+*followingthespin_unlocktohappenbeforethelist_addin+*__add_wait_queue.+*/set_current_state(TASK_KILLABLE);-spin_unlock(&ctx->fault_wqh.lock);+spin_unlock(&ctx->fault_pending_wqh.lock);if(likely(!ACCESS_ONCE(ctx->released)&&!fatal_signal_pending(current))){
@@ -270,11 +271,28 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address,}__set_current_state(TASK_RUNNING);-/* see finish_wait() comment for why list_empty_careful() */++/*+*Hereweracewiththelist_del;list_addin+*userfaultfd_ctx_read(),howeverbecausewedon'teverrun+*list_del_init()torefileacrossthetwolists,theprev+*andnextpointerswillneverpointtoself.list_addalso+*wouldneverletanyofthetwopointerstopointto+*self.Solist_empty_carefulwon'trisktoseebothpointers+*pointingtoselfatanytimeduringthelistrefile.The+*onlycasewherelist_del_init()iscalledisthefull+*removalinthewakefunctionandtherewedon'tre-list_add+*andit'sfinenottoblockonthespinlock.Theuwqonthis+*kernelstackcanbereleasedafterthelist_del_init.+*/if(!list_empty_careful(&uwq.wq.task_list)){-spin_lock(&ctx->fault_wqh.lock);-list_del_init(&uwq.wq.task_list);-spin_unlock(&ctx->fault_wqh.lock);+spin_lock(&ctx->fault_pending_wqh.lock);+/*+*Noneedoflist_del_init(),theuwqonthestack+*willbefreedshortlyanyway.+*/+list_del(&uwq.wq.task_list);+spin_unlock(&ctx->fault_pending_wqh.lock);}/*
@@ -332,59 +350,38 @@ static int userfaultfd_release(struct inode *inode, struct file *file)up_write(&mm->mmap_sem);/*-*Afternonewpagefaultscanwaitonthisfault_wqh,flush+*Afternonewpagefaultscanwaitonthisfault_*wqh,flush*thelastpagefaultsthatmayhavebeenalreadywaitingon-*thefault_wqh.+*thefault_*wqh.*/-spin_lock(&ctx->fault_wqh.lock);+spin_lock(&ctx->fault_pending_wqh.lock);+__wake_up_locked_key(&ctx->fault_pending_wqh,TASK_NORMAL,0,&range);__wake_up_locked_key(&ctx->fault_wqh,TASK_NORMAL,0,&range);-spin_unlock(&ctx->fault_wqh.lock);+spin_unlock(&ctx->fault_pending_wqh.lock);wake_up_poll(&ctx->fd_wqh,POLLHUP);userfaultfd_ctx_put(ctx);return0;}-/* fault_wqh.lock must be hold by the caller */-staticinlineunsignedintfind_userfault(structuserfaultfd_ctx*ctx,-structuserfaultfd_wait_queue**uwq)+/* fault_pending_wqh.lock must be hold by the caller */+staticinlinestructuserfaultfd_wait_queue*find_userfault(+structuserfaultfd_ctx*ctx){wait_queue_t*wq;-structuserfaultfd_wait_queue*_uwq;-unsignedintret=0;--VM_BUG_ON(!spin_is_locked(&ctx->fault_wqh.lock));+structuserfaultfd_wait_queue*uwq;-list_for_each_entry(wq,&ctx->fault_wqh.task_list,task_list){-_uwq=container_of(wq,structuserfaultfd_wait_queue,wq);-if(_uwq->pending){-ret=POLLIN;-if(!uwq)-/*-*Ifthere'satleastapendingand-*wedon'tcarewhichoneitis,-*breakimmediatelyandleveragethe-*efficiencyoftheLIFOwalk.-*/-break;-/*-*Ifweneedtofindwhichonewaspendingwe-*keepwalkinguntilwefindthefirstnot-*pendingone,soweread()theminFIFOorder.-*/-*uwq=_uwq;-}else-/*-*breaktheloopatthefirstnotpending-*one,therecannotbependinguserfaults-*afterthefirstnotpendingone,because-*allnewpendingonesareinsertedatthe-*headandwewalkitinLIFO.-*/-break;-}+VM_BUG_ON(!spin_is_locked(&ctx->fault_pending_wqh.lock));-returnret;+uwq=NULL;+if(!waitqueue_active(&ctx->fault_pending_wqh))+gotoout;+/* walk in reverse to provide FIFO behavior to read userfaults */+wq=list_last_entry(&ctx->fault_pending_wqh.task_list,+typeof(*wq),task_list);+uwq=container_of(wq,structuserfaultfd_wait_queue,wq);+out:+returnuwq;}staticunsignedintuserfaultfd_poll(structfile*file,poll_table*wait)
@@ -418,27 +426,34 @@ static ssize_t userfaultfd_ctx_read(struct userfaultfd_ctx *ctx, int no_wait,{ssize_tret;DECLARE_WAITQUEUE(wait,current);-structuserfaultfd_wait_queue*uwq=NULL;+structuserfaultfd_wait_queue*uwq;-/* always take the fd_wqh lock before the fault_wqh lock */+/* always take the fd_wqh lock before the fault_pending_wqh lock */spin_lock(&ctx->fd_wqh.lock);__add_wait_queue(&ctx->fd_wqh,&wait);for(;;){set_current_state(TASK_INTERRUPTIBLE);-spin_lock(&ctx->fault_wqh.lock);-if(find_userfault(ctx,&uwq)){+spin_lock(&ctx->fault_pending_wqh.lock);+uwq=find_userfault(ctx);+if(uwq){/*-*Thefault_wqh.lockpreventstheuwqto-*disappearfromunderus.+*Thefault_pending_wqh.lockpreventstheuwq+*todisappearfromunderus.+*+*Refilethisuserfaultfrom+*fault_pending_wqhtofault_wqh,it'snot+*pendinganymoreafterwereadit.*/-uwq->pending=false;+list_del(&uwq->wq.task_list);+__add_wait_queue(&ctx->fault_wqh,&uwq->wq);+/* careful to always initialize msg if ret == 0 */*msg=uwq->msg;-spin_unlock(&ctx->fault_wqh.lock);+spin_unlock(&ctx->fault_pending_wqh.lock);ret=0;break;}-spin_unlock(&ctx->fault_wqh.lock);+spin_unlock(&ctx->fault_pending_wqh.lock);if(signal_pending(current)){ret=-ERESTARTSYS;break;
@@ -497,16 +512,21 @@ static void __wake_userfault(struct userfaultfd_ctx *ctx,start=range->start;end=range->start+range->len;-spin_lock(&ctx->fault_wqh.lock);+spin_lock(&ctx->fault_pending_wqh.lock);/* wake all in the range and autoremove */-__wake_up_locked_key(&ctx->fault_wqh,TASK_NORMAL,0,range);-spin_unlock(&ctx->fault_wqh.lock);+if(waitqueue_active(&ctx->fault_pending_wqh))+__wake_up_locked_key(&ctx->fault_pending_wqh,TASK_NORMAL,0,+range);+if(waitqueue_active(&ctx->fault_wqh))+__wake_up_locked_key(&ctx->fault_wqh,TASK_NORMAL,0,range);+spin_unlock(&ctx->fault_pending_wqh.lock);}static__always_inlinevoidwake_userfault(structuserfaultfd_ctx*ctx,structuserfaultfd_wake_range*range){-if(waitqueue_active(&ctx->fault_wqh))+if(waitqueue_active(&ctx->fault_pending_wqh)||+waitqueue_active(&ctx->fault_wqh))__wake_userfault(ctx,range);}
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-14 17:41:21
This allows to select the userfaultfd during configuration to build it.
Signed-off-by: Andrea Arcangeli <redacted>
---
fs/Makefile | 1 +
init/Kconfig | 11 +++++++++++
2 files changed, 12 insertions(+)
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Pretty much every single time people use this "if
(waitqueue_active())" model, it tends to be a bug, because it means
that there is zero serialization with people who are just about to go
to sleep. It's fundamentally racy against all the "wait_event()" loops
that carefully do memory barriers between testing conditions and going
to sleep, because the memory barriers now don't exist on the waking
side.
So I'm making a new rule: if you use waitqueue_active(), I want an
explanation for why it's not racy with the waiter. A big comment about
the memory ordering, or about higher-level locks that are held by the
caller, or something.
Linus
Pretty much every single time people use this "if
(waitqueue_active())" model, it tends to be a bug, because it means
that there is zero serialization with people who are just about to go
to sleep. It's fundamentally racy against all the "wait_event()" loops
that carefully do memory barriers between testing conditions and going
to sleep, because the memory barriers now don't exist on the waking
side.
As far as 10/23 is concerned, the __wake_userfault taking the locks
would also ignore any "incoming" not yet "read(2)" userfault. "read(2)"
as in syscall read. So there was no race to worry about as "incoming"
userfaults would be ignored anyway by the wake.
The only case that had to be reliable in not missing wakeup events was
the "release" file operation. But that doesn't use waitqueue_active
and it relies on ctx->released combined with mmap_sem taken for writing.
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/tree/fs/userfaultfd.c?h=userfault&id=2f73ffa8267e41f04fe6b0d93d23feed45c0980a#n267
However later (after I started documenting what userland should do) I
started to question this old model of leaving the race handling to
userland. It's way too complex to leave the race handling to
userland. Furthermore it's inefficient because there would be lots of
spurious userfaults that we could have been waken up within the kernel
before userland could have a chance to read them.
So then I handled the race in the kernel in patch 17/23. That allowed
to drop hundres of line of locking code from qemu (two bits per page
and a mutex and lots of complexity) and then I didn't need to document
the complex rules described in this commit header:
http://git.kernel.org/cgit/linux/kernel/git/andrea/aa.git/commit/?h=userfault&id=5a2b3614e107482da9a1fcfcd120d30eb62d45dc
You can see from the patch it complicated the kernel by adding a
pagetable walk, but it's worth it because it's simpler than solving it
in userland, plus its solved at once for all users.
The only cons is that the old model would allow userland to be way
more consistent in enforcing asserts as it had the control.
With the current model POLLIN may be returned from poll() despite the
later read returns -EAGAIN, so it basically requires a non blocking
open if people uses poll(). (blocking reads without poll are still
fine)
Now back to your question: the waitqueue_active is still there in the
current model as well (since 17/23).
Since 17/23 losing a wakeup (as far as qemu is concerned) would mean
that qemu would read the address of the fault that didn't get waken up
because of the race, then it would send a page request to the source
node (it had no state in the destination node where the userfault runs
to know if it was a "dup"), which would discard the request (not
sending the page twice) noticing in its simple per-page bitmap that it
was already sent. So with the new model after 17/23 qemu, losing a
wakeup is a bug.
The wait_event is like this:
handle_userfault (wait_event)
-------
spin_lock(&ctx->fault_pending_wqh.lock);
/*
* After the __add_wait_queue the uwq is visible to userland
* through poll/read().
*/
__add_wait_queue(&ctx->fault_pending_wqh, &uwq.wq);
/*
* The smp_mb() after __set_current_state prevents the reads
* following the spin_unlock to happen before the list_add in
* __add_wait_queue.
*/
set_current_state(TASK_KILLABLE);
spin_unlock(&ctx->fault_pending_wqh.lock);
must_wait = userfaultfd_must_wait(ctx, address, flags, reason);
up_read(&mm->mmap_sem);
if (likely(must_wait && !ACCESS_ONCE(ctx->released) &&
!fatal_signal_pending(current))) {
wake_up_poll(&ctx->fd_wqh, POLLIN);
schedule();
The wakeup side is:
userfaultfd_copy
-----------
ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src,
uffdio_copy.len);
if (unlikely(put_user(ret, &user_uffdio_copy->copy)))
return -EFAULT;
if (ret < 0)
goto out;
BUG_ON(!ret);
/* len == 0 would wake all */
range.len = ret;
if (!(uffdio_copy.mode & UFFDIO_COPY_MODE_DONTWAKE)) {
range.start = uffdio_copy.dst;
wake_userfault(ctx, &range);
wake_userfault is the function that is using waitqueue_active so the
problem materializes if waitqueue_active moves before
mcopy_atomic. Precisely it should move before the set_pte_at below:
mcopy_atomic_pte
------
set_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte);
/* No need to invalidate - it was non-present before */
update_mmu_cache(dst_vma, dst_addr, dst_pte);
pte_unmap_unlock(dst_pte, ptl);
unlock would allow the read to be reordered before it even on
x86. There's an up_read as well in between but it has the same
problem. So you're right that theoretically we can miss a wakeup.
Practically it sounds unlikely because of the sheer size of
mcopy_atomic and we never experienced it but it's still a bug.
So I'm making a new rule: if you use waitqueue_active(), I want an
explanation for why it's not racy with the waiter. A big comment
about the memory ordering, or about higher-level locks that are held
by the caller, or something. Linus
The wait_event/handle_userfault side already has a smp_mb() to prevent
the lockless pagetable walk to be reordered before the list_add in
__add_wait_queue (needed as well precisely because of the
waitqueue_active optimization that I'd like to keep).
Thanks,
Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Fri, May 15, 2015 at 9:04 AM, Andrea Arcangeli [off-list ref] wrote:
To fix it I added this along a comment:
Ok, this looks good as a explanation/fix for the races (and also as an
example of my worry about waitqueue_active() use in general).
However, it now makes me suspect that the optimistic "let's check if
they are even active" may not be worth it any more. You're adding a
"smp_mb()" in order to avoid taking the real lock. Although I guess
there are two locks there (one for each wait-queue) so maybe it's
worth it.
Linus
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Pavel Emelyanov <hidden> Date: 2015-05-18 14:25:08
On 05/14/2015 08:30 PM, Andrea Arcangeli wrote:
Hello everyone,
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
The postcopy live migration feature on the qemu side is mostly ready
to be merged and it entirely depends on the userfaultfd syscall to be
merged as well. So it'd be great if this patchset could be reviewed
for merging in -mm.
Userfaults allow to implement on demand paging from userland and more
generally they allow userland to more efficiently take control of the
behavior of page faults than what was available before
(PROT_NONE + SIGSEGV trap).
Not to spam with 23 e-mails, all patches are
Acked-by: Pavel Emelyanov <redacted>
Thanks!
-- Pavel
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrew Morton <akpm@linux-foundation.org> Date: 2015-05-19 21:38:10
On Thu, 14 May 2015 19:30:57 +0200 Andrea Arcangeli [off-list ref] wrote:
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
It would be useful to have some userfaultfd testcases in
tools/testing/selftests/. Partly as an aid to arch maintainers when
enabling this. And also as a standalone thing to give people a
practical way of exercising this interface.
What are your thoughts on enabling userfaultfd for other architectures,
btw? Are there good use cases, are people working on it, etc?
Also, I assume a manpage is in the works? Sooner rather than later
would be good - Michael's review of proposed kernel interfaces has
often been valuable.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Richard Weinberger <hidden> Date: 2015-05-19 21:59:47
On Tue, May 19, 2015 at 11:38 PM, Andrew Morton
[off-list ref] wrote:
On Thu, 14 May 2015 19:30:57 +0200 Andrea Arcangeli [off-list ref] wrote:
quoted
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
It would be useful to have some userfaultfd testcases in
tools/testing/selftests/. Partly as an aid to arch maintainers when
enabling this. And also as a standalone thing to give people a
practical way of exercising this interface.
What are your thoughts on enabling userfaultfd for other architectures,
btw? Are there good use cases, are people working on it, etc?
UML is using SIGSEGV for page faults.
i.e. the UML processes receives a SIGSEGV, learns the faulting address
from the mcontext
and resolves the fault by installing a new mapping.
If userfaultfd is faster that the SIGSEGV notification it could speed
up UML a bit.
For UML I'm only interested in the notification, not the resolving
part. The "missing"
data is present, only a new mapping is needed. No copy of data.
Andrea, what do you think?
--
Thanks,
//richard
From: Andrea Arcangeli <hidden> Date: 2015-05-20 13:24:56
Hi Andrew,
On Tue, May 19, 2015 at 02:38:01PM -0700, Andrew Morton wrote:
On Thu, 14 May 2015 19:30:57 +0200 Andrea Arcangeli [off-list ref] wrote:
quoted
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
It would be useful to have some userfaultfd testcases in
tools/testing/selftests/. Partly as an aid to arch maintainers when
enabling this. And also as a standalone thing to give people a
practical way of exercising this interface.
Agreed.
I was also thinking about writing a trinity module for it, I wrote it
for an older version but it was much easier to do that back then
before we had ioctls, now it's more tricky because the ioctls requires
the fd open first etc... it's not enough to just call a syscall with a
flood of supervised-random params anymore.
What are your thoughts on enabling userfaultfd for other architectures,
btw? Are there good use cases, are people working on it, etc?
powerpc should be enabled and functional already. There's not much
arch dependent code in it, so in theory if the postcopy live migration
patchset is applied to qemu, it should work on powerpc out of the
box. Nobody tested it yet but I don't expect trouble on the kernel side.
Adding support for all other archs is just a few liner patch that
defines the syscall number. I didn't do that out of tree because every
time a new syscall materialized I would get more rejects during
rebase.
Also, I assume a manpage is in the works? Sooner rather than later
would be good - Michael's review of proposed kernel interfaces has
often been valuable.
Yes, the manpage was certainly planned. It would require updates as we
keep adding features (like the wrprotect tracking, the non-cooperative
usage, and extending the availability of the ioctls to tmpfs). We can
definitely write a manpage with the current features.
Ok, so I'll continue working on the testcase and on the manpage.
Thanks!!
Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-20 14:17:37
Hello Richard,
On Tue, May 19, 2015 at 11:59:42PM +0200, Richard Weinberger wrote:
On Tue, May 19, 2015 at 11:38 PM, Andrew Morton
[off-list ref] wrote:
quoted
On Thu, 14 May 2015 19:30:57 +0200 Andrea Arcangeli [off-list ref] wrote:
quoted
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
It would be useful to have some userfaultfd testcases in
tools/testing/selftests/. Partly as an aid to arch maintainers when
enabling this. And also as a standalone thing to give people a
practical way of exercising this interface.
What are your thoughts on enabling userfaultfd for other architectures,
btw? Are there good use cases, are people working on it, etc?
UML is using SIGSEGV for page faults.
i.e. the UML processes receives a SIGSEGV, learns the faulting address
from the mcontext
and resolves the fault by installing a new mapping.
If userfaultfd is faster that the SIGSEGV notification it could speed
up UML a bit.
For UML I'm only interested in the notification, not the resolving
part. The "missing"
data is present, only a new mapping is needed. No copy of data.
Andrea, what do you think?
I think you need some kind of UFFDIO_MPROTECT ioctl that is the same
ioctl wrprotect tracking also needs. At the moment we focused the
future plans mostly on wrprotection tracking but it could be extended
to protnone tracking, either with the same feature flag as
wrprotection (with a generic UFFDIO_MPROTECT) or with two separate
feature flags and two separate ioctl.
Your pages are not missing, like in the postcopy live snapshotting
case the pages are not missing. The userfaultfd memory protection
ioctl will not modify the VMA, but it'll just selectively mark
pte/trans_huge_pmd wrprotected/protnone in order to get the faults. In
the case of postcopy live snapshotting a single ioctl call will mark
the entire guest address space readonly.
For live snapshotting the fault resolution is a no brainer: when you
get the fault the page is still readable and it just needs to be
copied off by the live snapshotting thread to a different location,
and then the UFFDIO_MPROTECT will be called again to make the page
writable and wake the blocked fault.
For the protnone, you need to modify the page before waking the
blocked userfault, you can't just remove the protnone or other threads
could modify it (if there are other threads). You'd need a further
ioctl to copy the page off to a different place by using its kernel
address (the userland address is not mapped) and copy it back to
overwrite the original page.
Alternatively once we extend the handle_userfault to tmpfs you could
map the page in two virtual mappings and track the faults in one
mapping (where the tracked app runs) and read/write the page contents
in the other mapping that isn't tracked by the userfault.
These are the first thoughts that comes to mind without knowing
exactly what you need to do after you get the fault address, and
without knowing exactly why you need to mark the region PROT_NONE.
There will be some complications in adding the wrprotection/protnone
feature: if faults could already happen when the wrprotect/protnone is
armed, the handle_userfault() could be invoked in a retry-fault, that
is not ok without allowing the userfault to return VM_FAULT_RETRY even
during a refault (i.e. FAULT_FLAG_TRIED set but FAULT_FLAG_ALLOW_RETRY
not set). The invariants of vma->vm_page_prot and pte/trans_huge_pmd
permissions must also not break anywhere. These are the two main
reasons why these features that requires to flip protection bits are
left implemented later and made visible later with uffdio_api.feature
flags and/or through uffdio_register.ioctl during UFFDIO_REGISTER.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Hello up there,
On Thu, May 14, 2015 at 07:30:57PM +0200, Andrea Arcangeli wrote:
Hello everyone,
This is the latest userfaultfd patchset against mm-v4.1-rc3
2015-05-14-10:04.
The postcopy live migration feature on the qemu side is mostly ready
to be merged and it entirely depends on the userfaultfd syscall to be
merged as well. So it'd be great if this patchset could be reviewed
for merging in -mm.
Userfaults allow to implement on demand paging from userland and more
generally they allow userland to more efficiently take control of the
behavior of page faults than what was available before
(PROT_NONE + SIGSEGV trap).
The use cases are:
[...]
Even though there wasn't a real use case requesting it yet, it also
allows to implement distributed shared memory in a way that readonly
shared mappings can exist simultaneously in different hosts and they
can be become exclusive at the first wrprotect fault.
Sorry for maybe speaking up too late, but here is additional real
potential use-case which in my view is overlapping with the above:
Recently we needed to implement persistency for NumPy arrays - that is
to track made changes to array memory and transactionally either abandon
the changes on transaction abort, or store them back to storage on
transaction commit.
Since arrays can be large, it would be slow and thus not practical to
have original data copy and compare memory to original to find what
array parts have been changed.
So I've implemented a scheme where array data is initially PROT_READ
protected, then we catch SIGSEGV, if it is write and area belongs to array
data - we mark that page as PROT_WRITE and continue. On commit time we
know which parts were modified.
Also, since arrays could be large - bigger than RAM, and only sparse
parts of it could be needed to get needed information, for reading it
also makes sense to lazily load data in SIGSEGV handler with initial
PROT_NONE protection.
This is very similar to how memory mapped files work, but adds
transactionality which, as far as I know, is not provided by any
currently in-kernel filesystem on Linux.
The system is done as files, and arrays are then build on top of
this-way memory-mapped files. So from now on we can forget about NumPy
arrays and only talk about files, their mapping, lazy loading and
transactionally storing in-memory changes back to file storage.
To get this working, a custom user-space virtual memory manager is
unrolled, which manages RAM memory "pages", file mappings into virtual
address-space, tracks pages protection and does SIGSEGV handling
appropriately.
The gist of virtual memory-manager is this:
https://lab.nexedi.cn/kirr/wendelin.core/blob/master/include/wendelin/bigfile/virtmem.hhttps://lab.nexedi.cn/kirr/wendelin.core/blob/master/bigfile/virtmem.c (vma_on_pagefault)
For operations it currently needs
- establishing virtual memory areas and connecting to tracking it
- changing pages protection
PROT_NONE or absent - initially
PROT_NONE -> PROT_READ - after read
PROT_READ -> PROT_READWRITE - after write
PROT_READWRITE -> PROT_READ - after commit
PROT_READWRITE -> PROT_NONE or absent (again) - after abort
PROT_READ -> PROT_NONE or absent (again) - on reclaim
- working with aliasable memory (thus taken from tmpfs)
there could be two overlapping-in-file mapping for file (array)
requested at different time, and changes from one mapping should
propagate to another one -> for common parts only 1 page should
be memory-mapped into 2 places in address-space.
so what is currently lacking on userfaultfd side is:
- ability to remove / make PROT_NONE already mapped pages
(UFFDIO_REMAP was recently dropped)
- ability to arbitrarily change pages protection (e.g. RW -> R)
- inject aliasable memory from tmpfs (or better hugetlbfs) and into
several places (UFFDIO_REMAP + some mapping copy semantic).
The code is ugly because it is only a prototype. You can clone/read it
all from here:
https://lab.nexedi.cn/kirr/wendelin.core
Virtual memory-manager even has tests, and from them it could be seen
how the system is supposed to work (after each access - what pages and
where are mapped and how):
https://lab.nexedi.cn/kirr/wendelin.core/blob/master/bigfile/tests/test_virtmem.c
The performance currently is not great, partly because of page clearing
when getting ram from tmpfs, and partly because of mprotect/SIGSEGV/vmas
overhead and other dumb things on my side.
I still wanted to show the case, as userfaultd here has potential to
remove overhead related to kernel.
Thanks beforehand for feedback,
Kirill
P.S. some context
http://www.wendelin.io/NXD-Wendelin.Core.Non.Secret/asEntireHTML
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-21 15:53:45
Hi Kirill,
On Thu, May 21, 2015 at 04:11:11PM +0300, Kirill Smelkov wrote:
Sorry for maybe speaking up too late, but here is additional real
Not too late, in fact I don't think there's any change required for
this at this stage, but it'd be great if you could help me to review.
Since arrays can be large, it would be slow and thus not practical to
[..]
So I've implemented a scheme where array data is initially PROT_READ
protected, then we catch SIGSEGV, if it is write and area belongs to array
In the case of postcopy live migration (for qemu and/or containers) and
postcopy live snapshotting, splitting the vmas is not an option
because we may run out of them.
If your PROT_READ areas are limited perhaps this isn't an issue but
with hundreds GB guests (currently plenty in production) that needs to
live migrate fully reliably and fast, the vmas could exceed the limit
if we were to use mprotect. If your arrays are very large and the
PROT_READ aren't limited, using userfaultfd this isn't only an
optimization for you too, it's actually a must to avoid a potential
-ENOMEM.
Also, since arrays could be large - bigger than RAM, and only sparse
parts of it could be needed to get needed information, for reading it
also makes sense to lazily load data in SIGSEGV handler with initial
PROT_NONE protection.
Similarly I heard somebody wrote a fastresume to load the suspended
(on disk) guest ram using userfaultfd. That is a slightly less
fundamental case than postcopy because you could do it also with
MAP_SHARED, but it's still interesting in allowing to compress or
decompress the suspended ram on the fly with lz4 for example,
something MAP_PRIVATE/MAP_SHARED wouldn't do (plus there's the
additional benefit of not having an orphaned inode left open even if
the file is deleted, that prevents to unmount the filesystem for the
whole lifetime of the guest).
This is very similar to how memory mapped files work, but adds
transactionality which, as far as I know, is not provided by any
currently in-kernel filesystem on Linux.
I'll check it more in detail ASAP, thanks for the pointers!
For operations it currently needs
- establishing virtual memory areas and connecting to tracking it
That's the UFFDIO_REGISTER/UNREGISTER.
- changing pages protection
PROT_NONE or absent - initially
absent is what works with -mm already. The lazy loading already works.
PROT_NONE -> PROT_READ - after read
Current UFFDIO_COPY will map it using vma->vm_page_prot.
We'll need a new flag for UFFDIO_COPY to map it readonly. This is
already contemplated:
/*
* There will be a wrprotection flag later that allows to map
* pages wrprotected on the fly. And such a flag will be
* available if the wrprotection ioctl are implemented for the
* range according to the uffdio_register.ioctls.
*/
#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0)
__u64 mode;
If the memory protection framework exists (either through the
uffdio_register.ioctl out value, or through uffdio_api.features
out-only value) you can pass a new flag (MODE_WP) above to transition
from "absent" to "PROT_READ".
PROT_READ -> PROT_READWRITE - after write
This will need to add UFFDIO_MPROTECT.
PROT_READWRITE -> PROT_READ - after commit
UFFDIO_MPROTECT again (but harder if going from rw to ro, because of a
slight mess to solve with regard to FAULT_FLAG_TRIED, in case you want
to run this UFFDIO_MPROTECT without stopping the threads that are
accessing the memory concurrently).
And this should only work if the uffdio_register.mode had MODE_WP set,
so we don't run into the races created by COWs (gup vs fork race).
PROT_READWRITE -> PROT_NONE or absent (again) - after abort
UFFDIO_MPROTECT again, but you won't be able to read the page contents
inside the memory manager thread (the one working with
userfaultfd).
The manager at all times if forbidden to touch the memory it is
tracking with userfaultfd (if it does it'll deadlock, but kill -9 will
get rid of it). gdb ironically because it is using an underoptimized
access_process_vm wouldn't hang, because FAULT_FLAG_RETRY won't be set
in handle_userfault in the gdb context, and it'll just receive a
sigbus if by mistake the user tries to touch the memory. Even if it
will hung later as get_user_pages_locked|unlocked gets used there too,
kill -9 would solve gdb too.
Back to the problem of accessing the UFFDIO_MPROTECT(PROT_NONE)
memory: to do that a new ioctl should be required. I'd rather not go
back to the route of UFFDIO_REMAP, but it could copy the data using
the kernel address.
It could be simply a reverse UFFDIO_COPY. We could add a
UFFDIO_COPY_MODE_REVERSE flag to the "mode" of UFFDIO_COPY to mean
"read source from kernel address and write destination in user
address". By default it reads the source from user address and write
the destination in kernel address (to be atomic).
If you want to put data back before lifting the PROT_NONE, UFFDIO_COPY
could be used in the standard way but with a
UFFDIO_COPY_MODE_OVERWRITE flag that just overwrites the contents of
the old page if it's not mapped (protnone), or just get rid of the old
page (currently it'd return -EEXIST if the pte is not none).
So the process would be:
UFFDIO_COPY(dst_tmpaddr, src_addr, mode=REVERSE)
UFFDIO_COPY(src_addr, dst_tmpaddr, mode=OVERWRITE)
Then if you also set mode=READONLY in the last UFFDIO_COPY, it'll
create a wrprotected mapping atomically before giving visibility to
the new page contents:
UFFDIO_COPY(src_addr, dst_tmpaddr, mode=OVERWRITE|WP)
PROT_READ -> PROT_NONE or absent (again) - on reclaim
Same as above.
- working with aliasable memory (thus taken from tmpfs)
there could be two overlapping-in-file mapping for file (array)
requested at different time, and changes from one mapping should
propagate to another one -> for common parts only 1 page should
be memory-mapped into 2 places in address-space.
Why isn't the manager thread taking care of calling UFFDIO_MPROTECT in
two places?
And UFFDIO_COPY would fill the page and replace the old page and the
effect would be visible as far as the "data" is concerned, but the
protection bits would be more naturally different for each
mapping, like a double mmap call is also required to map such an area
in two places.
You could have a MAP_PRIVATE vma with PROT_READ, you can't create a
writable pte into it, just because you called
UFFDIO_MPROTECT(PROT_READ|PROT_WRITE) in a different mapping of the
same tmpfs page.
NOTE: the availability of the UFFDIO_MPROTECT|COPY on tmpfs ares would
still depend on UFFDIO_REGISTER to return the respecteve ioctl id in
the uffdio_register.ioctl (out value of the register ioctl).
so what is currently lacking on userfaultfd side is:
- ability to remove / make PROT_NONE already mapped pages
(UFFDIO_REMAP was recently dropped)
- ability to arbitrarily change pages protection (e.g. RW -> R)
- inject aliasable memory from tmpfs (or better hugetlbfs) and into
several places (UFFDIO_REMAP + some mapping copy semantic).
I think UFFDIO_COPY if added with OVERWRITE|REVERSE|WP flags is an ok
substitute for UFFDIO_REMAP.
If UFFDIO_COPY sees the page is protnone during the REVERSE copy (to
extract the memory atomically), it can also skip the tlb flush (and
obviously there's no tlb flush in the reverse direction). If the page
was not protnone, it can turn it in protnone, do a tlb flush, and then
copy it to the destination address using the userland mapping.
UFFDIO_MPROTECT is definitely necessary for postcopy live snapshotting
too (the reverse UFFDIO_COPY is not, it never deals with
PROT_NONE and it never cares about missing faults).
MPROTECT(PROT_NONE) so far seems needed only by this and perhaps UML
(and perhaps qemu linux-user).
I posted in another email why these features aren't implemented yet
==
There will be some complications in adding the wrprotection/protnone
feature: if faults could already happen when the wrprotect/protnone is
armed, the handle_userfault() could be invoked in a retry-fault, that
is not ok without allowing the userfault to return VM_FAULT_RETRY even
during a refault (i.e. FAULT_FLAG_TRIED set but FAULT_FLAG_ALLOW_RETRY
not set). The invariants of vma->vm_page_prot and pte/trans_huge_pmd
permissions must also not break anywhere. These are the two main
reasons why these features that requires to flip protection bits are
left implemented later and made visible later with uffdio_api.feature
flags and/or through uffdio_register.ioctl during UFFDIO_REGISTER.
==
The performance currently is not great, partly because of page clearing
when getting ram from tmpfs, and partly because of mprotect/SIGSEGV/vmas
overhead and other dumb things on my side.
Also the page faults get slowed down when the rbtree grows a lot,
userfaultfd won't let the rbtree grow.
I still wanted to show the case, as userfaultd here has potential to
remove overhead related to kernel.
That's very useful and interesting feedback!
Could you review the API to be sure we don't have to modify it when we
extend it like described above?
1) tmpfs returning uffdio_register.ioctl |=
UFFDIO_MPROTECT|UFFDIO_COPY when enabled
2) UFFDIO_MPROTECT(PROT_NONE|READ|WRITE|EXEC) and in turn
UFFDIO_COPY_MODE_REVERSE|UFFDIO_COPY_MODE_OVERWRITE|UFFDIO_COPY_MODE_WP
being available if uffdio_register.ioctl includes UFFDIO_MPROTECT
(and uffdio_api.features will then include
UFFD_FEATURE_PAGEFAULT_WP to signal the uffd_msg.pagefault.flag WP
is available [bit 1], and UFFDIO_REGISTER_MODE_WP can be used in
uffdio_register.mode)
All of it could just check for uffdio_api.features &
UFFD_FEATURE_PAGEFAULT_WP being set, but you'd still have to check for
UFFDIO_MPROTECT being set in uffdio_register.ioctl for tmpfs areas (or
to know it's not available yet on hugetlbfs), so I think it's more
robust to check UFFDIO_MPROTECT ioctl being set in
uffdio_register.ioctl to assume all mprotection and writeprotect
tracking features are available for that specific range. The feature
flag will just tell that UFFDIO_REGISTER_MODE_WP can be used in the
register ioctl, that is something you need to know before in order to
"arm" the VMA for wrprotect faults.
For your usage I think you probably want to set
UFFDIO_REGISTER_MODE_WP|UFFDIO_REGISTER_MODE_MISSING and you'll be
told through uffdio_msg.flags if it's a WP or MISSING fault. You won't
be told if it's missing because of PROT_NONE or absent.
On a side note: all of the above is completely orthognal from the
non-cooperative usage: as far as memory protection features it doesn't
need any, it just needs to track more events like fork/mremap to
adjust its offsets as the memory manager is not part of the app and it
has no way to orchestrate by other means.
Doing it all at once (non-cooperative + full memory protection) looked
too much. We should just try to get the API right in a way that won't
require an UFFD_API bump passed to uffdio_api.api. Even then, if an
api bump is required, that's not a big deal, until recently the
non-cooperative usage already did the API bumb but we accomodated the
read(2) API to avoid it.
Thinking at the worst case scenario, if the API gets bumped the only
thing that has to remain fixed is the ioctl number of the UFFDIO_API
and the uffdio_api structure. Everything else can be upgraded without
risk of ABI breakage, even the ioctl numbers can be reused (except the
very UFFDIO_API). When the non-cooperative usage bumped the API it
actually kept all ioctl the same except the read(2) format.
Comments welcome, thanks!
Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Hi Andrea,
On Thu, May 21, 2015 at 05:52:51PM +0200, Andrea Arcangeli wrote:
Hi Kirill,
On Thu, May 21, 2015 at 04:11:11PM +0300, Kirill Smelkov wrote:
quoted
Sorry for maybe speaking up too late, but here is additional real
Not too late, in fact I don't think there's any change required for
this at this stage, but it'd be great if you could help me to review.
Thanks
quoted
Since arrays can be large, it would be slow and thus not practical to
[..]
quoted
So I've implemented a scheme where array data is initially PROT_READ
protected, then we catch SIGSEGV, if it is write and area belongs to array
In the case of postcopy live migration (for qemu and/or containers) and
postcopy live snapshotting, splitting the vmas is not an option
because we may run out of them.
If your PROT_READ areas are limited perhaps this isn't an issue but
with hundreds GB guests (currently plenty in production) that needs to
live migrate fully reliably and fast, the vmas could exceed the limit
if we were to use mprotect. If your arrays are very large and the
PROT_READ aren't limited, using userfaultfd this isn't only an
optimization for you too, it's actually a must to avoid a potential
-ENOMEM.
Also, since arrays could be large - bigger than RAM, and only sparse
parts of it could be needed to get needed information, for reading it
also makes sense to lazily load data in SIGSEGV handler with initial
PROT_NONE protection.
Similarly I heard somebody wrote a fastresume to load the suspended
(on disk) guest ram using userfaultfd. That is a slightly less
fundamental case than postcopy because you could do it also with
MAP_SHARED, but it's still interesting in allowing to compress or
decompress the suspended ram on the fly with lz4 for example,
something MAP_PRIVATE/MAP_SHARED wouldn't do (plus there's the
additional benefit of not having an orphaned inode left open even if
the file is deleted, that prevents to unmount the filesystem for the
whole lifetime of the guest).
I see. Just a note - transparent compression/decompression could be
achieved with MAP_SHARED if the compression is being performed by
underlying filesystem - e.g. implemented with FUSE.
( I have not measured performance though )
quoted
This is very similar to how memory mapped files work, but adds
transactionality which, as far as I know, is not provided by any
currently in-kernel filesystem on Linux.
I'll check it more in detail ASAP, thanks for the pointers!
quoted
For operations it currently needs
- establishing virtual memory areas and connecting to tracking it
That's the UFFDIO_REGISTER/UNREGISTER.
Yes
quoted
- changing pages protection
PROT_NONE or absent - initially
absent is what works with -mm already. The lazy loading already works.
Yes
quoted
PROT_NONE -> PROT_READ - after read
Current UFFDIO_COPY will map it using vma->vm_page_prot.
We'll need a new flag for UFFDIO_COPY to map it readonly. This is
already contemplated:
/*
* There will be a wrprotection flag later that allows to map
* pages wrprotected on the fly. And such a flag will be
* available if the wrprotection ioctl are implemented for the
* range according to the uffdio_register.ioctls.
*/
#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0)
__u64 mode;
If the memory protection framework exists (either through the
uffdio_register.ioctl out value, or through uffdio_api.features
out-only value) you can pass a new flag (MODE_WP) above to transition
from "absent" to "PROT_READ".
Yes. The same probably applies to UFFDIO_ZEROPAGE (to mmap-in zeropage
as RO on read, if that part of file is currently hole)
So we settle on adding
UFFDIO_COPY_MODE_WP and
UFFDIO_ZEROPAGE_MODE_WP
?
Or maybe why we have both COPY_DONTWAKE and ZEROPAGE_DONTWAKE (and
previously REMAP_MODE_DONTWAKE) and now duplicate _MODE_WP to all them?
Maybe it makes sense to move those common flags related to waking up or
not, mmaping in as R or RW (and maybe other in the future) to common
place.
quoted
PROT_READ -> PROT_READWRITE - after write
This will need to add UFFDIO_MPROTECT.
Yes
quoted
PROT_READWRITE -> PROT_READ - after commit
UFFDIO_MPROTECT again (but harder if going from rw to ro, because of a
slight mess to solve with regard to FAULT_FLAG_TRIED, in case you want
to run this UFFDIO_MPROTECT without stopping the threads that are
accessing the memory concurrently).
Yes. I understand the race of a manager making pages RW -> R, and an
other thread in user process simultaneously making a write to the same
page.
On user-level this race can be solved this way: before commit, changed
pages are first marked as R and only then written to storage.
- If a write is racing with RW->R protection being made - it's a client
problem (of having one thread still modifying data, and other thread
triggering commit) - we are ok with committing what has already been
written at the moment RW->R has happened.
- If a write happened after RW->R protection has been made (even if it
is racy), we are ok if we get a proper notification to userfaultfd
handler of write to WP area.
So if on kernel side the "slight mess" can be solved, userspace is ok to
live with the potential race and solve it itself.
And this should only work if the uffdio_register.mode had MODE_WP set,
so we don't run into the races created by COWs (gup vs fork race).
It is ok to start with registering with
(UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP)
for whole area at the beginning. In other words we are asking
userfaultfd "we want to handle all kind of faults - both reads and writes"
quoted
PROT_READWRITE -> PROT_NONE or absent (again) - after abort
UFFDIO_MPROTECT again,
My idea here is that on transaction abort, we just don't need that
changed memory - we can both forget the changes and free the appropriate
pages - if in next transaction someone will need the file data of that
same part again, it just reloads the usual way from file.
So it is maybe
UFFDIO_MFREE ( make pte absent, and free(*) referenced page
(*) free maybe = decrement its refcount )
what is needed here.
But for generality, I agree it make sense to have a way to just MPROTECT
with PROT_NONE without freeing the page.
but you won't be able to read the page contents
inside the memory manager thread (the one working with
userfaultfd).
With PROT_NONE I see.
The manager at all times if forbidden to touch the memory it is
tracking with userfaultfd (if it does it'll deadlock, but kill -9 will
get rid of it). gdb ironically because it is using an underoptimized
access_process_vm wouldn't hang, because FAULT_FLAG_RETRY won't be set
in handle_userfault in the gdb context, and it'll just receive a
sigbus if by mistake the user tries to touch the memory. Even if it
will hung later as get_user_pages_locked|unlocked gets used there too,
kill -9 would solve gdb too.
Wait. I partly understand, because I have no much experience in mm. But
are you saying the manager cannot access the memory it tracks, if pages
even have PROT_READ or PROT_READWRITE protection, and we know they were
already loaded, e.g. they are not missing?
If yes, then for sure, there need to be a way to get the data back from
memory to manager to implement storing changes back.
And maybe for some cases it would make sense to first set just
protection to PROT_NONE so manager know the client cannot mess with the
data while it accesses it.
Back to the problem of accessing the UFFDIO_MPROTECT(PROT_NONE)
memory: to do that a new ioctl should be required. I'd rather not go
back to the route of UFFDIO_REMAP, but it could copy the data using
the kernel address.
It could be simply a reverse UFFDIO_COPY. We could add a
UFFDIO_COPY_MODE_REVERSE flag to the "mode" of UFFDIO_COPY to mean
"read source from kernel address and write destination in user
address". By default it reads the source from user address and write
the destination in kernel address (to be atomic).
Yes, with small clarification that "write to kernel address" is write to
"kernel address associated to address in destination mm"
If you want to put data back before lifting the PROT_NONE, UFFDIO_COPY
could be used in the standard way but with a
UFFDIO_COPY_MODE_OVERWRITE flag that just overwrites the contents of
the old page if it's not mapped (protnone), or just get rid of the old
page (currently it'd return -EEXIST if the pte is not none).
So the process would be:
UFFDIO_COPY(dst_tmpaddr, src_addr, mode=REVERSE)
UFFDIO_COPY(src_addr, dst_tmpaddr, mode=OVERWRITE)
Then if you also set mode=READONLY in the last UFFDIO_COPY, it'll
create a wrprotected mapping atomically before giving visibility to
the new page contents:
UFFDIO_COPY(src_addr, dst_tmpaddr, mode=OVERWRITE|WP)
I agree in general.
The only thing which confuses me a bit is the REVERSE flag and dst/src
being skipped. I would rather keep the src / dst ordering and in mode
have COPY_TO and COPY_FROM or have both UFFDIO_COPY_TO and
UFFDIO_COPY_FROM to clarify API.
But this is only a style and does not change semantics.
But I wonder though again - is it maybe possible to get managed memory
content (with PROT_READ or PROT_READWRITE) without copying?
quoted
PROT_READ -> PROT_NONE or absent (again) - on reclaim
Same as above.
The same as above about abort applies to reclaim - we just need
to forget and free memory here - so UFFDIO_MFREE.
quoted
- working with aliasable memory (thus taken from tmpfs)
there could be two overlapping-in-file mapping for file (array)
requested at different time, and changes from one mapping should
propagate to another one -> for common parts only 1 page should
be memory-mapped into 2 places in address-space.
Why isn't the manager thread taking care of calling UFFDIO_MPROTECT in
two places?
And UFFDIO_COPY would fill the page and replace the old page and the
effect would be visible as far as the "data" is concerned, but the
protection bits would be more naturally different for each
mapping, like a double mmap call is also required to map such an area
in two places.
Because if we have a write on one place, the manager will
write-unprotect it, and would not get notified on further writes to the
same area.
And I need further writes to be visible in other mappings instantly.
Here is simplified example:
In [1]: from numpy import *
In [2]: A = zeros(10) # it simulates big array backed by manager
In [3]: a = A[0:5] # one part is mapped with start/stop = (0,5)
# something unrelated is done in between
In [4]: b = A[3:10] # another part is mapped with start/stop = (3,10)
# NOTE a and b overlaps in array/backing file.
# but since we already allocated address-space for a and did other
# things, address space beyond a could be already allocated, so we
# cannot just extend it and make b referencing addresses starting at
# a tail.
In [5]: a
Out[5]: array([ 0., 0., 0., 0., 0.])
In [6]: b
Out[6]: array([ 0., 0., 0., 0., 0., 0., 0.])
In [7]: a[4] = 1 # first change to a; the manager removes WP
In [8]: a
Out[8]: array([ 0., 0., 0., 0., 1.])
In [9]: b
Out[9]: array([ 0., 1., 0., 0., 0., 0., 0.]) # propagated to b
# write again - the manager does not get a WP fault, but the change have to
# propagate again
In [10]: a[4] = 2
In [11]: a
Out[11]: array([ 0., 0., 0., 0., 2.])
In [12]: b
Out[12]: array([ 0., 2., 0., 0., 0., 0., 0.])
so if we resolve fault for a[4], the exact page mapped in, should be
also eventually mapped into b[1].
You could have a MAP_PRIVATE vma with PROT_READ, you can't create a
writable pte into it, just because you called
UFFDIO_MPROTECT(PROT_READ|PROT_WRITE) in a different mapping of the
same tmpfs page.
NOTE: the availability of the UFFDIO_MPROTECT|COPY on tmpfs ares would
still depend on UFFDIO_REGISTER to return the respecteve ioctl id in
the uffdio_register.ioctl (out value of the register ioctl).
It is ok to check. But I'd like to note: here we mmap two overlapping
parts of a tmpfs file in two regions, and register both regions to
userfaultfd.
quoted
so what is currently lacking on userfaultfd side is:
- ability to remove / make PROT_NONE already mapped pages
(UFFDIO_REMAP was recently dropped)
- ability to arbitrarily change pages protection (e.g. RW -> R)
- inject aliasable memory from tmpfs (or better hugetlbfs) and into
several places (UFFDIO_REMAP + some mapping copy semantic).
I think UFFDIO_COPY if added with OVERWRITE|REVERSE|WP flags is an ok
substitute for UFFDIO_REMAP.
If UFFDIO_COPY sees the page is protnone during the REVERSE copy (to
extract the memory atomically), it can also skip the tlb flush (and
obviously there's no tlb flush in the reverse direction). If the page
was not protnone, it can turn it in protnone, do a tlb flush, and then
copy it to the destination address using the userland mapping.
We are also ok if the page is PROT_READ - in this case no need to do a
tlbflush - nothing can change to page while we are copying it - only we
have to care not to allow changing protection to PROT_READWRITE in the
process.
UFFDIO_MPROTECT is definitely necessary for postcopy live snapshotting
too (the reverse UFFDIO_COPY is not, it never deals with
PROT_NONE and it never cares about missing faults).
MPROTECT(PROT_NONE) so far seems needed only by this and perhaps UML
(and perhaps qemu linux-user).
I posted in another email why these features aren't implemented yet
==
There will be some complications in adding the wrprotection/protnone
feature: if faults could already happen when the wrprotect/protnone is
armed, the handle_userfault() could be invoked in a retry-fault, that
is not ok without allowing the userfault to return VM_FAULT_RETRY even
during a refault (i.e. FAULT_FLAG_TRIED set but FAULT_FLAG_ALLOW_RETRY
not set). The invariants of vma->vm_page_prot and pte/trans_huge_pmd
permissions must also not break anywhere. These are the two main
reasons why these features that requires to flip protection bits are
left implemented later and made visible later with uffdio_api.feature
flags and/or through uffdio_register.ioctl during UFFDIO_REGISTER.
==
I understand, maybe not in full details though. The changes would anyway
be needed to make userfaultfd capable of generic memory managing,
instead of populating it only in one way.
And as I noted above, besides UFFDIO_COPY(both direction, overwrite) and
UFFDIO_MPROTECT, UFFDIO_MFREE is also needed.
quoted
The performance currently is not great, partly because of page clearing
when getting ram from tmpfs, and partly because of mprotect/SIGSEGV/vmas
overhead and other dumb things on my side.
Also the page faults get slowed down when the rbtree grows a lot,
userfaultfd won't let the rbtree grow.
Yes. This is the same as splitting or not vmas.
quoted
I still wanted to show the case, as userfaultd here has potential to
remove overhead related to kernel.
That's very useful and interesting feedback!
Could you review the API to be sure we don't have to modify it when we
extend it like described above?
1) tmpfs returning uffdio_register.ioctl |=
UFFDIO_MPROTECT|UFFDIO_COPY when enabled
and UFFDIO_MFREE with maybe ability to automatically punch hole of freed
memory (we need to return the memory to the system on reclaim, and
preferable on abort).
2) UFFDIO_MPROTECT(PROT_NONE|READ|WRITE|EXEC) and in turn
UFFDIO_COPY_MODE_REVERSE|UFFDIO_COPY_MODE_OVERWRITE|UFFDIO_COPY_MODE_WP
being available if uffdio_register.ioctl includes UFFDIO_MPROTECT
UFFDIO_MPROTECT -> UFFDIO_MPROTECT(PROT_NONE|READ|WRITE|EXEC) looks ok.
but imho various copy modes could be allowed besides mprotect - e.g. for
a manager to get back managed memory. Thus, maybe
UFFDIO_COPY_MODE_REVERSE (or analogues) and UFFDIO_COPY_MODE_OVERWRITE
should be available for when just UFFDIO_COPY is set.
From userspace point of view, it would be simpler to operate when all
those operations are always allowed though.
(and uffdio_api.features will then include
UFFD_FEATURE_PAGEFAULT_WP to signal the uffd_msg.pagefault.flag WP
is available [bit 1], and UFFDIO_REGISTER_MODE_WP can be used in
uffdio_register.mode)
Yes, though it is not fully represents the capability - e.g.
MPROTECT(PROT_NONE) should work too, so it should be maybe
UFFD_FEATURE_PAGEFAULT_PROTECT.
Again, from clients point of view it would be simpler if those features
are in base - e.g. they are always available.
All of it could just check for uffdio_api.features &
UFFD_FEATURE_PAGEFAULT_WP being set, but you'd still have to check for
UFFDIO_MPROTECT being set in uffdio_register.ioctl for tmpfs areas (or
to know it's not available yet on hugetlbfs), so I think it's more
robust to check UFFDIO_MPROTECT ioctl being set in
uffdio_register.ioctl to assume all mprotection and writeprotect
tracking features are available for that specific range. The feature
flag will just tell that UFFDIO_REGISTER_MODE_WP can be used in the
register ioctl, that is something you need to know before in order to
"arm" the VMA for wrprotect faults.
ok
For your usage I think you probably want to set
UFFDIO_REGISTER_MODE_WP|UFFDIO_REGISTER_MODE_MISSING and you'll be
told through uffdio_msg.flags if it's a WP or MISSING fault.
ok
You won't be told if it's missing because of PROT_NONE or absent.
looks like not good - to know whether a page is mapped there already or
not. Is it possible to distinguish this cases too?
On a side note: all of the above is completely orthognal from the
non-cooperative usage: as far as memory protection features it doesn't
need any, it just needs to track more events like fork/mremap to
adjust its offsets as the memory manager is not part of the app and it
has no way to orchestrate by other means.
Doing it all at once (non-cooperative + full memory protection) looked
too much. We should just try to get the API right in a way that won't
require an UFFD_API bump passed to uffdio_api.api. Even then, if an
api bump is required, that's not a big deal, until recently the
non-cooperative usage already did the API bumb but we accomodated the
read(2) API to avoid it.
Thinking at the worst case scenario, if the API gets bumped the only
thing that has to remain fixed is the ioctl number of the UFFDIO_API
and the uffdio_api structure. Everything else can be upgraded without
risk of ABI breakage, even the ioctl numbers can be reused (except the
very UFFDIO_API). When the non-cooperative usage bumped the API it
actually kept all ioctl the same except the read(2) format.
I agree we can change API / ABI with versioning, but it is better to try
to get it right from the beginning and not fixup with several api
versions on top.
Thanks for your comments and feedback,
Kirill
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrew Morton <akpm@linux-foundation.org> Date: 2015-05-22 20:18:27
On Thu, 14 May 2015 19:31:19 +0200 Andrea Arcangeli [off-list ref] wrote:
quoted hunk
If the rwsem starves writers it wasn't strictly a bug but lockdep
doesn't like it and this avoids depending on lowlevel implementation
details of the lock.
...
From: Andrea Arcangeli <hidden> Date: 2015-05-22 20:48:42
On Fri, May 22, 2015 at 01:18:22PM -0700, Andrew Morton wrote:
On Thu, 14 May 2015 19:31:19 +0200 Andrea Arcangeli [off-list ref] wrote:
quoted
If the rwsem starves writers it wasn't strictly a bug but lockdep
doesn't like it and this avoids depending on lowlevel implementation
details of the lock.
...
I'm not sure what this is trying to do. BUILD_BUG_ON(local_variable)?
It goes bang in my build. I'll just delete it.
Yes, it has to be a false positive failure, so it's fine to drop
it. My gcc 4.8.4 can go inside the static called function and see that
only mcopy_atomic_pte can return -EFAULT. RHEL7 (4.8.3) gcc didn't
complain either. Perhaps to make the BUILD_BUG_ON work with older gcc,
it requrires a local variable set explicitly in the callee, but it's
not worth it.
It would be bad if we end up in the -EFAULT path in the zeropage case
(if somebody later adds an apparently innocent -EFAULT retval and
unexpectedly ends up in the mcopy_atomic_pte retry logic), but it's
not important, the caller should be reviewed before improvising new
retvals anyway.
The retry loop addition and the BUILD_BUG_ON is all about the
copy_from_user run while we already hold the mmap_sem (potentially of
a different process in the non-cooperative case but it's a problem if
it's the current task mmap_sem in case the rwlock implementation
changes to avoid write starvation and becomes non-reentrant). lockdep
definitely complains (even if I think in practice it'd be safe to
read-lock recurse, we just got lockdep complains never deadlocks in
fact). I didn't want to call gup_fast as copy_from_user is faster and
I got an usable user mapping with likely TLB entry hot too. The
lockdep warnings we hit I think were associated with NUMA hinting
faults or something infrequent like that, the fast path doesn't need
to retry.
Thanks,
Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrew Morton <akpm@linux-foundation.org> Date: 2015-05-22 21:18:36
There's a more serious failure with i386 allmodconfig:
fs/userfaultfd.c:145:2: note: in expansion of macro 'BUILD_BUG_ON'
BUILD_BUG_ON(sizeof(struct uffd_msg) != 32);
I'm surprised the feature is even reachable on i386 builds?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-05-23 01:04:46
On Fri, May 22, 2015 at 02:18:30PM -0700, Andrew Morton wrote:
There's a more serious failure with i386 allmodconfig:
fs/userfaultfd.c:145:2: note: in expansion of macro 'BUILD_BUG_ON'
BUILD_BUG_ON(sizeof(struct uffd_msg) != 32);
I'm surprised the feature is even reachable on i386 builds?
Unless we risk to run out of vma->vm_flags there's no particular
reason not to enable it on 32bit (even if we run out, making vm_flags
an unsigned long long is a few liner patch). Certainly it's less
useful on 32bit as there's a 3G limit but the max vmas per process are
still a small fraction of that. Especially if used for the volatile
pages on demand notification of page reclaim, it could end up useful
on arm32 (S6 is 64bit I think and latest snapdragon is too, so perhaps
it's too late anyway, but again it's not big deal).
Removing the BUILD_BUG_ON I think is not ok here because while I'm ok
to support 32bit archs, I don't want translation, the 64bit kernel
should talk with the 32bit app directly without a layer in between.
I tried to avoid using packet as without packed I could not get the
alignment wrong (and future union also couldn't get it wrong), and I
could avoid those reserved1/2/3, but it's more robust to use it in
combination with the BUILD_BUG_ON to detect right away problems like
this with 32bit builds that aligns things differently.
I'm actually surprised the buildbot that sends me email about all
archs didn't actually send me anything about it for 32bit x86?
Perhaps I'm overlooking something or x86 32bit (or any other 32bit
arch for that matter) isn't being checked? This is actually a fairly
recent change, perhaps the buildbot was shutdown recently? That
buildbot was very useful to detect for problems like this.
===
From 2f0a48670dc515932dec8b983871ec35caeba553 Mon Sep 17 00:00:00 2001
From: Andrea Arcangeli <redacted>
Date: Sat, 23 May 2015 02:26:32 +0200
Subject: [PATCH] userfaultfd: update the uffd_msg structure to be the same on
32/64bit
Avoiding to using packed allowed the code to be nicer and it avoided
the reserved1/2/3 but the structure must be the same for 32bit and
64bit archs so x86 applications built with the 32bit ABI can run on
the 64bit kernel without requiring translation of the data read
through the read syscall.
$ gcc -m64 p.c && ./a.out
32
0
16
8
8
16
24
$ gcc -m32 p.c && ./a.out
32
0
16
8
8
16
24
int main()
{
printf("%lu\n", sizeof(struct uffd_msg));
printf("%lu\n", (unsigned long) &((struct uffd_msg *) 0)->event);
printf("%lu\n", (unsigned long) &((struct uffd_msg *) 0)->arg.pagefault.address);
printf("%lu\n", (unsigned long) &((struct uffd_msg *) 0)->arg.pagefault.flags);
printf("%lu\n", (unsigned long) &((struct uffd_msg *) 0)->arg.reserved.reserved1);
printf("%lu\n", (unsigned long) &((struct uffd_msg *) 0)->arg.reserved.reserved2);
printf("%lu\n", (unsigned long) &((struct uffd_msg *) 0)->arg.reserved.reserved3);
}
Reported-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Andrea Arcangeli <redacted>
---
include/uapi/linux/userfaultfd.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Dave Hansen <hidden> Date: 2015-06-23 19:00:32
On 05/14/2015 10:31 AM, Andrea Arcangeli wrote:
+static int userfaultfd_wake_function(wait_queue_t *wq, unsigned mode,
+ int wake_flags, void *key)
+{
+ struct userfaultfd_wake_range *range = key;
+ int ret;
+ struct userfaultfd_wait_queue *uwq;
+ unsigned long start, len;
+
+ uwq = container_of(wq, struct userfaultfd_wait_queue, wq);
+ ret = 0;
+ /* don't wake the pending ones to avoid reads to block */
+ if (uwq->pending && !ACCESS_ONCE(uwq->ctx->released))
+ goto out;
+ /* len == 0 means wake all */
+ start = range->start;
+ len = range->len;
+ if (len && (start > uwq->address || start + len <= uwq->address))
+ goto out;
+ ret = wake_up_state(wq->private, mode);
+ if (ret)
+ /* wake only once, autoremove behavior */
+ list_del_init(&wq->task_list);
+out:
+ return ret;
+}
...
+static __always_inline int validate_range(struct mm_struct *mm,
+ __u64 start, __u64 len)
+{
+ __u64 task_size = mm->task_size;
+
+ if (start & ~PAGE_MASK)
+ return -EINVAL;
+ if (len & ~PAGE_MASK)
+ return -EINVAL;
+ if (!len)
+ return -EINVAL;
+ if (start < mmap_min_addr)
+ return -EINVAL;
+ if (start >= task_size)
+ return -EINVAL;
+ if (len > task_size - start)
+ return -EINVAL;
+ return 0;
+}
Hey Andrea,
Down in userfaultfd_wake_function(), it looks like you intended for a
len=0 to mean "wake all". But the validate_range() that we do from
userspace has a !len check in it, which keeps us from passing a len=0 in
from userspace.
Was that "wake all" for some internal use, or is the check too strict?
I was trying to use the wake ioctl after an madvise() (as opposed to
filling things in using a userfd copy).
From: Andrea Arcangeli <hidden> Date: 2015-06-23 21:41:58
Hi Dave,
On Tue, Jun 23, 2015 at 12:00:19PM -0700, Dave Hansen wrote:
Down in userfaultfd_wake_function(), it looks like you intended for a
len=0 to mean "wake all". But the validate_range() that we do from
userspace has a !len check in it, which keeps us from passing a len=0 in
from userspace.
Was that "wake all" for some internal use, or is the check too strict?
It's for internal use or userfaultfd_release that has to wake them all
(after setting ctx->released) if the uffd is closed. It avoids to
enlarge the structure by depending on the invariant that userland
cannot pass len=0.
If we'd accept len=0 from userland as valid, I'd be safer if it does
nothing like in madvise, I doubt we want to expose this non standard
kernel internal behavior to userland.
I was trying to use the wake ioctl after an madvise() (as opposed to
filling things in using a userfd copy).
madvise will return 0 if len=0, mremap would return -EINVAL if new_len
is zero, mmap also returns -EINVAL if len is 0, not all MM syscalls
are as permissive as madvise. Can't you pass the same len you pass to
madvise to UFFDIO_WAKE (or just skip the call if the madvise len is
zero)?
Thanks,
Andrea
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
----
powerpc: Bump up __NR_syscalls to account for __NR_userfaultfd
From: Bharata B Rao <redacted>
With userfaultfd syscall, the number of syscalls will be 365 on PowerPC.
Reflect the same in __NR_syscalls.
Signed-off-by: Bharata B Rao <redacted>
---
arch/powerpc/include/asm/unistd.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Andrea Arcangeli <hidden> Date: 2015-08-11 13:48:32
Hello Bharata,
On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
Not late, just in time. I increased the number of syscalls in earlier
versions, it must have gotten lost during a rejecting rebase, sorry.
I applied it to my tree and it can be applied to -mm and linux-next,
thanks!
The syscall for arm32 are also ready and on their way to the arm tree,
the testsuite worked fine there. ppc also should work fine if you
could confirm it'd be interesting, just beware that I got a typo in
the testcase:
----
powerpc: Bump up __NR_syscalls to account for __NR_userfaultfd
From: Bharata B Rao <redacted>
With userfaultfd syscall, the number of syscalls will be 365 on PowerPC.
Reflect the same in __NR_syscalls.
Signed-off-by: Bharata B Rao <redacted>
---
arch/powerpc/include/asm/unistd.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Andrea Arcangeli <redacted>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Bharata B Rao <hidden> Date: 2015-08-12 05:24:06
On Tue, Aug 11, 2015 at 03:48:26PM +0200, Andrea Arcangeli wrote:
Hello Bharata,
On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
quoted
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
Not late, just in time. I increased the number of syscalls in earlier
versions, it must have gotten lost during a rejecting rebase, sorry.
I applied it to my tree and it can be applied to -mm and linux-next,
thanks!
The syscall for arm32 are also ready and on their way to the arm tree,
the testsuite worked fine there. ppc also should work fine if you
could confirm it'd be interesting, just beware that I got a typo in
the testcase:
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2015-09-08 06:08:14
On Wed, 2015-08-12 at 10:53 +0530, Bharata B Rao wrote:
On Tue, Aug 11, 2015 at 03:48:26PM +0200, Andrea Arcangeli wrote:
quoted
Hello Bharata,
On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
quoted
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
Not late, just in time. I increased the number of syscalls in earlier
versions, it must have gotten lost during a rejecting rebase, sorry.
I applied it to my tree and it can be applied to -mm and linux-next,
thanks!
The syscall for arm32 are also ready and on their way to the arm tree,
the testsuite worked fine there. ppc also should work fine if you
could confirm it'd be interesting, just beware that I got a typo in
the testcase:
From: Bharata B Rao <hidden> Date: 2015-09-08 06:40:00
On Tue, Sep 08, 2015 at 04:08:06PM +1000, Michael Ellerman wrote:
On Wed, 2015-08-12 at 10:53 +0530, Bharata B Rao wrote:
quoted
On Tue, Aug 11, 2015 at 03:48:26PM +0200, Andrea Arcangeli wrote:
quoted
Hello Bharata,
On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
quoted
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
Not late, just in time. I increased the number of syscalls in earlier
versions, it must have gotten lost during a rejecting rebase, sorry.
I applied it to my tree and it can be applied to -mm and linux-next,
thanks!
The syscall for arm32 are also ready and on their way to the arm tree,
the testsuite worked fine there. ppc also should work fine if you
could confirm it'd be interesting, just beware that I got a typo in
the testcase:
Hmm, not for me. See below.
What setup were you testing on Bharata?
I was on commit a94572f5799dd of userfault21 branch in Andrea's tree
git://git.kernel.org/pub/scm/linux/kernel/git/andrea/aa.git
#uname -a
Linux 4.1.0-rc8+ #1 SMP Tue Aug 11 11:33:50 IST 2015 ppc64le ppc64le ppc64le GNU/Linux
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
Mine is:
$ uname -a
Linux lebuntu 4.2.0-09705-g3a166acc1432 #2 SMP Tue Sep 8 15:18:00 AEST 2015 ppc64le ppc64le ppc64le GNU/Linux
Which is 7d9071a09502 plus a couple of powerpc patches.
$ zgrep USERFAULTFD /proc/config.gz
CONFIG_USERFAULTFD=y
$ sudo ./userfaultfd 128 32
nr_pages: 2048, nr_pages_per_cpu: 128
bounces: 31, mode: rnd racing ver poll, error mutex 2 2
error mutex 2 10
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2015-09-08 07:14:31
On Tue, 2015-09-08 at 12:09 +0530, Bharata B Rao wrote:
On Tue, Sep 08, 2015 at 04:08:06PM +1000, Michael Ellerman wrote:
quoted
On Wed, 2015-08-12 at 10:53 +0530, Bharata B Rao wrote:
quoted
On Tue, Aug 11, 2015 at 03:48:26PM +0200, Andrea Arcangeli wrote:
quoted
Hello Bharata,
On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
quoted
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
Not late, just in time. I increased the number of syscalls in earlier
versions, it must have gotten lost during a rejecting rebase, sorry.
I applied it to my tree and it can be applied to -mm and linux-next,
thanks!
The syscall for arm32 are also ready and on their way to the arm tree,
the testsuite worked fine there. ppc also should work fine if you
could confirm it'd be interesting, just beware that I got a typo in
the testcase:
Hmm, not for me. See below.
What setup were you testing on Bharata?
I was on commit a94572f5799dd of userfault21 branch in Andrea's tree
git://git.kernel.org/pub/scm/linux/kernel/git/andrea/aa.git
#uname -a
Linux 4.1.0-rc8+ #1 SMP Tue Aug 11 11:33:50 IST 2015 ppc64le ppc64le ppc64le GNU/Linux
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
OK, do you mind testing mainline with the same setup to see if the selftest
passes.
cheers
From: Dr. David Alan Gilbert <hidden> Date: 2015-09-08 08:59:58
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
On Tue, Sep 08, 2015 at 04:08:06PM +1000, Michael Ellerman wrote:
quoted
On Wed, 2015-08-12 at 10:53 +0530, Bharata B Rao wrote:
quoted
On Tue, Aug 11, 2015 at 03:48:26PM +0200, Andrea Arcangeli wrote:
quoted
Hello Bharata,
On Tue, Aug 11, 2015 at 03:37:29PM +0530, Bharata B Rao wrote:
quoted
May be it is a bit late to bring this up, but I needed the following fix
to userfault21 branch of your git tree to compile on powerpc.
Not late, just in time. I increased the number of syscalls in earlier
versions, it must have gotten lost during a rejecting rebase, sorry.
I applied it to my tree and it can be applied to -mm and linux-next,
thanks!
The syscall for arm32 are also ready and on their way to the arm tree,
the testsuite worked fine there. ppc also should work fine if you
could confirm it'd be interesting, just beware that I got a typo in
the testcase:
Hmm, not for me. See below.
What setup were you testing on Bharata?
I was on commit a94572f5799dd of userfault21 branch in Andrea's tree
git://git.kernel.org/pub/scm/linux/kernel/git/andrea/aa.git
#uname -a
Linux 4.1.0-rc8+ #1 SMP Tue Aug 11 11:33:50 IST 2015 ppc64le ppc64le ppc64le GNU/Linux
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
Interesting - I'd not got that far myself on power; I was hitting a problem
loading htab ( htab_load() bad index 2113929216 (14848+0 entries) in htab stream (htab_shift=25) )
Did you have to make any changes to the qemu code to get that happy?
Dave
quoted
Mine is:
$ uname -a
Linux lebuntu 4.2.0-09705-g3a166acc1432 #2 SMP Tue Sep 8 15:18:00 AEST 2015 ppc64le ppc64le ppc64le GNU/Linux
Which is 7d9071a09502 plus a couple of powerpc patches.
$ zgrep USERFAULTFD /proc/config.gz
CONFIG_USERFAULTFD=y
$ sudo ./userfaultfd 128 32
nr_pages: 2048, nr_pages_per_cpu: 128
bounces: 31, mode: rnd racing ver poll, error mutex 2 2
error mutex 2 10
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
From: Bharata B Rao <hidden> Date: 2015-09-08 10:01:41
On Tue, Sep 08, 2015 at 09:59:47AM +0100, Dr. David Alan Gilbert wrote:
* Bharata B Rao (bharata-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org) wrote:
quoted
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
Interesting - I'd not got that far myself on power; I was hitting a problem
loading htab ( htab_load() bad index 2113929216 (14848+0 entries) in htab stream (htab_shift=25) )
Did you have to make any changes to the qemu code to get that happy?
I should have mentioned that I tried only QEMU driven migration within
the same host using wp3-postcopy branch of your tree. I don't see the
above issue.
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off x-postcopy-ram: on
Migration status: completed
total time: 39432 milliseconds
downtime: 162 milliseconds
setup: 14 milliseconds
transferred ram: 1297209 kbytes
throughput: 270.72 mbps
remaining ram: 0 kbytes
total ram: 4194560 kbytes
duplicate: 734015 pages
skipped: 0 pages
normal: 318469 pages
normal bytes: 1273876 kbytes
dirty sync count: 4
I will try migration between different hosts soon and check.
Regards,
Bharata.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2015-09-08 10:40:30
On Tue, 2015-09-08 at 17:14 +1000, Michael Ellerman wrote:
On Tue, 2015-09-08 at 12:09 +0530, Bharata B Rao wrote:
quoted
On Tue, Sep 08, 2015 at 04:08:06PM +1000, Michael Ellerman wrote:
quoted
Hmm, not for me. See below.
What setup were you testing on Bharata?
I was on commit a94572f5799dd of userfault21 branch in Andrea's tree
git://git.kernel.org/pub/scm/linux/kernel/git/andrea/aa.git
#uname -a
Linux 4.1.0-rc8+ #1 SMP Tue Aug 11 11:33:50 IST 2015 ppc64le ppc64le ppc64le GNU/Linux
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
OK, do you mind testing mainline with the same setup to see if the selftest
passes.
Ah, I just tried it on big endian and it works. So it seems to not work on
little endian for some reason, /probably/ a test case bug?
cheers
From: Dr. David Alan Gilbert <hidden> Date: 2015-09-08 12:28:39
* Michael Ellerman (mpe@ellerman.id.au) wrote:
On Tue, 2015-09-08 at 17:14 +1000, Michael Ellerman wrote:
quoted
On Tue, 2015-09-08 at 12:09 +0530, Bharata B Rao wrote:
quoted
On Tue, Sep 08, 2015 at 04:08:06PM +1000, Michael Ellerman wrote:
quoted
Hmm, not for me. See below.
What setup were you testing on Bharata?
I was on commit a94572f5799dd of userfault21 branch in Andrea's tree
git://git.kernel.org/pub/scm/linux/kernel/git/andrea/aa.git
#uname -a
Linux 4.1.0-rc8+ #1 SMP Tue Aug 11 11:33:50 IST 2015 ppc64le ppc64le ppc64le GNU/Linux
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
OK, do you mind testing mainline with the same setup to see if the selftest
passes.
Ah, I just tried it on big endian and it works. So it seems to not work on
little endian for some reason, /probably/ a test case bug?
Hmm; I think we're missing a test-case fix that Andrea made me for a bug I hit on Power
I hit a couple of weeks back. I think that would have been on le.
Dave
cheers
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
From: Dr. David Alan Gilbert <hidden> Date: 2015-09-08 12:47:04
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
On Tue, Sep 08, 2015 at 09:59:47AM +0100, Dr. David Alan Gilbert wrote:
quoted
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
quoted
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
Interesting - I'd not got that far myself on power; I was hitting a problem
loading htab ( htab_load() bad index 2113929216 (14848+0 entries) in htab stream (htab_shift=25) )
Did you have to make any changes to the qemu code to get that happy?
I should have mentioned that I tried only QEMU driven migration within
the same host using wp3-postcopy branch of your tree. I don't see the
above issue.
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off x-postcopy-ram: on
Migration status: completed
total time: 39432 milliseconds
downtime: 162 milliseconds
setup: 14 milliseconds
transferred ram: 1297209 kbytes
throughput: 270.72 mbps
remaining ram: 0 kbytes
total ram: 4194560 kbytes
duplicate: 734015 pages
skipped: 0 pages
normal: 318469 pages
normal bytes: 1273876 kbytes
dirty sync count: 4
I will try migration between different hosts soon and check.
I hit that on the same host; are you sure you've switched into postcopy mode;
i.e. issued a migrate_start_postcopy before the end of migration?
(My current world I have working on x86-64 and I've also tested reasonably well
on aarch64, and get to that htab problem on Power).
Dave
Regards,
Bharata.
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
From: Bharata B Rao <hidden> Date: 2015-09-08 13:37:46
On Tue, Sep 08, 2015 at 01:46:52PM +0100, Dr. David Alan Gilbert wrote:
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
quoted
On Tue, Sep 08, 2015 at 09:59:47AM +0100, Dr. David Alan Gilbert wrote:
quoted
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
quoted
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
Interesting - I'd not got that far myself on power; I was hitting a problem
loading htab ( htab_load() bad index 2113929216 (14848+0 entries) in htab stream (htab_shift=25) )
Did you have to make any changes to the qemu code to get that happy?
I should have mentioned that I tried only QEMU driven migration within
the same host using wp3-postcopy branch of your tree. I don't see the
above issue.
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off x-postcopy-ram: on
Migration status: completed
total time: 39432 milliseconds
downtime: 162 milliseconds
setup: 14 milliseconds
transferred ram: 1297209 kbytes
throughput: 270.72 mbps
remaining ram: 0 kbytes
total ram: 4194560 kbytes
duplicate: 734015 pages
skipped: 0 pages
normal: 318469 pages
normal bytes: 1273876 kbytes
dirty sync count: 4
I will try migration between different hosts soon and check.
I hit that on the same host; are you sure you've switched into postcopy mode;
i.e. issued a migrate_start_postcopy before the end of migration?
Sorry I was following your discussion with Li in this thread
https://www.marc.info/?l=qemu-devel&m=143035620026744&w=4
and it wasn't obvious to me that anything apart from turning on the
x-postcopy-ram capability was required :(
So I do see the problem now.
At the source
-------------
Error reading data from KVM HTAB fd: Bad file descriptor
Segmentation fault
At the target
-------------
htab_load() bad index 2113929216 (14336+0 entries) in htab stream (htab_shift=25)
qemu-system-ppc64: error while loading state section id 56(spapr/htab)
qemu-system-ppc64: postcopy_ram_listen_thread: loadvm failed: -22
qemu-system-ppc64: VQ 0 size 0x100 Guest index 0x0 inconsistent with Host index 0x1f: delta 0xffe1
qemu-system-ppc64: error while loading state for instance 0x0 of device 'pci@800000020000000:00.0/virtio-net'
*** Error in `./ppc64-softmmu/qemu-system-ppc64': corrupted double-linked list: 0x00000100241234a0 ***
======= Backtrace: =========
/lib64/power8/libc.so.6Segmentation fault
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Dr. David Alan Gilbert <hidden> Date: 2015-09-08 14:14:09
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
On Tue, Sep 08, 2015 at 01:46:52PM +0100, Dr. David Alan Gilbert wrote:
quoted
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
quoted
On Tue, Sep 08, 2015 at 09:59:47AM +0100, Dr. David Alan Gilbert wrote:
quoted
* Bharata B Rao (bharata@linux.vnet.ibm.com) wrote:
quoted
In fact I had successfully done postcopy migration of sPAPR guest with
this setup.
Interesting - I'd not got that far myself on power; I was hitting a problem
loading htab ( htab_load() bad index 2113929216 (14848+0 entries) in htab stream (htab_shift=25) )
Did you have to make any changes to the qemu code to get that happy?
I should have mentioned that I tried only QEMU driven migration within
the same host using wp3-postcopy branch of your tree. I don't see the
above issue.
(qemu) info migrate
capabilities: xbzrle: off rdma-pin-all: off auto-converge: off zero-blocks: off compress: off x-postcopy-ram: on
Migration status: completed
total time: 39432 milliseconds
downtime: 162 milliseconds
setup: 14 milliseconds
transferred ram: 1297209 kbytes
throughput: 270.72 mbps
remaining ram: 0 kbytes
total ram: 4194560 kbytes
duplicate: 734015 pages
skipped: 0 pages
normal: 318469 pages
normal bytes: 1273876 kbytes
dirty sync count: 4
I will try migration between different hosts soon and check.
I hit that on the same host; are you sure you've switched into postcopy mode;
i.e. issued a migrate_start_postcopy before the end of migration?
Sorry I was following your discussion with Li in this thread
https://www.marc.info/?l=qemu-devel&m=143035620026744&w=4
and it wasn't obvious to me that anything apart from turning on the
x-postcopy-ram capability was required :(
OK.
So I do see the problem now.
At the source
-------------
Error reading data from KVM HTAB fd: Bad file descriptor
Segmentation fault
At the target
-------------
htab_load() bad index 2113929216 (14336+0 entries) in htab stream (htab_shift=25)
qemu-system-ppc64: error while loading state section id 56(spapr/htab)
qemu-system-ppc64: postcopy_ram_listen_thread: loadvm failed: -22
qemu-system-ppc64: VQ 0 size 0x100 Guest index 0x0 inconsistent with Host index 0x1f: delta 0xffe1
qemu-system-ppc64: error while loading state for instance 0x0 of device 'pci@800000020000000:00.0/virtio-net'
*** Error in `./ppc64-softmmu/qemu-system-ppc64': corrupted double-linked list: 0x00000100241234a0 ***
======= Backtrace: =========
/lib64/power8/libc.so.6Segmentation fault
Good - my current world has got rid of the segfaults/corruption in the cleanup on power - but those
are only after it stumbled over the htab problem.
I don't know the innards of power/htab, so if you've got any pointers on what upset it
I'd be happy for some pointers.
(We should probably trim the cc - since I don't think this is userfault related).
Dave
--
Dr. David Alan Gilbert / dgilbert@redhat.com / Manchester, UK
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -0,0 +1,140 @@+= Userfaultfd =++== Objective ==++Userfaults allow the implementation of on-demand paging from userland+and more generally they allow userland to take control various memory+page faults, something otherwise only the kernel code could do.++For example userfaults allows a proper and more optimal implementation+of the PROT_NONE+SIGSEGV trick.++== Design ==++Userfaults are delivered and resolved through the userfaultfd syscall.++The userfaultfd (aside from registering and unregistering virtual+memory ranges) provides two primary functionalities:++1) read/POLLIN protocol to notify a userland thread of the faults+ happening++2) various UFFDIO_* ioctls that can manage the virtual memory regions+ registered in the userfaultfd that allows userland to efficiently+ resolve the userfaults it receives via 1) or to manage the virtual+ memory in the background++The real advantage of userfaults if compared to regular virtual memory+management of mremap/mprotect is that the userfaults in all their+operations never involve heavyweight structures like vmas (in fact the+userfaultfd runtime load never takes the mmap_sem for writing).++Vmas are not suitable for page- (or hugepage) granular fault tracking+when dealing with virtual address spaces that could span+Terabytes. Too many vmas would be needed for that.++The userfaultfd once opened by invoking the syscall, can also be+passed using unix domain sockets to a manager process, so the same+manager process could handle the userfaults of a multitude of+different processes without them being aware about what is going on+(well of course unless they later try to use the userfaultfd+themselves on the same region the manager is already tracking, which+is a corner case that would currently return -EBUSY).++== API ==++When first opened the userfaultfd must be enabled invoking the+UFFDIO_API ioctl specifying a uffdio_api.api value set to UFFD_API (or+a later API version) which will specify the read/POLLIN protocol+userland intends to speak on the UFFD. The UFFDIO_API ioctl if+successful (i.e. if the requested uffdio_api.api is spoken also by the+running kernel), will return into uffdio_api.features and+uffdio_api.ioctls two 64bit bitmasks of respectively the activated+feature of the read(2) protocol and the generic ioctl available.++Once the userfaultfd has been enabled the UFFDIO_REGISTER ioctl should+be invoked (if present in the returned uffdio_api.ioctls bitmask) to+register a memory range in the userfaultfd by setting the+uffdio_register structure accordingly. The uffdio_register.mode+bitmask will specify to the kernel which kind of faults to track for+the range (UFFDIO_REGISTER_MODE_MISSING would track missing+pages). The UFFDIO_REGISTER ioctl will return the+uffdio_register.ioctls bitmask of ioctls that are suitable to resolve+userfaults on the range registered. Not all ioctls will necessarily be+supported for all memory types depending on the underlying virtual+memory backend (anonymous memory vs tmpfs vs real filebacked+mappings).++Userland can use the uffdio_register.ioctls to manage the virtual+address space in the background (to add or potentially also remove+memory from the userfaultfd registered range). This means a userfault+could be triggering just before userland maps in the background the+user-faulted page.++The primary ioctl to resolve userfaults is UFFDIO_COPY. That+atomically copies a page into the userfault registered range and wakes+up the blocked userfaults (unless uffdio_copy.mode &+UFFDIO_COPY_MODE_DONTWAKE is set). Other ioctl works similarly to+UFFDIO_COPY.++== QEMU/KVM ==++QEMU/KVM is using the userfaultfd syscall to implement postcopy live+migration. Postcopy live migration is one form of memory+externalization consisting of a virtual machine running with part or+all of its memory residing on a different node in the cloud. The+userfaultfd abstraction is generic enough that not a single line of+KVM kernel code had to be modified in order to add postcopy live+migration to QEMU.++Guest async page faults, FOLL_NOWAIT and all other GUP features work+just fine in combination with userfaults. Userfaults trigger async+page faults in the guest scheduler so those guest processes that+aren't waiting for userfaults (i.e. network bound) can keep running in+the guest vcpus.++It is generally beneficial to run one pass of precopy live migration+just before starting postcopy live migration, in order to avoid+generating userfaults for readonly guest regions.++The implementation of postcopy live migration currently uses one+single bidirectional socket but in the future two different sockets+will be used (to reduce the latency of the userfaults to the minimum+possible without having to decrease /proc/sys/net/ipv4/tcp_wmem).++The QEMU in the source node writes all pages that it knows are missing+in the destination node, into the socket, and the migration thread of+the QEMU running in the destination node runs UFFDIO_COPY|ZEROPAGE+ioctls on the userfaultfd in order to map the received pages into the+guest (UFFDIO_ZEROCOPY is used if the source page was a zero page).++A different postcopy thread in the destination node listens with+poll() to the userfaultfd in parallel. When a POLLIN event is+generated after a userfault triggers, the postcopy thread read() from+the userfaultfd and receives the fault address (or -EAGAIN in case the+userfault was already resolved and waken by a UFFDIO_COPY|ZEROPAGE run+by the parallel QEMU migration thread).++After the QEMU postcopy thread (running in the destination node) gets+the userfault address it writes the information about the missing page+into the socket. The QEMU source node receives the information and+roughly "seeks" to that page address and continues sending all+remaining missing pages from that new page offset. Soon after that+(just the time to flush the tcp_wmem queue through the network) the+migration thread in the QEMU running in the destination node will+receive the page that triggered the userfault and it'll map it as+usual with the UFFDIO_COPY|ZEROPAGE (without actually knowing if it+was spontaneously sent by the source or if it was an urgent page+requested through an userfault).++By the time the userfaults start, the QEMU in the destination node+doesn't need to keep any per-page state bitmap relative to the live+migration around and a single per-page bitmap has to be maintained in+the QEMU running in the source node to know which pages are still+missing in the destination node. The bitmap in the source node is+checked to find which missing pages to send in round robin and we seek+over it when receiving incoming userfaults. After sending each page of+course the bitmap is updated accordingly. It's also useful to avoid+sending the same page twice (in case the userfault is read by the+postcopy thread just before UFFDIO_COPY|ZEROPAGE runs in the migration+thread).--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Peter Zijlstra <peterz@infradead.org> Date: 2015-10-22 12:11:22
On Thu, May 14, 2015 at 07:31:11PM +0200, Andrea Arcangeli wrote:
quoted hunk
@@ -255,21 +259,23 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address, * through poll/read(). */ __add_wait_queue(&ctx->fault_wqh, &uwq.wq);- for (;;) {- set_current_state(TASK_KILLABLE);- if (!uwq.pending || ACCESS_ONCE(ctx->released) ||- fatal_signal_pending(current))- break;- spin_unlock(&ctx->fault_wqh.lock);+ set_current_state(TASK_KILLABLE);+ spin_unlock(&ctx->fault_wqh.lock);+ if (likely(!ACCESS_ONCE(ctx->released) &&+ !fatal_signal_pending(current))) { wake_up_poll(&ctx->fd_wqh, POLLIN); schedule();+ ret |= VM_FAULT_MAJOR;+ }
So what happens here if schedule() spontaneously wakes for no reason?
I'm not sure enough of userfaultfd semantics to say if that would be
bad, but the code looks suspiciously like it relies on schedule() not to
do that; which is wrong.
+ __set_current_state(TASK_RUNNING);
+ /* see finish_wait() comment for why list_empty_careful() */
+ if (!list_empty_careful(&uwq.wq.task_list)) {
spin_lock(&ctx->fault_wqh.lock);
+ list_del_init(&uwq.wq.task_list);
+ spin_unlock(&ctx->fault_wqh.lock);
}
- __remove_wait_queue(&ctx->fault_wqh, &uwq.wq);
- __set_current_state(TASK_RUNNING);
- spin_unlock(&ctx->fault_wqh.lock);
/*
* ctx may go away after this if the userfault pseudo fd is
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-10-22 13:20:23
On Thu, Oct 22, 2015 at 02:10:56PM +0200, Peter Zijlstra wrote:
On Thu, May 14, 2015 at 07:31:11PM +0200, Andrea Arcangeli wrote:
quoted
@@ -255,21 +259,23 @@ int handle_userfault(struct vm_area_struct *vma, unsigned long address, * through poll/read(). */ __add_wait_queue(&ctx->fault_wqh, &uwq.wq);- for (;;) {- set_current_state(TASK_KILLABLE);- if (!uwq.pending || ACCESS_ONCE(ctx->released) ||- fatal_signal_pending(current))- break;- spin_unlock(&ctx->fault_wqh.lock);+ set_current_state(TASK_KILLABLE);+ spin_unlock(&ctx->fault_wqh.lock);+ if (likely(!ACCESS_ONCE(ctx->released) &&+ !fatal_signal_pending(current))) { wake_up_poll(&ctx->fd_wqh, POLLIN); schedule();+ ret |= VM_FAULT_MAJOR;+ }
So what happens here if schedule() spontaneously wakes for no reason?
I'm not sure enough of userfaultfd semantics to say if that would be
bad, but the code looks suspiciously like it relies on schedule() not to
do that; which is wrong.
That would repeat the fault and trigger the DEBUG_VM printk above,
complaining that FAULT_FLAG_ALLOW_RETRY is not set. It is only a
problem for kernel faults (copy_user/get_user_pages*). Userland won't
error out in such a way because userland would return 0 and not
VM_FAULT_RETRY. So it's only required when the task schedule in
TASK_KILLABLE state.
If schedule spontaneously wakes up a task in TASK_KILLABLE state that
would be a bug in the scheduler in my view. Luckily there doesn't seem
to be such a bug, or at least we never experienced it.
Overall this dependency on the scheduler will be lifted soon, as it
must be lifted in order to track the write protect faults, so longer
term this is not a concern and this is not a design issue, but this
remains an implementation detail that avoided to change the arch code
and gup.
If you send a reproducer to show how the current scheduler can wake up
the task in TASK_KILLABLE despite not receiving a wakeup, that would
help too as we never experienced that.
The reason this dependency will be lifted soon is that the userfaultfd
write protect tracking may be armed at any time while the app is
running so we may already be in the middle of a page fault that
returned VM_FAULT_RETRY by the time we arm the write protect
tracking. So longer term the arch page fault and
__get_user_pages_locked must allow handle_userfault() to return
VM_FAULT_RETRY even if FAULT_FLAG_TRIED is set. Then we don't care if
the task is waken.
Waking a task in TASK_KILLABLE state it will still waste CPU so the
scheduler still shouldn't do that. All load balancing works better if
the task isn't running anyway so I can't imagine a good reason for
wanting to run a task in TASK_KILLABLE state before it gets the
wakeup.
Trying to predict that a wakeup is always happening in less time than
it takes to schedule the task out of the CPU, sounds a very CPU
intensive thing to measure and it's probably better to leave those
heuristics in the caller like by spinning on a lock for a while before
blocking.
Thanks,
Andrea
From: Peter Zijlstra <peterz@infradead.org> Date: 2015-10-22 13:38:34
On Thu, Oct 22, 2015 at 03:20:15PM +0200, Andrea Arcangeli wrote:
If schedule spontaneously wakes up a task in TASK_KILLABLE state that
would be a bug in the scheduler in my view. Luckily there doesn't seem
to be such a bug, or at least we never experienced it.
Well, there will be a wakeup, just not the one you were hoping for.
We have code that does:
@cond = true;
get_task_struct(p);
queue(p)
/* random wait somewhere */
for (;;) {
prepare_to_wait();
if (@cond)
break;
...
handle_userfault()
...
schedule();
...
dequeue(p)
wake_up_process(p) ---> wakeup without userfault wakeup
These races are (extremely) rare, but they do exist. Therefore one must
never assume schedule() will not spuriously wake because of these
things.
Also, see:
lkml.kernel.org/r/CA+55aFwHkOo+YGWKYROmce1-H_uG3KfEUmCkJUerTj=ojY2H6Q@mail.gmail.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-10-22 14:18:39
On Thu, Oct 22, 2015 at 03:38:24PM +0200, Peter Zijlstra wrote:
On Thu, Oct 22, 2015 at 03:20:15PM +0200, Andrea Arcangeli wrote:
quoted
If schedule spontaneously wakes up a task in TASK_KILLABLE state that
would be a bug in the scheduler in my view. Luckily there doesn't seem
to be such a bug, or at least we never experienced it.
Well, there will be a wakeup, just not the one you were hoping for.
We have code that does:
@cond = true;
get_task_struct(p);
queue(p)
/* random wait somewhere */
for (;;) {
prepare_to_wait();
if (@cond)
break;
...
handle_userfault()
...
schedule();
...
dequeue(p)
wake_up_process(p) ---> wakeup without userfault wakeup
These races are (extremely) rare, but they do exist. Therefore one must
never assume schedule() will not spuriously wake because of these
things.
Also, see:
lkml.kernel.org/r/CA+55aFwHkOo+YGWKYROmce1-H_uG3KfEUmCkJUerTj=ojY2H6Q@mail.gmail.com
With one more spinlock taken in the fast path we could recheck if the
waitqueue is still queued and this is a false positive extremely rare
spurious wakeup, and in such case set the state back to TASK_KILLABLE
and schedule.
However in the long term such a spinlock should be removed because
it's faster to stick with the current lockless list_empty_careful and
not to recheck the auto-remove waitqueue, but then we must be able to
re-enter handle_userfault() even if FAULT_FLAG_TRIED was set
(currently we can't return VM_FAULT_RETRY if FAULT_FLAG_TRIED is set
and that's the problem). This change is planned for a long time as we
need it to arm the vma-less write protection while the app is running,
so I'm not sure if it's worth going for the short term fix if this is
extremely rare.
The risk of memory corruption is still zero no matter what happens
here, in the extremely rare case the app will get a SIGBUS or a
syscall will return -EFAULT. The kernel also cannot crash. So it's not
very severe concern if it happens extremely rarely (we never
reproduced it and stress testing run for months). Of course in the
longer term this would have been fixed regardless as said in previous
email.
I think going for the longer term fix that was already planned, is
better than doing a short term fix and the real question is how I
should proceed to change the arch code and gup to cope with
handle_userfault() being re-entered.
The simplest thing is to drop FAULT_FLAG_TRIED as a whole. Or I could
add a new VM_FAULT_USERFAULT flag specific to handle_userfault that
would be returned even if FAULT_FLAG_TRIED is set, so that only
userfaults will be allowed to be repeated indefinitely (and then
VM_FAULT_USERFAULT shouldn't trigger a transition to FAULT_FLAG_TRIED,
unlike VM_FAULT_RETRY does).
This is all about being allowed to drop the mmap_sem.
If we'd check the waitqueue with the spinlock (to be sure the wakeup
isn't happening from under us while we check if we got an userfault
wakeup or if this is a spurious schedule), we could also limit the
VM_FAULT_RETRY to 2 max events if I add a FAULT_FLAG_TRIED2 and I
still use VM_FAULT_RETRY (instead of VM_FAULT_USERFAULT).
Being able to return VM_FAULT_RETRY indefinitely is only needed if we
don't handle the extremely wakeup race condition in handle_userfault
by taking the spinlock once more time in the fast path (i.e. after the
schedule).
I'm not exactly sure why we allow VM_FAULT_RETRY only once currently
so I'm tempted to drop FAULT_FLAG_TRIED entirely.
I've no real preference on how to tweak the page fault code to be able
to return VM_FAULT_RETRY indefinitely and I would aim for the smallest
change possible, so if you've suggestions now it's good time.
Thanks,
Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Peter Zijlstra <peterz@infradead.org> Date: 2015-10-22 15:15:24
On Thu, Oct 22, 2015 at 04:18:31PM +0200, Andrea Arcangeli wrote:
The risk of memory corruption is still zero no matter what happens
here, in the extremely rare case the app will get a SIGBUS or a
That might still upset people, SIGBUS isn't something an app can really
recover from.
I'm not exactly sure why we allow VM_FAULT_RETRY only once currently
so I'm tempted to drop FAULT_FLAG_TRIED entirely.
I think to ensure we make forward progress.
I've no real preference on how to tweak the page fault code to be able
to return VM_FAULT_RETRY indefinitely and I would aim for the smallest
change possible, so if you've suggestions now it's good time.
Indefinitely is such a long time, we should try and finish
computation before the computer dies etc. :-)
Yes, yes.. I know, extremely unlikely etc. Still guarantees are good.
In any case, I'm not really too bothered how you fix it, just figured
I'd let you know.
From: Andrea Arcangeli <hidden> Date: 2015-10-22 15:31:00
On Thu, Oct 22, 2015 at 05:15:09PM +0200, Peter Zijlstra wrote:
Indefinitely is such a long time, we should try and finish
computation before the computer dies etc. :-)
Indefinitely as read_seqcount_retry, eventually it makes progress.
Even returning 0 from the page fault can trigger it again
indefinitely, so VM_FAULT_RETRY isn't fundamentally different from
returning 0 and retrying the page fault again later. So it's not clear
why VM_FAULT_RETRY can only try once more.
FAULT_FLAG_TRIED as a message to the VM so it starts to do heavy
locking and block more aggressively is actually useful as such, but it
shouldn't be a replacement of FAULT_FLAG_ALLOW_RETRY. What I meant
with removing FAULT_FLAG_TRIED is really about converting it to an
hint, but not controlling if the page fault can keep retrying
in-kernel.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -0,0 +1,140 @@+= Userfaultfd =++== Objective ==++Userfaults allow the implementation of on-demand paging from userland+and more generally they allow userland to take control various memory+page faults, something otherwise only the kernel code could do.++For example userfaults allows a proper and more optimal implementation+of the PROT_NONE+SIGSEGV trick.++== Design ==++Userfaults are delivered and resolved through the userfaultfd syscall.++The userfaultfd (aside from registering and unregistering virtual+memory ranges) provides two primary functionalities:++1) read/POLLIN protocol to notify a userland thread of the faults+ happening++2) various UFFDIO_* ioctls that can manage the virtual memory regions+ registered in the userfaultfd that allows userland to efficiently+ resolve the userfaults it receives via 1) or to manage the virtual+ memory in the background++The real advantage of userfaults if compared to regular virtual memory+management of mremap/mprotect is that the userfaults in all their+operations never involve heavyweight structures like vmas (in fact the+userfaultfd runtime load never takes the mmap_sem for writing).++Vmas are not suitable for page- (or hugepage) granular fault tracking+when dealing with virtual address spaces that could span+Terabytes. Too many vmas would be needed for that.++The userfaultfd once opened by invoking the syscall, can also be+passed using unix domain sockets to a manager process, so the same+manager process could handle the userfaults of a multitude of+different processes without them being aware about what is going on+(well of course unless they later try to use the userfaultfd+themselves on the same region the manager is already tracking, which+is a corner case that would currently return -EBUSY).++== API ==++When first opened the userfaultfd must be enabled invoking the+UFFDIO_API ioctl specifying a uffdio_api.api value set to UFFD_API (or+a later API version) which will specify the read/POLLIN protocol+userland intends to speak on the UFFD. The UFFDIO_API ioctl if+successful (i.e. if the requested uffdio_api.api is spoken also by the+running kernel), will return into uffdio_api.features and+uffdio_api.ioctls two 64bit bitmasks of respectively the activated+feature of the read(2) protocol and the generic ioctl available.++Once the userfaultfd has been enabled the UFFDIO_REGISTER ioctl should+be invoked (if present in the returned uffdio_api.ioctls bitmask) to+register a memory range in the userfaultfd by setting the+uffdio_register structure accordingly. The uffdio_register.mode+bitmask will specify to the kernel which kind of faults to track for+the range (UFFDIO_REGISTER_MODE_MISSING would track missing+pages). The UFFDIO_REGISTER ioctl will return the+uffdio_register.ioctls bitmask of ioctls that are suitable to resolve+userfaults on the range registered. Not all ioctls will necessarily be+supported for all memory types depending on the underlying virtual+memory backend (anonymous memory vs tmpfs vs real filebacked+mappings).++Userland can use the uffdio_register.ioctls to manage the virtual+address space in the background (to add or potentially also remove+memory from the userfaultfd registered range). This means a userfault+could be triggering just before userland maps in the background the+user-faulted page.++The primary ioctl to resolve userfaults is UFFDIO_COPY. That+atomically copies a page into the userfault registered range and wakes+up the blocked userfaults (unless uffdio_copy.mode &+UFFDIO_COPY_MODE_DONTWAKE is set). Other ioctl works similarly to+UFFDIO_COPY.++== QEMU/KVM ==++QEMU/KVM is using the userfaultfd syscall to implement postcopy live+migration. Postcopy live migration is one form of memory+externalization consisting of a virtual machine running with part or+all of its memory residing on a different node in the cloud. The+userfaultfd abstraction is generic enough that not a single line of+KVM kernel code had to be modified in order to add postcopy live+migration to QEMU.++Guest async page faults, FOLL_NOWAIT and all other GUP features work+just fine in combination with userfaults. Userfaults trigger async+page faults in the guest scheduler so those guest processes that+aren't waiting for userfaults (i.e. network bound) can keep running in+the guest vcpus.++It is generally beneficial to run one pass of precopy live migration+just before starting postcopy live migration, in order to avoid+generating userfaults for readonly guest regions.++The implementation of postcopy live migration currently uses one+single bidirectional socket but in the future two different sockets+will be used (to reduce the latency of the userfaults to the minimum+possible without having to decrease /proc/sys/net/ipv4/tcp_wmem).++The QEMU in the source node writes all pages that it knows are missing+in the destination node, into the socket, and the migration thread of+the QEMU running in the destination node runs UFFDIO_COPY|ZEROPAGE+ioctls on the userfaultfd in order to map the received pages into the+guest (UFFDIO_ZEROCOPY is used if the source page was a zero page).++A different postcopy thread in the destination node listens with+poll() to the userfaultfd in parallel. When a POLLIN event is+generated after a userfault triggers, the postcopy thread read() from+the userfaultfd and receives the fault address (or -EAGAIN in case the+userfault was already resolved and waken by a UFFDIO_COPY|ZEROPAGE run+by the parallel QEMU migration thread).++After the QEMU postcopy thread (running in the destination node) gets+the userfault address it writes the information about the missing page+into the socket. The QEMU source node receives the information and+roughly "seeks" to that page address and continues sending all+remaining missing pages from that new page offset. Soon after that+(just the time to flush the tcp_wmem queue through the network) the+migration thread in the QEMU running in the destination node will+receive the page that triggered the userfault and it'll map it as+usual with the UFFDIO_COPY|ZEROPAGE (without actually knowing if it+was spontaneously sent by the source or if it was an urgent page+requested through an userfault).++By the time the userfaults start, the QEMU in the destination node+doesn't need to keep any per-page state bitmap relative to the live+migration around and a single per-page bitmap has to be maintained in+the QEMU running in the source node to know which pages are still+missing in the destination node. The bitmap in the source node is+checked to find which missing pages to send in round robin and we seek+over it when receiving incoming userfaults. After sending each page of+course the bitmap is updated accordingly. It's also useful to avoid+sending the same page twice (in case the userfault is read by the+postcopy thread just before UFFDIO_COPY|ZEROPAGE runs in the migration+thread).--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Michael Kerrisk
Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/
Linux/UNIX System Programming Training: http://man7.org/training/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrea Arcangeli <hidden> Date: 2015-12-04 17:55:10
Hello Michael,
On Fri, Dec 04, 2015 at 04:50:03PM +0100, Michael Kerrisk (man-pages) wrote:
Hi Andrea,
On 09/11/2015 10:47 AM, Michael Kerrisk (man-pages) wrote:
quoted
On 05/14/2015 07:30 PM, Andrea Arcangeli wrote:
quoted
Add documentation.
Hi Andrea,
I do not recall... Did you write a man page also for this new system call?
No response to my last mail, so I'll try again... Did you
write any man page for this interface?
I wished I would answer with the manpage itself to give a more
satisfactory answer, but answer is still no at this time. Right now
there's the write protection tracking feature posted to linux-mm and
I'm currently reviewing that. It's worth documenting that part too in
the manpage as it's going to happen sooner than later.
Lack of manpage so far didn't prevent userland to use it (qemu
postcopy is already in upstream qemu and it depends on userfaultfd),
nor review of the code nor other kernel contributors to extend the
syscall API. Other users started testing the syscall too. This is just
to explain why unfortunately the manpage didn't get the top priority
yet, but nevertheless the manpage should happen too and it's
important. Advice on how to proceed is welcome.
Thanks,
Andrea