From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3FFE7349CE7 for ; Wed, 15 Jul 2026 21:18:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784150301; cv=none; b=OD+IOnqWFPZcpIcFmCixcqLFdih++8MGJcUYvaAlSBUrTNnPW2Dp0p89qzR6eJ65c2xVd4A12vZVVesRw1qIiL7J/lWXQ/vz/eKXb4TdeGJGdG3q5GVOB2IQ6l8/Qzgsep0/ylVYMVV7gKhblrWDP8iI2jX1B8MNHHcegPM3Cg0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784150301; c=relaxed/simple; bh=Pkr6N6PJWbJ4xovBWtNnMNpKZNeFGGV5bVo5SBnWin0=; h=Date:To:From:Subject:Message-Id; b=oXOLKl49WF4Q1U0PTqI2TgQzLq2NQ1XWwGB99EEZoxhua+WDe+o5Oh+LHN7gu1Hq5mGChlP/xQNbrA3dKqfAViiMRuKGENJE7W6MymBjYORYHkSXqgvW8svPZgwqpcW9X4Izo/n5hqA32dE8d6nl4JW0dI+YNlCWcXG9PitEStk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux-foundation.org header.i=@linux-foundation.org header.b=vuSiRcO8; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux-foundation.org header.i=@linux-foundation.org header.b="vuSiRcO8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C6F9B1F000E9; Wed, 15 Jul 2026 21:18:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux-foundation.org; s=korg; t=1784150299; bh=OJgBhjXMH1niYTwguDoCqYP9l/9Kqkt6venL97gsH7Y=; h=Date:To:From:Subject; b=vuSiRcO8+rqyFNU5edqMwVsX3/uasdHUrZxDEEypIoiUom9kub9WbncRqAUSxsTB0 n5fIgfRjHtwCuyrrbxyUxhyHbcvTd7t4YgakzSnhrPGwpxmxHzLFQJzQZTFt3uxuNa vk9pmP+cqvoqXcj+VOX1B3BfZs+PdiQst73JG86o= Date: Wed, 15 Jul 2026 14:18:19 -0700 To: mm-commits@vger.kernel.org,skinsburskii@gmail.com,akpm@linux-foundation.org From: Andrew Morton Subject: + selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults.patch added to mm-unstable branch Message-Id: <20260715211819.C6F9B1F000E9@smtp.kernel.org> Precedence: bulk X-Mailing-List: mm-commits@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: The patch titled Subject: selftests/mm: add HMM test for mmap lock-dropping faults has been added to the -mm mm-unstable branch. Its filename is selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults.patch This patch will shortly appear at https://git.kernel.org/pub/scm/linux/kernel/git/akpm/25-new.git/tree/patches/selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults.patch This patch will later appear in the mm-unstable branch at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Before you just go and hit "reply", please: a) Consider who else should be cc'ed b) Prefer to cc a suitable mailing list as well c) Ideally: find the original patch on the mailing list and do a reply-to-all to that, adding suitable additional cc's *** Remember to use Documentation/process/submit-checklist.rst when testing your code *** The -mm tree is included into linux-next via various branches at git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm and is updated there most days ------------------------------------------------------ From: Stanislav Kinsburskii Subject: selftests/mm: add HMM test for mmap lock-dropping faults Date: Wed, 15 Jul 2026 11:16:13 -0700 Add test_hmm coverage for the HMM lock-dropping fault path. The test module gets a new HMM_DMIRROR_READ_UNLOCKED ioctl that calls hmm_range_fault_unlocked_timeout() with a timeout of 0, exercising the unbounded retry mode while allowing the mmap lock to be dropped during fault handling. Add a userfaultfd_read selftest that registers an anonymous mapping with UFFDIO_REGISTER_MODE_MISSING, services the faults from a handler thread with UFFDIO_COPY, and verifies that HMM can read back the data supplied by the handler. This exercises the path where handle_mm_fault() drops mmap_lock and hmm_range_fault_unlocked_timeout() restarts the walk internally. Assisted-by: GitHub-Copilot:claude-opus-4.6 Link: https://lore.kernel.org/178413937354.1155966.16649896548407894107.stgit@skinsburskii Signed-off-by: Stanislav Kinsburskii Cc: Danilo Krummrich Cc: Dave Airlie Cc: David Hildenbrand Cc: Dexuan Cui Cc: Haiyang Zhang Cc: Jason Gunthorpe Cc: Jonathan Corbet Cc: Kees Cook Cc: K. Y. Srinivasan Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lizhi Hou Cc: Long Li Cc: Lorenzo Stoakes Cc: Lyude Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Michal Hocko Cc: Mike Rapoport Cc: Oded Gabbay Cc: Oleg Nesterov Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Thomas Zimemrmann Cc: Vlastimil Babka Cc: Wei Liu Signed-off-by: Andrew Morton --- lib/test_hmm.c | 107 ++++++++++++++++ lib/test_hmm_uapi.h | 1 tools/testing/selftests/mm/hmm-tests.c | 150 +++++++++++++++++++++++ 3 files changed, 257 insertions(+), 1 deletion(-) --- a/lib/test_hmm.c~selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults +++ a/lib/test_hmm.c @@ -389,6 +389,67 @@ out: return ret; } +static int dmirror_range_fault_unlocked(struct dmirror *dmirror, + struct hmm_range *range, + unsigned long timeout) +{ + int ret; + + while (true) { + ret = hmm_range_fault_unlocked_timeout(range, timeout); + if (ret) + goto out; + + mutex_lock(&dmirror->mutex); + if (mmu_interval_read_retry(range->notifier, + range->notifier_seq)) { + mutex_unlock(&dmirror->mutex); + continue; + } + break; + } + + ret = dmirror_do_fault(dmirror, range); + + mutex_unlock(&dmirror->mutex); +out: + return ret; +} + +static int dmirror_fault_unlocked(struct dmirror *dmirror, + unsigned long start, + unsigned long end, bool write, + unsigned long timeout) +{ + struct mm_struct *mm = dmirror->notifier.mm; + unsigned long addr; + unsigned long pfns[32]; + struct hmm_range range = { + .notifier = &dmirror->notifier, + .hmm_pfns = pfns, + .pfn_flags_mask = 0, + .default_flags = + HMM_PFN_REQ_FAULT | (write ? HMM_PFN_REQ_WRITE : 0), + .dev_private_owner = dmirror->mdevice, + }; + int ret = 0; + + if (!mmget_not_zero(mm)) + return -EFAULT; + + for (addr = start; addr < end; addr = range.end) { + range.start = addr; + range.end = min(addr + (ARRAY_SIZE(pfns) << PAGE_SHIFT), end); + + ret = dmirror_range_fault_unlocked(dmirror, &range, timeout); + if (ret) + break; + } + + mmput(mm); + return ret; +} + static int dmirror_fault(struct dmirror *dmirror, unsigned long start, unsigned long end, bool write) { @@ -488,6 +549,48 @@ static int dmirror_read(struct dmirror * return ret; } +static int dmirror_read_unlocked(struct dmirror *dmirror, + struct hmm_dmirror_cmd *cmd, + unsigned long timeout) +{ + struct dmirror_bounce bounce; + unsigned long start, end; + unsigned long size = cmd->npages << PAGE_SHIFT; + int ret; + + start = cmd->addr; + end = start + size; + if (end < start) + return -EINVAL; + + ret = dmirror_bounce_init(&bounce, start, size); + if (ret) + return ret; + + while (1) { + mutex_lock(&dmirror->mutex); + ret = dmirror_do_read(dmirror, start, end, &bounce); + mutex_unlock(&dmirror->mutex); + if (ret != -ENOENT) + break; + + start = cmd->addr + (bounce.cpages << PAGE_SHIFT); + ret = dmirror_fault_unlocked(dmirror, start, end, false, timeout); + if (ret) + break; + cmd->faults++; + } + + if (ret == 0) { + if (copy_to_user(u64_to_user_ptr(cmd->ptr), bounce.ptr, + bounce.size)) + ret = -EFAULT; + } + cmd->cpages = bounce.cpages; + dmirror_bounce_fini(&bounce); + return ret; +} + static int dmirror_do_write(struct dmirror *dmirror, unsigned long start, unsigned long end, struct dmirror_bounce *bounce) { @@ -1572,7 +1675,9 @@ static long dmirror_fops_unlocked_ioctl( dmirror->flags = cmd.npages; ret = 0; break; - + case HMM_DMIRROR_READ_UNLOCKED: + ret = dmirror_read_unlocked(dmirror, &cmd, 0); + break; default: return -EINVAL; } --- a/lib/test_hmm_uapi.h~selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults +++ a/lib/test_hmm_uapi.h @@ -38,6 +38,7 @@ struct hmm_dmirror_cmd { #define HMM_DMIRROR_CHECK_EXCLUSIVE _IOWR('H', 0x06, struct hmm_dmirror_cmd) #define HMM_DMIRROR_RELEASE _IOWR('H', 0x07, struct hmm_dmirror_cmd) #define HMM_DMIRROR_FLAGS _IOWR('H', 0x08, struct hmm_dmirror_cmd) +#define HMM_DMIRROR_READ_UNLOCKED _IOWR('H', 0x09, struct hmm_dmirror_cmd) #define HMM_DMIRROR_FLAG_FAIL_ALLOC (1ULL << 0) --- a/tools/testing/selftests/mm/hmm-tests.c~selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults +++ a/tools/testing/selftests/mm/hmm-tests.c @@ -29,6 +29,10 @@ #include #include #include +#include +#include +#include +#include /* * This is a private UAPI to the kernel test module so it isn't exported @@ -2952,4 +2956,150 @@ TEST_F_TIMEOUT(hmm, benchmark_thp_migrat &thp_results, ®ular_results); } } +/* + * Test that HMM can fault in pages backed by userfaultfd using the + * hmm_range_fault_unlocked_timeout() path with no timeout. This exercises + * the lock-drop retry logic in the HMM framework. + */ +struct uffd_thread_args { + int uffd; + int stop_fd; + void *page_buffer; + unsigned long page_size; +}; + +static void *uffd_handler_thread(void *arg) +{ + struct uffd_thread_args *args = arg; + struct uffd_msg msg; + struct uffdio_copy copy; + struct pollfd pollfd[2]; + int ret; + + pollfd[0].fd = args->uffd; + pollfd[0].events = POLLIN; + pollfd[1].fd = args->stop_fd; + pollfd[1].events = POLLIN; + + while (1) { + ret = poll(pollfd, 2, -1); + if (ret <= 0) + break; + if (pollfd[1].revents) + break; + if (!(pollfd[0].revents & POLLIN)) + break; + + ret = read(args->uffd, &msg, sizeof(msg)); + if (ret != sizeof(msg)) + break; + + if (msg.event != UFFD_EVENT_PAGEFAULT) + break; + + /* Fill the page with a known pattern */ + memset(args->page_buffer, 0xAB, args->page_size); + + copy.dst = msg.arg.pagefault.address & ~(args->page_size - 1); + copy.src = (unsigned long)args->page_buffer; + copy.len = args->page_size; + copy.mode = 0; + copy.copy = 0; + + ret = ioctl(args->uffd, UFFDIO_COPY, ©); + if (ret < 0) + break; + } + + return NULL; +} + +TEST_F(hmm, userfaultfd_read) +{ + struct hmm_buffer *buffer; + struct uffd_thread_args uffd_args; + unsigned long npages; + unsigned long size; + unsigned long i; + unsigned char *ptr; + pthread_t thread; + int uffd; + int stop_fd; + int ret; + struct uffdio_api api; + struct uffdio_register reg; + uint64_t stop = 1; + ssize_t nwrite; + + npages = 4; + size = npages << self->page_shift; + + /* Create userfaultfd */ + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); + if (uffd < 0) + SKIP(return, "userfaultfd not available"); + + api.api = UFFD_API; + api.features = 0; + ret = ioctl(uffd, UFFDIO_API, &api); + ASSERT_EQ(ret, 0); + + buffer = malloc(sizeof(*buffer)); + ASSERT_NE(buffer, NULL); + + buffer->fd = -1; + buffer->size = size; + buffer->mirror = malloc(size); + ASSERT_NE(buffer->mirror, NULL); + + /* Create anonymous mapping */ + buffer->ptr = mmap(NULL, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + ASSERT_NE(buffer->ptr, MAP_FAILED); + + /* Register the region with userfaultfd */ + reg.range.start = (unsigned long)buffer->ptr; + reg.range.len = size; + reg.mode = UFFDIO_REGISTER_MODE_MISSING; + ret = ioctl(uffd, UFFDIO_REGISTER, ®); + ASSERT_EQ(ret, 0); + + /* Set up the handler thread */ + uffd_args.uffd = uffd; + stop_fd = eventfd(0, EFD_CLOEXEC); + ASSERT_GE(stop_fd, 0); + uffd_args.stop_fd = stop_fd; + uffd_args.page_buffer = malloc(self->page_size); + ASSERT_NE(uffd_args.page_buffer, NULL); + uffd_args.page_size = self->page_size; + + ret = pthread_create(&thread, NULL, uffd_handler_thread, &uffd_args); + ASSERT_EQ(ret, 0); + + /* + * Use the unlocked read path which allows the mmap lock to be + * dropped during the fault, enabling userfaultfd resolution. + */ + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ_UNLOCKED, + buffer, npages); + ASSERT_EQ(ret, 0); + ASSERT_EQ(buffer->cpages, npages); + + /* Verify the device read the data filled by the uffd handler */ + ptr = buffer->mirror; + for (i = 0; i < size; ++i) + ASSERT_EQ(ptr[i], (unsigned char)0xAB); + + nwrite = write(stop_fd, &stop, sizeof(stop)); + ASSERT_EQ(nwrite, sizeof(stop)); + pthread_join(thread, NULL); + close(stop_fd); + free(uffd_args.page_buffer); + close(uffd); + hmm_buffer_free(buffer); +} + + TEST_HARNESS_MAIN _ Patches currently in -mm which might be from skinsburskii@gmail.com are lib-test_hmm-use-device-devt-for-coherent-device-range-selection.patch mm-hmm-move-page-fault-handling-out-of-walk-callbacks.patch mm-hmm-add-hmm_range_fault_unlocked_timeout-for-mmap-lock-drop-support.patch selftests-mm-add-hmm-test-for-mmap-lock-dropping-faults.patch mshv-use-hmm_range_fault_unlocked_timeout-for-region-faults.patch drm-nouveau-use-hmm_range_fault_unlocked_timeout-for-svm-faults.patch rdma-umem-use-hmm_range_fault_unlocked_timeout-for-odp-faults.patch accel-amdxdna-use-hmm_range_fault_unlocked_timeout-for-range-population.patch drm-gpusvm-use-hmm_range_fault_unlocked_timeout-for-range-faults.patch