History

userfaultfd: add minor fault registration mode

Patch series "userfaultfd: add minor fault handling", v9. Overview ======== This series adds a new userfaultfd feature, UFFD_FEATURE_MINOR_HUGETLBFS. When enabled (via the UFFDIO_API ioctl), this feature means that any hugetlbfs VMAs registered with UFFDIO_REGISTER_MODE_MISSING will *also* get events for "minor" faults. By "minor" fault, I mean the following situation: Let there exist two mappings (i.e., VMAs) to the same page(s) (shared memory). One of the mappings is registered with userfaultfd (in minor mode), and the other is not. Via the non-UFFD mapping, the underlying pages have already been allocated & filled with some contents. The UFFD mapping has not yet been faulted in; when it is touched for the first time, this results in what I'm calling a "minor" fault. As a concrete example, when working with hugetlbfs, we have huge_pte_none(), but find_lock_page() finds an existing page. We also add a new ioctl to resolve such faults: UFFDIO_CONTINUE. The idea is, userspace resolves the fault by either a) doing nothing if the contents are already correct, or b) updating the underlying contents using the second, non-UFFD mapping (via memcpy/memset or similar, or something fancier like RDMA, or etc...). In either case, userspace issues UFFDIO_CONTINUE to tell the kernel "I have ensured the page contents are correct, carry on setting up the mapping". Use Case ======== Consider the use case of VM live migration (e.g. under QEMU/KVM): 1. While a VM is still running, we copy the contents of its memory to a target machine. The pages are populated on the target by writing to the non-UFFD mapping, using the setup described above. The VM is still running (and therefore its memory is likely changing), so this may be repeated several times, until we decide the target is "up to date enough". 2. We pause the VM on the source, and start executing on the target machine. During this gap, the VM's user(s) will *see* a pause, so it is desirable to minimize this window. 3. Between the last time any page was copied from the source to the target, and when the VM was paused, the contents of that page may have changed - and therefore the copy we have on the target machine is out of date. Although we can keep track of which pages are out of date, for VMs with large amounts of memory, it is "slow" to transfer this information to the target machine. We want to resume execution before such a transfer would complete. 4. So, the guest begins executing on the target machine. The first time it touches its memory (via the UFFD-registered mapping), userspace wants to intercept this fault. Userspace checks whether or not the page is up to date, and if not, copies the updated page from the source machine, via the non-UFFD mapping. Finally, whether a copy was performed or not, userspace issues a UFFDIO_CONTINUE ioctl to tell the kernel "I have ensured the page contents are correct, carry on setting up the mapping". We don't have to do all of the final updates on-demand. The userfaultfd manager can, in the background, also copy over updated pages once it receives the map of which pages are up-to-date or not. Interaction with Existing APIs ============================== Because this is a feature, a registered VMA could potentially receive both missing and minor faults. I spent some time thinking through how the existing API interacts with the new feature: UFFDIO_CONTINUE cannot be used to resolve non-minor faults, as it does not allocate a new page. If UFFDIO_CONTINUE is used on a non-minor fault: - For non-shared memory or shmem, -EINVAL is returned. - For hugetlb, -EFAULT is returned. UFFDIO_COPY and UFFDIO_ZEROPAGE cannot be used to resolve minor faults. Without modifications, the existing codepath assumes a new page needs to be allocated. This is okay, since userspace must have a second non-UFFD-registered mapping anyway, thus there isn't much reason to want to use these in any case (just memcpy or memset or similar). - If UFFDIO_COPY is used on a minor fault, -EEXIST is returned. - If UFFDIO_ZEROPAGE is used on a minor fault, -EEXIST is returned (or -EINVAL in the case of hugetlb, as UFFDIO_ZEROPAGE is unsupported in any case). - UFFDIO_WRITEPROTECT simply doesn't work with shared memory, and returns -ENOENT in that case (regardless of the kind of fault). Future Work =========== This series only supports hugetlbfs. I have a second series in flight to support shmem as well, extending the functionality. This series is more mature than the shmem support at this point, and the functionality works fully on hugetlbfs, so this series can be merged first and then shmem support will follow. This patch (of 6): This feature allows userspace to intercept "minor" faults. By "minor" faults, I mean the following situation: Let there exist two mappings (i.e., VMAs) to the same page(s). One of the mappings is registered with userfaultfd (in minor mode), and the other is not. Via the non-UFFD mapping, the underlying pages have already been allocated & filled with some contents. The UFFD mapping has not yet been faulted in; when it is touched for the first time, this results in what I'm calling a "minor" fault. As a concrete example, when working with hugetlbfs, we have huge_pte_none(), but find_lock_page() finds an existing page. This commit adds the new registration mode, and sets the relevant flag on the VMAs being registered. In the hugetlb fault path, if we find that we have huge_pte_none(), but find_lock_page() does indeed find an existing page, then we have a "minor" fault, and if the VMA has the userfaultfd registration flag, we call into userfaultfd to handle it. This is implemented as a new registration mode, instead of an API feature. This is because the alternative implementation has significant drawbacks [1]. However, doing it this was requires we allocate a VM_* flag for the new registration mode. On 32-bit systems, there are no unused bits, so this feature is only supported on architectures with CONFIG_ARCH_USES_HIGH_VMA_FLAGS. When attempting to register a VMA in MINOR mode on 32-bit architectures, we return -EINVAL. [1] https://lore.kernel.org/patchwork/patch/1380226/ [peterx@redhat.com: fix minor fault page leak] Link: https://lkml.kernel.org/r/20210322175132.36659-1-peterx@redhat.com

Axel Rasmussen committed 5 years ago

Commit 7677f7f1 parent eb14d4e10 files · +150 −62Browse files

Unified · Split

10 files
  1. arch/arm64/Kconfig +1 −0
  2. arch/x86/Kconfig +1 −0
  3. fs/proc/task_mmu.c +3 −0
  4. fs/userfaultfd.c +47 −31
  5. include/linux/mm.h +7 −0
  6. include/linux/userfaultfd_k.h +14 −1
  7. include/trace/events/mmflags.h +7 −0
  8. include/uapi/linux/userfaultfd.h +13 −2
  9. init/Kconfig +5 −0
  10. mm/hugetlb.c +52 −28
arch/arm64/Kconfig+1−0
@@ -213,6 +213,7 @@ config ARM64 · 212 unmodified lines
213 select SWIOTLB213 select SWIOTLB
214 select SYSCTL_EXCEPTION_TRACE214 select SYSCTL_EXCEPTION_TRACE
215 select THREAD_INFO_IN_TASK215 select THREAD_INFO_IN_TASK
216 select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD
216 help217 help
217 ARM 64-bit (AArch64) Linux support.218 ARM 64-bit (AArch64) Linux support.
218 219
arch/x86/Kconfig+1−0
@@ -165,6 +165,7 @@ config X86 · 164 unmodified lines
165 select HAVE_ARCH_TRANSPARENT_HUGEPAGE165 select HAVE_ARCH_TRANSPARENT_HUGEPAGE
166 select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64166 select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64
167 select HAVE_ARCH_USERFAULTFD_WP if X86_64 && USERFAULTFD167 select HAVE_ARCH_USERFAULTFD_WP if X86_64 && USERFAULTFD
168 select HAVE_ARCH_USERFAULTFD_MINOR if X86_64 && USERFAULTFD
168 select HAVE_ARCH_VMAP_STACK if X86_64169 select HAVE_ARCH_VMAP_STACK if X86_64
169 select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET170 select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
170 select HAVE_ARCH_WITHIN_STACK_FRAMES171 select HAVE_ARCH_WITHIN_STACK_FRAMES
fs/proc/task_mmu.c+3−0
@@ -661,6 +661,9 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma) · 660 unmodified lines
661 [ilog2(VM_PKEY_BIT4)] = "",661 [ilog2(VM_PKEY_BIT4)] = "",
662#endif662#endif
663#endif /* CONFIG_ARCH_HAS_PKEYS */663#endif /* CONFIG_ARCH_HAS_PKEYS */
664#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
665 [ilog2(VM_UFFD_MINOR)] = "ui",
666#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
664 };667 };
665 size_t i;668 size_t i;
666 669
fs/userfaultfd.c+47−31
@@ -197,24 +197,21 @@ static inline struct uffd_msg userfault_msg(unsigned long address, · 196 unmodified lines
197 msg_init(&msg);197 msg_init(&msg);
198 msg.event = UFFD_EVENT_PAGEFAULT;198 msg.event = UFFD_EVENT_PAGEFAULT;
199 msg.arg.pagefault.address = address;199 msg.arg.pagefault.address = address;
200 /*
201 * These flags indicate why the userfault occurred:
202 * - UFFD_PAGEFAULT_FLAG_WP indicates a write protect fault.
203 * - UFFD_PAGEFAULT_FLAG_MINOR indicates a minor fault.
204 * - Neither of these flags being set indicates a MISSING fault.
205 *
206 * Separately, UFFD_PAGEFAULT_FLAG_WRITE indicates it was a write
207 * fault. Otherwise, it was a read fault.
208 */
200 if (flags & FAULT_FLAG_WRITE)209 if (flags & FAULT_FLAG_WRITE)
201 /*
202 * If UFFD_FEATURE_PAGEFAULT_FLAG_WP was set in the
203 * uffdio_api.features and UFFD_PAGEFAULT_FLAG_WRITE
204 * was not set in a UFFD_EVENT_PAGEFAULT, it means it
205 * was a read fault, otherwise if set it means it's
206 * a write fault.
207 */
208 msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WRITE;210 msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WRITE;
209 if (reason & VM_UFFD_WP)211 if (reason & VM_UFFD_WP)
210 /*
211 * If UFFD_FEATURE_PAGEFAULT_FLAG_WP was set in the
212 * uffdio_api.features and UFFD_PAGEFAULT_FLAG_WP was
213 * not set in a UFFD_EVENT_PAGEFAULT, it means it was
214 * a missing fault, otherwise if set it means it's a
215 * write protect fault.
216 */
217 msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WP;212 msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_WP;
213 if (reason & VM_UFFD_MINOR)
214 msg.arg.pagefault.flags |= UFFD_PAGEFAULT_FLAG_MINOR;
218 if (features & UFFD_FEATURE_THREAD_ID)215 if (features & UFFD_FEATURE_THREAD_ID)
219 msg.arg.pagefault.feat.ptid = task_pid_vnr(current);216 msg.arg.pagefault.feat.ptid = task_pid_vnr(current);
220 return msg;217 return msg;
@@ -401,8 +398,10 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason) · 180 unmodified lines
401 398
402 BUG_ON(ctx->mm != mm);399 BUG_ON(ctx->mm != mm);
403 400
404 VM_BUG_ON(reason & ~(VM_UFFD_MISSING|VM_UFFD_WP));401 /* Any unrecognized flag is a bug. */
405 VM_BUG_ON(!(reason & VM_UFFD_MISSING) ^ !!(reason & VM_UFFD_WP));402 VM_BUG_ON(reason & ~__VM_UFFD_FLAGS);
403 /* 0 or > 1 flags set is a bug; we expect exactly 1. */
404 VM_BUG_ON(!reason || (reason & (reason - 1)));
406 405
407 if (ctx->features & UFFD_FEATURE_SIGBUS)406 if (ctx->features & UFFD_FEATURE_SIGBUS)
408 goto out;407 goto out;
@@ -612,7 +611,7 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx, · 203 unmodified lines
612 for (vma = mm->mmap; vma; vma = vma->vm_next)611 for (vma = mm->mmap; vma; vma = vma->vm_next)
613 if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {612 if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {
614 vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;613 vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
615 vma->vm_flags &= ~(VM_UFFD_WP | VM_UFFD_MISSING);614 vma->vm_flags &= ~__VM_UFFD_FLAGS;
616 }615 }
617 mmap_write_unlock(mm);616 mmap_write_unlock(mm);
618 617
@@ -644,7 +643,7 @@ int dup_userfaultfd(struct vm_area_struct *vma, struct list_head *fcs) · 25 unmodified lines
644 octx = vma->vm_userfaultfd_ctx.ctx;643 octx = vma->vm_userfaultfd_ctx.ctx;
645 if (!octx || !(octx->features & UFFD_FEATURE_EVENT_FORK)) {644 if (!octx || !(octx->features & UFFD_FEATURE_EVENT_FORK)) {
646 vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;645 vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
647 vma->vm_flags &= ~(VM_UFFD_WP | VM_UFFD_MISSING);646 vma->vm_flags &= ~__VM_UFFD_FLAGS;
648 return 0;647 return 0;
649 }648 }
650 649
@@ -726,7 +725,7 @@ void mremap_userfaultfd_prep(struct vm_area_struct *vma, · 75 unmodified lines
726 } else {725 } else {
727 /* Drop uffd context if remap feature not enabled */726 /* Drop uffd context if remap feature not enabled */
728 vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;727 vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
729 vma->vm_flags &= ~(VM_UFFD_WP | VM_UFFD_MISSING);728 vma->vm_flags &= ~__VM_UFFD_FLAGS;
730 }729 }
731}730}
732 731
@@ -867,12 +866,12 @@ static int userfaultfd_release(struct inode *inode, struct file *file) · 134 unmodified lines
867 for (vma = mm->mmap; vma; vma = vma->vm_next) {866 for (vma = mm->mmap; vma; vma = vma->vm_next) {
868 cond_resched();867 cond_resched();
869 BUG_ON(!!vma->vm_userfaultfd_ctx.ctx ^868 BUG_ON(!!vma->vm_userfaultfd_ctx.ctx ^
870 !!(vma->vm_flags & (VM_UFFD_MISSING | VM_UFFD_WP)));869 !!(vma->vm_flags & __VM_UFFD_FLAGS));
871 if (vma->vm_userfaultfd_ctx.ctx != ctx) {870 if (vma->vm_userfaultfd_ctx.ctx != ctx) {
872 prev = vma;871 prev = vma;
873 continue;872 continue;
874 }873 }
875 new_flags = vma->vm_flags & ~(VM_UFFD_MISSING | VM_UFFD_WP);874 new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS;
876 prev = vma_merge(mm, prev, vma->vm_start, vma->vm_end,875 prev = vma_merge(mm, prev, vma->vm_start, vma->vm_end,
877 new_flags, vma->anon_vma,876 new_flags, vma->anon_vma,
878 vma->vm_file, vma->vm_pgoff,877 vma->vm_file, vma->vm_pgoff,
@@ -1262,9 +1261,19 @@ static inline bool vma_can_userfault(struct vm_area_struct *vma, · 383 unmodified lines
1262 unsigned long vm_flags)1261 unsigned long vm_flags)
1263{1262{
1264 /* FIXME: add WP support to hugetlbfs and shmem */1263 /* FIXME: add WP support to hugetlbfs and shmem */
1265 return vma_is_anonymous(vma) ||1264 if (vm_flags & VM_UFFD_WP) {
1266 ((is_vm_hugetlb_page(vma) || vma_is_shmem(vma)) &&1265 if (is_vm_hugetlb_page(vma) || vma_is_shmem(vma))
1267 !(vm_flags & VM_UFFD_WP));1266 return false;
1267 }
1268
1269 if (vm_flags & VM_UFFD_MINOR) {
1270 /* FIXME: Add minor fault interception for shmem. */
1271 if (!is_vm_hugetlb_page(vma))
1272 return false;
1273 }
1274
1275 return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) ||
1276 vma_is_shmem(vma);
1268}1277}
1269 1278
1270static int userfaultfd_register(struct userfaultfd_ctx *ctx,1279static int userfaultfd_register(struct userfaultfd_ctx *ctx,
@@ -1290,14 +1299,19 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, · 19 unmodified lines
1290 ret = -EINVAL;1299 ret = -EINVAL;
1291 if (!uffdio_register.mode)1300 if (!uffdio_register.mode)
1292 goto out;1301 goto out;
1293 if (uffdio_register.mode & ~(UFFDIO_REGISTER_MODE_MISSING|1302 if (uffdio_register.mode & ~UFFD_API_REGISTER_MODES)
1294 UFFDIO_REGISTER_MODE_WP))
1295 goto out;1303 goto out;
1296 vm_flags = 0;1304 vm_flags = 0;
1297 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING)1305 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING)
1298 vm_flags |= VM_UFFD_MISSING;1306 vm_flags |= VM_UFFD_MISSING;
1299 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP)1307 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP)
1300 vm_flags |= VM_UFFD_WP;1308 vm_flags |= VM_UFFD_WP;
1309 if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) {
1310#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
1311 goto out;
1312#endif
1313 vm_flags |= VM_UFFD_MINOR;
1314 }
1301 1315
1302 ret = validate_range(mm, &uffdio_register.range.start,1316 ret = validate_range(mm, &uffdio_register.range.start,
1303 uffdio_register.range.len);1317 uffdio_register.range.len);
@@ -1341,7 +1355,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, · 37 unmodified lines
1341 cond_resched();1355 cond_resched();
1342 1356
1343 BUG_ON(!!cur->vm_userfaultfd_ctx.ctx ^1357 BUG_ON(!!cur->vm_userfaultfd_ctx.ctx ^
1344 !!(cur->vm_flags & (VM_UFFD_MISSING | VM_UFFD_WP)));1358 !!(cur->vm_flags & __VM_UFFD_FLAGS));
1345 1359
1346 /* check not compatible vmas */1360 /* check not compatible vmas */
1347 ret = -EINVAL;1361 ret = -EINVAL;
@@ -1421,8 +1435,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, · 73 unmodified lines
1421 start = vma->vm_start;1435 start = vma->vm_start;
1422 vma_end = min(end, vma->vm_end);1436 vma_end = min(end, vma->vm_end);
1423 1437
1424 new_flags = (vma->vm_flags &1438 new_flags = (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags;
1425 ~(VM_UFFD_MISSING|VM_UFFD_WP)) | vm_flags;
1426 prev = vma_merge(mm, prev, start, vma_end, new_flags,1439 prev = vma_merge(mm, prev, start, vma_end, new_flags,
1427 vma->anon_vma, vma->vm_file, vma->vm_pgoff,1440 vma->anon_vma, vma->vm_file, vma->vm_pgoff,
1428 vma_policy(vma),1441 vma_policy(vma),
@@ -1544,7 +1557,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, · 115 unmodified lines
1544 cond_resched();1557 cond_resched();
1545 1558
1546 BUG_ON(!!cur->vm_userfaultfd_ctx.ctx ^1559 BUG_ON(!!cur->vm_userfaultfd_ctx.ctx ^
1547 !!(cur->vm_flags & (VM_UFFD_MISSING | VM_UFFD_WP)));1560 !!(cur->vm_flags & __VM_UFFD_FLAGS));
1548 1561
1549 /*1562 /*
1550 * Check not compatible vmas, not strictly required1563 * Check not compatible vmas, not strictly required
@@ -1595,7 +1608,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, · 44 unmodified lines
1595 wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range);1608 wake_userfault(vma->vm_userfaultfd_ctx.ctx, &range);
1596 }1609 }
1597 1610
1598 new_flags = vma->vm_flags & ~(VM_UFFD_MISSING | VM_UFFD_WP);1611 new_flags = vma->vm_flags & ~__VM_UFFD_FLAGS;
1599 prev = vma_merge(mm, prev, start, vma_end, new_flags,1612 prev = vma_merge(mm, prev, start, vma_end, new_flags,
1600 vma->anon_vma, vma->vm_file, vma->vm_pgoff,1613 vma->anon_vma, vma->vm_file, vma->vm_pgoff,
1601 vma_policy(vma),1614 vma_policy(vma),
@@ -1863,6 +1876,9 @@ static int userfaultfd_api(struct userfaultfd_ctx *ctx, · 261 unmodified lines
1863 goto err_out;1876 goto err_out;
1864 /* report all available features and ioctls to userland */1877 /* report all available features and ioctls to userland */
1865 uffdio_api.features = UFFD_API_FEATURES;1878 uffdio_api.features = UFFD_API_FEATURES;
1879#ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
1880 uffdio_api.features &= ~UFFD_FEATURE_MINOR_HUGETLBFS;
1881#endif
1866 uffdio_api.ioctls = UFFD_API_IOCTLS;1882 uffdio_api.ioctls = UFFD_API_IOCTLS;
1867 ret = -EFAULT;1883 ret = -EFAULT;
1868 if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api)))1884 if (copy_to_user(buf, &uffdio_api, sizeof(uffdio_api)))
include/linux/mm.h+7−0
@@ -372,6 +372,13 @@ extern unsigned int kobjsize(const void *objp); · 371 unmodified lines
372# define VM_GROWSUP VM_NONE372# define VM_GROWSUP VM_NONE
373#endif373#endif
374 374
375#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
376# define VM_UFFD_MINOR_BIT 37
377# define VM_UFFD_MINOR BIT(VM_UFFD_MINOR_BIT) /* UFFD minor faults */
378#else /* !CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
379# define VM_UFFD_MINOR VM_NONE
380#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
381
375/* Bits set in the VMA until the stack is in its final location */382/* Bits set in the VMA until the stack is in its final location */
376#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ)383#define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ)
377 384
include/linux/userfaultfd_k.h+14−1
@@ -17,6 +17,9 @@ · 16 unmodified lines
17#include <linux/mm.h>17#include <linux/mm.h>
18#include <asm-generic/pgtable_uffd.h>18#include <asm-generic/pgtable_uffd.h>
19 19
20/* The set of all possible UFFD-related VM flags. */
21#define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_WP | VM_UFFD_MINOR)
22
20/*23/*
21 * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining24 * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
22 * new flags, since they might collide with O_* ones. We want25 * new flags, since they might collide with O_* ones. We want
@@ -71,6 +74,11 @@ static inline bool userfaultfd_wp(struct vm_area_struct *vma) · 48 unmodified lines
71 return vma->vm_flags & VM_UFFD_WP;74 return vma->vm_flags & VM_UFFD_WP;
72}75}
73 76
77static inline bool userfaultfd_minor(struct vm_area_struct *vma)
78{
79 return vma->vm_flags & VM_UFFD_MINOR;
80}
81
74static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,82static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,
75 pte_t pte)83 pte_t pte)
76{84{
@@ -85,7 +93,7 @@ static inline bool userfaultfd_huge_pmd_wp(struct vm_area_struct *vma, · 8 unmodified lines
85 93
86static inline bool userfaultfd_armed(struct vm_area_struct *vma)94static inline bool userfaultfd_armed(struct vm_area_struct *vma)
87{95{
88 return vma->vm_flags & (VM_UFFD_MISSING | VM_UFFD_WP);96 return vma->vm_flags & __VM_UFFD_FLAGS;
89}97}
90 98
91extern int dup_userfaultfd(struct vm_area_struct *, struct list_head *);99extern int dup_userfaultfd(struct vm_area_struct *, struct list_head *);
@@ -132,6 +140,11 @@ static inline bool userfaultfd_wp(struct vm_area_struct *vma) · 40 unmodified lines
132 return false;140 return false;
133}141}
134 142
143static inline bool userfaultfd_minor(struct vm_area_struct *vma)
144{
145 return false;
146}
147
135static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,148static inline bool userfaultfd_pte_wp(struct vm_area_struct *vma,
136 pte_t pte)149 pte_t pte)
137{150{
include/trace/events/mmflags.h+7−0
@@ -137,6 +137,12 @@ IF_HAVE_PG_ARCH_2(PG_arch_2, "arch_2" ) · 136 unmodified lines
137#define IF_HAVE_VM_SOFTDIRTY(flag,name)137#define IF_HAVE_VM_SOFTDIRTY(flag,name)
138#endif138#endif
139 139
140#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
141# define IF_HAVE_UFFD_MINOR(flag, name) {flag, name},
142#else
143# define IF_HAVE_UFFD_MINOR(flag, name)
144#endif
145
140#define __def_vmaflag_names \146#define __def_vmaflag_names \
141 {VM_READ, "read" }, \147 {VM_READ, "read" }, \
142 {VM_WRITE, "write" }, \148 {VM_WRITE, "write" }, \
@@ -148,6 +154,7 @@ IF_HAVE_PG_ARCH_2(PG_arch_2, "arch_2" ) · 5 unmodified lines
148 {VM_MAYSHARE, "mayshare" }, \154 {VM_MAYSHARE, "mayshare" }, \
149 {VM_GROWSDOWN, "growsdown" }, \155 {VM_GROWSDOWN, "growsdown" }, \
150 {VM_UFFD_MISSING, "uffd_missing" }, \156 {VM_UFFD_MISSING, "uffd_missing" }, \
157IF_HAVE_UFFD_MINOR(VM_UFFD_MINOR, "uffd_minor" ) \
151 {VM_PFNMAP, "pfnmap" }, \158 {VM_PFNMAP, "pfnmap" }, \
152 {VM_DENYWRITE, "denywrite" }, \159 {VM_DENYWRITE, "denywrite" }, \
153 {VM_UFFD_WP, "uffd_wp" }, \160 {VM_UFFD_WP, "uffd_wp" }, \
include/uapi/linux/userfaultfd.h+13−2
@@ -19,15 +19,19 @@ · 18 unmodified lines
19 * means the userland is reading).19 * means the userland is reading).
20 */20 */
21#define UFFD_API ((__u64)0xAA)21#define UFFD_API ((__u64)0xAA)
22#define UFFD_API_REGISTER_MODES (UFFDIO_REGISTER_MODE_MISSING | \
23 UFFDIO_REGISTER_MODE_WP | \
24 UFFDIO_REGISTER_MODE_MINOR)
22#define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \25#define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \
23 UFFD_FEATURE_EVENT_FORK | \26 UFFD_FEATURE_EVENT_FORK | \
24 UFFD_FEATURE_EVENT_REMAP | \27 UFFD_FEATURE_EVENT_REMAP | \
25 UFFD_FEATURE_EVENT_REMOVE | \28 UFFD_FEATURE_EVENT_REMOVE | \
26 UFFD_FEATURE_EVENT_UNMAP | \29 UFFD_FEATURE_EVENT_UNMAP | \
27 UFFD_FEATURE_MISSING_HUGETLBFS | \30 UFFD_FEATURE_MISSING_HUGETLBFS | \
28 UFFD_FEATURE_MISSING_SHMEM | \31 UFFD_FEATURE_MISSING_SHMEM | \
29 UFFD_FEATURE_SIGBUS | \32 UFFD_FEATURE_SIGBUS | \
30 UFFD_FEATURE_THREAD_ID)33 UFFD_FEATURE_THREAD_ID | \
34 UFFD_FEATURE_MINOR_HUGETLBFS)
31#define UFFD_API_IOCTLS \35#define UFFD_API_IOCTLS \
32 ((__u64)1 << _UFFDIO_REGISTER | \36 ((__u64)1 << _UFFDIO_REGISTER | \
33 (__u64)1 << _UFFDIO_UNREGISTER | \37 (__u64)1 << _UFFDIO_UNREGISTER | \
@@ -127,6 +131,7 @@ struct uffd_msg { · 93 unmodified lines
127/* flags for UFFD_EVENT_PAGEFAULT */131/* flags for UFFD_EVENT_PAGEFAULT */
128#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */132#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */
129#define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */133#define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */
134#define UFFD_PAGEFAULT_FLAG_MINOR (1<<2) /* If reason is VM_UFFD_MINOR */
130 135
131struct uffdio_api {136struct uffdio_api {
132 /* userland asks for an API number and the features to enable */137 /* userland asks for an API number and the features to enable */
@@ -171,6 +176,10 @@ struct uffdio_api { · 38 unmodified lines
171 *176 *
172 * UFFD_FEATURE_THREAD_ID pid of the page faulted task_struct will177 * UFFD_FEATURE_THREAD_ID pid of the page faulted task_struct will
173 * be returned, if feature is not requested 0 will be returned.178 * be returned, if feature is not requested 0 will be returned.
179 *
180 * UFFD_FEATURE_MINOR_HUGETLBFS indicates that minor faults
181 * can be intercepted (via REGISTER_MODE_MINOR) for
182 * hugetlbfs-backed pages.
174 */183 */
175#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0)184#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0)
176#define UFFD_FEATURE_EVENT_FORK (1<<1)185#define UFFD_FEATURE_EVENT_FORK (1<<1)
@@ -181,6 +190,7 @@ struct uffdio_api { · 4 unmodified lines
181#define UFFD_FEATURE_EVENT_UNMAP (1<<6)190#define UFFD_FEATURE_EVENT_UNMAP (1<<6)
182#define UFFD_FEATURE_SIGBUS (1<<7)191#define UFFD_FEATURE_SIGBUS (1<<7)
183#define UFFD_FEATURE_THREAD_ID (1<<8)192#define UFFD_FEATURE_THREAD_ID (1<<8)
193#define UFFD_FEATURE_MINOR_HUGETLBFS (1<<9)
184 __u64 features;194 __u64 features;
185 195
186 __u64 ioctls;196 __u64 ioctls;
@@ -195,6 +205,7 @@ struct uffdio_register { · 8 unmodified lines
195 struct uffdio_range range;205 struct uffdio_range range;
196#define UFFDIO_REGISTER_MODE_MISSING ((__u64)1<<0)206#define UFFDIO_REGISTER_MODE_MISSING ((__u64)1<<0)
197#define UFFDIO_REGISTER_MODE_WP ((__u64)1<<1)207#define UFFDIO_REGISTER_MODE_WP ((__u64)1<<1)
208#define UFFDIO_REGISTER_MODE_MINOR ((__u64)1<<2)
198 __u64 mode;209 __u64 mode;
199 210
200 /*211 /*
init/Kconfig+5−0
@@ -1644,6 +1644,11 @@ config HAVE_ARCH_USERFAULTFD_WP · 1643 unmodified lines
1644 help1644 help
1645 Arch has userfaultfd write protection support1645 Arch has userfaultfd write protection support
1646 1646
1647config HAVE_ARCH_USERFAULTFD_MINOR
1648 bool
1649 help
1650 Arch has userfaultfd minor fault support
1651
1647config MEMBARRIER1652config MEMBARRIER
1648 bool "Enable membarrier() system call" if EXPERT1653 bool "Enable membarrier() system call" if EXPERT
1649 default y1654 default y
mm/hugetlb.c+52−28
@@ -4469,6 +4469,44 @@ int huge_add_to_page_cache(struct page *page, struct address_space *mapping, · 4468 unmodified lines
4469 return 0;4469 return 0;
4470}4470}
4471 4471
4472static inline vm_fault_t hugetlb_handle_userfault(struct vm_area_struct *vma,
4473 struct address_space *mapping,
4474 pgoff_t idx,
4475 unsigned int flags,
4476 unsigned long haddr,
4477 unsigned long reason)
4478{
4479 vm_fault_t ret;
4480 u32 hash;
4481 struct vm_fault vmf = {
4482 .vma = vma,
4483 .address = haddr,
4484 .flags = flags,
4485
4486 /*
4487 * Hard to debug if it ends up being
4488 * used by a callee that assumes
4489 * something about the other
4490 * uninitialized fields... same as in
4491 * memory.c
4492 */
4493 };
4494
4495 /*
4496 * hugetlb_fault_mutex and i_mmap_rwsem must be
4497 * dropped before handling userfault. Reacquire
4498 * after handling fault to make calling code simpler.
4499 */
4500 hash = hugetlb_fault_mutex_hash(mapping, idx);
4501 mutex_unlock(&hugetlb_fault_mutex_table[hash]);
4502 i_mmap_unlock_read(mapping);
4503 ret = handle_userfault(&vmf, reason);
4504 i_mmap_lock_read(mapping);
4505 mutex_lock(&hugetlb_fault_mutex_table[hash]);
4506
4507 return ret;
4508}
4509
4472static vm_fault_t hugetlb_no_page(struct mm_struct *mm,4510static vm_fault_t hugetlb_no_page(struct mm_struct *mm,
4473 struct vm_area_struct *vma,4511 struct vm_area_struct *vma,
4474 struct address_space *mapping, pgoff_t idx,4512 struct address_space *mapping, pgoff_t idx,
@@ -4507,35 +4545,11 @@ static vm_fault_t hugetlb_no_page(struct mm_struct *mm, · 32 unmodified lines
4507retry:4545retry:
4508 page = find_lock_page(mapping, idx);4546 page = find_lock_page(mapping, idx);
4509 if (!page) {4547 if (!page) {
4510 /*4548 /* Check for page in userfault range */
4511 * Check for page in userfault range
4512 */
4513 if (userfaultfd_missing(vma)) {4549 if (userfaultfd_missing(vma)) {
4514 u32 hash;4550 ret = hugetlb_handle_userfault(vma, mapping, idx,
4515 struct vm_fault vmf = {4551 flags, haddr,
4516 .vma = vma,4552 VM_UFFD_MISSING);
4517 .address = haddr,
4518 .flags = flags,
4519 /*
4520 * Hard to debug if it ends up being
4521 * used by a callee that assumes
4522 * something about the other
4523 * uninitialized fields... same as in
4524 * memory.c
4525 */
4526 };
4527
4528 /*
4529 * hugetlb_fault_mutex and i_mmap_rwsem must be
4530 * dropped before handling userfault. Reacquire
4531 * after handling fault to make calling code simpler.
4532 */
4533 hash = hugetlb_fault_mutex_hash(mapping, idx);
4534 mutex_unlock(&hugetlb_fault_mutex_table[hash]);
4535 i_mmap_unlock_read(mapping);
4536 ret = handle_userfault(&vmf, VM_UFFD_MISSING);
4537 i_mmap_lock_read(mapping);
4538 mutex_lock(&hugetlb_fault_mutex_table[hash]);
4539 goto out;4553 goto out;
4540 }4554 }
4541 4555
@@ -4591,6 +4605,16 @@ retry: · 49 unmodified lines
4591 VM_FAULT_SET_HINDEX(hstate_index(h));4605 VM_FAULT_SET_HINDEX(hstate_index(h));
4592 goto backout_unlocked;4606 goto backout_unlocked;
4593 }4607 }
4608
4609 /* Check for page in userfault range. */
4610 if (userfaultfd_minor(vma)) {
4611 unlock_page(page);
4612 put_page(page);
4613 ret = hugetlb_handle_userfault(vma, mapping, idx,
4614 flags, haddr,
4615 VM_UFFD_MINOR);
4616 goto out;
4617 }
4594 }4618 }
4595 4619
4596 /*4620 /*