From patchwork Wed Apr 12 16:44:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Xu X-Patchwork-Id: 13209360 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6FA18C77B6E for ; Wed, 12 Apr 2023 16:44:12 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 1059E90000A; Wed, 12 Apr 2023 12:44:12 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 0B662900003; Wed, 12 Apr 2023 12:44:12 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id EBF9D90000A; Wed, 12 Apr 2023 12:44:11 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0017.hostedemail.com [216.40.44.17]) by kanga.kvack.org (Postfix) with ESMTP id DAF9F900003 for ; Wed, 12 Apr 2023 12:44:11 -0400 (EDT) Received: from smtpin30.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay10.hostedemail.com (Postfix) with ESMTP id AB627C01AF for ; Wed, 12 Apr 2023 16:44:11 +0000 (UTC) X-FDA: 80673311502.30.999EC80 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by imf05.hostedemail.com (Postfix) with ESMTP id 6745F10000A for ; Wed, 12 Apr 2023 16:44:09 +0000 (UTC) Authentication-Results: imf05.hostedemail.com; dkim=pass header.d=redhat.com header.s=mimecast20190719 header.b=XjfspCJS; spf=pass (imf05.hostedemail.com: domain of peterx@redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=peterx@redhat.com; dmarc=pass (policy=none) header.from=redhat.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1681317849; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=moJlUxkxV9yMXHXtrl4RDvvI70k6nNWMdoirhLeOGGU=; b=7Kb3y6s2wIF9qRGIIpcXMUO3Pr5+XjmsK45jY+pQR5JWOh0iRSOJKIT7BjaT/lDX1iCfGt 04EgP7XdA+k1COCk+L4USLK6Z8RUF5dH6D7x1spa2zpsWyB6Myc6OOisf1VLwEtp1mkV8v Bedv0DNRWMdwPDYFKNKaCyT5FsRDI+g= ARC-Authentication-Results: i=1; imf05.hostedemail.com; dkim=pass header.d=redhat.com header.s=mimecast20190719 header.b=XjfspCJS; spf=pass (imf05.hostedemail.com: domain of peterx@redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=peterx@redhat.com; dmarc=pass (policy=none) header.from=redhat.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1681317849; a=rsa-sha256; cv=none; b=kZOpHnR+8wirBspxfDhcDCCPCtjCC9LYSHo7hQyCX+DnU4LejQvNuVleafBLcEvFM6tUyG wCThqB7BtORD0zRzMXTV6QuJ2UO5JKveeK3XMq7JhhHndKzQ/p6WrFmLmZzr10mxgZaFF0 8pSX4fD7ab48W2JpOKlgxZy/Jib9CSU= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681317848; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=moJlUxkxV9yMXHXtrl4RDvvI70k6nNWMdoirhLeOGGU=; b=XjfspCJSbEveToo8/3D0S+GHMY+6Hf1e1kyw+YarT/K6L8VFv9wg4kc2ww2LpkkfeJaPNK N/rnuKcMxZBta+VCD8lkc7/rRKt96PksSQyfwizq5HJunpshxJA2bRVDI2eaSaOPWQXMcz WBbmPTvAmU3qdhweuPGmrix1fkh8WZs= Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-422-y9yr1hIvNFWg8YPRk5BBmw-1; Wed, 12 Apr 2023 12:44:07 -0400 X-MC-Unique: y9yr1hIvNFWg8YPRk5BBmw-1 Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-3e8d943d3a4so667771cf.1 for ; Wed, 12 Apr 2023 09:44:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681317845; x=1683909845; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=moJlUxkxV9yMXHXtrl4RDvvI70k6nNWMdoirhLeOGGU=; b=JfpKpYSI4NE3FK9XSURLePrVT5c8sm/EtiCZ+0MXwbDZPz/K4Q9KaIJdLp1KlwzLGi fwFNpNxOZ+TSIqo4Y3gt0rfzuMHTnk9HkuVBL2UV7fQKaXZfHGrrcNPKOoPc0rfYEJHi rcY080ZnQ+R/ZKn+p7pI/vciB8ps7s3d21PFfzflYWDB9wCE+b1DwlyBL6bMBoMOoTbr V0NLbT4KZMJpNAfW52CHWiI5nX88XuyNC+yISOvRZ+VVO4/OVfSvvTrUBP7kH5y9l4KJ pn0DdgYmhapudEqJZWo5eCVdQGviRrmyDV9x4dCwj8r1oYc0G1zLYE87QEJhAeloHB2B kUMA== X-Gm-Message-State: AAQBX9fEDXruW73PD2pB5r7uDBz0hBR5wpF53MMxCeGIIiQtuGfIH91p EYs/mhMHA3ljAob17LZoIKCDbEhP+lkZtYlcXaxvsoY5vYC3R4FbJsic2VgeeRJurKTCuYWFiKV cA9TM0OSTgBY= X-Received: by 2002:a05:622a:14cf:b0:3b8:6c6e:4949 with SMTP id u15-20020a05622a14cf00b003b86c6e4949mr4198048qtx.4.1681317844851; Wed, 12 Apr 2023 09:44:04 -0700 (PDT) X-Google-Smtp-Source: AKy350ZyI0LO2mW+nKpthrbnN+kJ/X5tJn7Dl5m81czcPWaU9AQPMWeY1C8uomlpugD00uwX305oNw== X-Received: by 2002:a05:622a:14cf:b0:3b8:6c6e:4949 with SMTP id u15-20020a05622a14cf00b003b86c6e4949mr4198015qtx.4.1681317844406; Wed, 12 Apr 2023 09:44:04 -0700 (PDT) Received: from x1n.redhat.com (bras-base-aurron9127w-grc-40-70-52-229-124.dsl.bell.ca. [70.52.229.124]) by smtp.gmail.com with ESMTPSA id do28-20020a05620a2b1c00b007484bc04a63sm4782932qkb.99.2023.04.12.09.44.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Apr 2023 09:44:03 -0700 (PDT) From: Peter Xu To: linux-kernel@vger.kernel.org, linux-mm@kvack.org Cc: David Hildenbrand , Andrew Morton , Leonardo Bras Soares Passos , peterx@redhat.com, Andrea Arcangeli , Nadav Amit , Mike Rapoport , Axel Rasmussen , Mike Kravetz Subject: [PATCH v2 25/31] selftests/mm: Move uffd sig/events tests into uffd unit tests Date: Wed, 12 Apr 2023 12:44:00 -0400 Message-Id: <20230412164400.328798-1-peterx@redhat.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230412163922.327282-1-peterx@redhat.com> References: <20230412163922.327282-1-peterx@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Stat-Signature: 3dkuwuc8jqpzste93t63jcae7katpeto X-Rspam-User: X-Rspamd-Queue-Id: 6745F10000A X-Rspamd-Server: rspam06 X-HE-Tag: 1681317849-326875 X-HE-Meta: U2FsdGVkX1/W3cbK/GIzhMce3o4ePaDQzFyZXZKnuYbzs2z62AFBWB9f7e6fwaRVV7mgJ/Fr3gsLfJdbPS3FR1eiq0pas9YO/BuHpK2tfAFUPwrlGm+cKIUn84uw+tfyLUh5hwXNdl6L7qYUWDjpw6Her86bcpasCrRrIxc6dZ/fmrj4YAY0rAPrdvvYHcEPytFaShAe5+sWaTctDrHtVRYd9cmNJcciBngUZWTasqoxHJuchCMgGV7UaDuEb7IiwzphBwWw1W24Flq6ZsS47F8CZTOXos8sPayApyrnoX/MRu9+BOAaA9eEhSAFXmGqnJyGM4fDJ5fbQno+IkgsPm+ruutvS0jFE5thjbwqFn5hkqmsFAu0dI7TsVf1Wzfr4n5GOm2YlpcK/+7A4OmJH05q8FJTiXiRGAAaMfisqNkUxGsFdUEKGojs0HaE5MWyZ4EBG8s7UOM3KupWPE+6IP5CYr/ceqAJV5dqxYp5vIEvX3PsmdE0KgH7s5ZPjrW3ianJNScYOeE/LEHKfPrq8Prdr1gsneyUo5otp6riTw54C4k0m5ea+Pq2FJUx7+J9mBbzrx98VOrjiiPMTqr9WY09jh23ogFukOdKBe7cmcXZy6Kab+m5/EbfrGAkRxOxKiTIGXFPdJ63AZKEfFu5LoVGMvK3OWGixV2ZSV1fs+glgSegJejwgLnJ434zPnVj0wz2nGkcHy2nJLGKHVG8mfnijZ4tiwMM53aZHIrdRKxFNWKRgB7QisV2iJ+dy+aBoCdNhYt7ayENgL+/2ZVuuOca3rMJa9nnWyeKinEao6KwuKdczAe8lcoeF+4l73b6cM7dFa0EHW85gjw5Qr+ijkJ8fWfY8Y6Gjh1mWWyspC60N9vWtAoyl4kNaVLz2rRQAHlc1hIsuEZMfK5i1eM1hhtEWIO8llgBmKqkRUYUe9HoWtgBq0Jideo8eylIwIp+wyaxG000hatWR553urE Wrl+UKZx OTBBqc+0sV61nrbttM7YbmlcNCgrI87l04rwjl9VbUNzJaSdfmI3vJ4IF48zcOzousyKIjhHXqY2yh/PgMTl6r2+2IE5QGPQrrRVe/BIxKb1rLQyLIpZzeTIy9IVrJtvxWCJtMEn4QULsq7Hid7QjDWLhxke9M5OzLbQUB8hcT+304rg/rXfsEnIC/wFWX/n1Wdyt9gg1XqTd6QS6WZXVZ4Dyj33t39k5Aa5Qjv93iRl6f/0vsjPHGUPrZ2PT7IzBHG6/R41dZ41UotYhEq2sL+SvFhw5nWVs7fLdpUXdGkqBrfuJmPO8w/f7yQa91SikN5QApUQtM1+qeNhnKMfBI05RdCmNeRFg1kXPNEHjKtiuguCboDwj6GKgxzUQyXZV+R9q1iIDnTVQkdimLg/D8e34vnWy3X7v5WtVoGfPpNguh68FQqbQTttAtiLFT8GBtSZOv2lkFf4ZGX3UOmeE3q4gUNLLFegUruj3 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: Move the two tests into the unit test, and convert it into 20 standalone tests: - events test on all 5 mem types, with wp on/off - signal test on all 5 mem types, with wp on/off Testing sigbus on anon... done Testing sigbus on shmem... done Testing sigbus on shmem-private... done Testing sigbus on hugetlb... done Testing sigbus on hugetlb-private... done Testing sigbus-wp on anon... done Testing sigbus-wp on shmem... done Testing sigbus-wp on shmem-private... done Testing sigbus-wp on hugetlb... done Testing sigbus-wp on hugetlb-private... done Testing events on anon... done Testing events on shmem... done Testing events on shmem-private... done Testing events on hugetlb... done Testing events on hugetlb-private... done Testing events-wp on anon... done Testing events-wp on shmem... done Testing events-wp on shmem-private... done Testing events-wp on hugetlb... done Testing events-wp on hugetlb-private... done It'll also remove a lot of global references along the way, e.g. test_uffdio_wp will be replaced with the wp value passed over. Signed-off-by: Peter Xu --- tools/testing/selftests/mm/uffd-stress.c | 227 +--------------- tools/testing/selftests/mm/uffd-unit-tests.c | 264 +++++++++++++++++++ 2 files changed, 265 insertions(+), 226 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-stress.c b/tools/testing/selftests/mm/uffd-stress.c index f9322bbaf825..ce51180238d8 100644 --- a/tools/testing/selftests/mm/uffd-stress.c +++ b/tools/testing/selftests/mm/uffd-stress.c @@ -273,133 +273,6 @@ static int stress(struct uffd_args *args) return 0; } -sigjmp_buf jbuf, *sigbuf; - -static void sighndl(int sig, siginfo_t *siginfo, void *ptr) -{ - if (sig == SIGBUS) { - if (sigbuf) - siglongjmp(*sigbuf, 1); - abort(); - } -} - -/* - * For non-cooperative userfaultfd test we fork() a process that will - * generate pagefaults, will mremap the area monitored by the - * userfaultfd and at last this process will release the monitored - * area. - * For the anonymous and shared memory the area is divided into two - * parts, the first part is accessed before mremap, and the second - * part is accessed after mremap. Since hugetlbfs does not support - * mremap, the entire monitored area is accessed in a single pass for - * HUGETLB_TEST. - * The release of the pages currently generates event for shmem and - * anonymous memory (UFFD_EVENT_REMOVE), hence it is not checked - * for hugetlb. - * For signal test(UFFD_FEATURE_SIGBUS), signal_test = 1, we register - * monitored area, generate pagefaults and test that signal is delivered. - * Use UFFDIO_COPY to allocate missing page and retry. For signal_test = 2 - * test robustness use case - we release monitored area, fork a process - * that will generate pagefaults and verify signal is generated. - * This also tests UFFD_FEATURE_EVENT_FORK event along with the signal - * feature. Using monitor thread, verify no userfault events are generated. - */ -static int faulting_process(int signal_test, bool wp) -{ - unsigned long nr; - unsigned long long count; - unsigned long split_nr_pages; - unsigned long lastnr; - struct sigaction act; - volatile unsigned long signalled = 0; - - split_nr_pages = (nr_pages + 1) / 2; - - if (signal_test) { - sigbuf = &jbuf; - memset(&act, 0, sizeof(act)); - act.sa_sigaction = sighndl; - act.sa_flags = SA_SIGINFO; - if (sigaction(SIGBUS, &act, 0)) - err("sigaction"); - lastnr = (unsigned long)-1; - } - - for (nr = 0; nr < split_nr_pages; nr++) { - volatile int steps = 1; - unsigned long offset = nr * page_size; - - if (signal_test) { - if (sigsetjmp(*sigbuf, 1) != 0) { - if (steps == 1 && nr == lastnr) - err("Signal repeated"); - - lastnr = nr; - if (signal_test == 1) { - if (steps == 1) { - /* This is a MISSING request */ - steps++; - if (copy_page(uffd, offset, wp)) - signalled++; - } else { - /* This is a WP request */ - assert(steps == 2); - wp_range(uffd, - (__u64)area_dst + - offset, - page_size, false); - } - } else { - signalled++; - continue; - } - } - } - - count = *area_count(area_dst, nr); - if (count != count_verify[nr]) - err("nr %lu memory corruption %llu %llu\n", - nr, count, count_verify[nr]); - /* - * Trigger write protection if there is by writing - * the same value back. - */ - *area_count(area_dst, nr) = count; - } - - if (signal_test) - return signalled != split_nr_pages; - - area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, area_src); - if (area_dst == MAP_FAILED) - err("mremap"); - /* Reset area_src since we just clobbered it */ - area_src = NULL; - - for (; nr < nr_pages; nr++) { - count = *area_count(area_dst, nr); - if (count != count_verify[nr]) { - err("nr %lu memory corruption %llu %llu\n", - nr, count, count_verify[nr]); - } - /* - * Trigger write protection if there is by writing - * the same value back. - */ - *area_count(area_dst, nr) = count; - } - - uffd_test_ops->release_pages(area_dst); - - for (nr = 0; nr < nr_pages; nr++) - if (my_bcmp(area_dst + nr * page_size, zeropage, page_size)) - err("nr %lu is not zero", nr); - - return 0; -} - static void retry_uffdio_zeropage(int ufd, struct uffdio_zeropage *uffdio_zeropage, unsigned long offset) @@ -483,103 +356,6 @@ static int userfaultfd_zeropage_test(void) return 0; } -static int userfaultfd_events_test(void) -{ - pthread_t uffd_mon; - int err, features; - pid_t pid; - char c; - struct uffd_args args = { 0 }; - - printf("testing events (fork, remap, remove): "); - fflush(stdout); - - features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP | - UFFD_FEATURE_EVENT_REMOVE; - uffd_test_ctx_init(features); - - fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); - - if (uffd_register(uffd, area_dst, nr_pages * page_size, - true, test_uffdio_wp, false)) - err("register failure"); - - args.apply_wp = test_uffdio_wp; - if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &args)) - err("uffd_poll_thread create"); - - pid = fork(); - if (pid < 0) - err("fork"); - - if (!pid) - exit(faulting_process(0, test_uffdio_wp)); - - waitpid(pid, &err, 0); - if (err) - err("faulting process failed"); - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) - err("pipe write"); - if (pthread_join(uffd_mon, NULL)) - return 1; - - uffd_stats_report(&args, 1); - - return args.missing_faults != nr_pages; -} - -static int userfaultfd_sig_test(void) -{ - unsigned long userfaults; - pthread_t uffd_mon; - int err, features; - pid_t pid; - char c; - struct uffd_args args = { 0 }; - - printf("testing signal delivery: "); - fflush(stdout); - - features = UFFD_FEATURE_EVENT_FORK|UFFD_FEATURE_SIGBUS; - uffd_test_ctx_init(features); - - fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); - - if (uffd_register(uffd, area_dst, nr_pages * page_size, - true, test_uffdio_wp, false)) - err("register failure"); - - if (faulting_process(1, test_uffdio_wp)) - err("faulting process failed"); - - uffd_test_ops->release_pages(area_dst); - - args.apply_wp = test_uffdio_wp; - if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &args)) - err("uffd_poll_thread create"); - - pid = fork(); - if (pid < 0) - err("fork"); - - if (!pid) - exit(faulting_process(2, test_uffdio_wp)); - - waitpid(pid, &err, 0); - if (err) - err("faulting process failed"); - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) - err("pipe write"); - if (pthread_join(uffd_mon, (void **)&userfaults)) - return 1; - - printf("done.\n"); - if (userfaults) - err("Signal test failed, userfaults: %ld", userfaults); - - return userfaults != 0; -} - static int userfaultfd_stress(void) { void *area; @@ -691,8 +467,7 @@ static int userfaultfd_stress(void) uffd_stats_report(args, nr_cpus); } - return userfaultfd_zeropage_test() || userfaultfd_sig_test() - || userfaultfd_events_test(); + return userfaultfd_zeropage_test(); } static void set_test_type(const char *type) diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index cba04608bdb0..94549696f4b2 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -18,6 +18,9 @@ #define MEM_HUGETLB BIT_ULL(3) #define MEM_HUGETLB_PRIVATE BIT_ULL(4) +#define MEM_ALL (MEM_ANON | MEM_SHMEM | MEM_SHMEM_PRIVATE | \ + MEM_HUGETLB | MEM_HUGETLB_PRIVATE) + struct mem_type { const char *name; unsigned int mem_flag; @@ -426,6 +429,237 @@ void uffd_minor_collapse_test(void) uffd_minor_test_common(true, false); } +static sigjmp_buf jbuf, *sigbuf; + +static void sighndl(int sig, siginfo_t *siginfo, void *ptr) +{ + if (sig == SIGBUS) { + if (sigbuf) + siglongjmp(*sigbuf, 1); + abort(); + } +} + +/* + * For non-cooperative userfaultfd test we fork() a process that will + * generate pagefaults, will mremap the area monitored by the + * userfaultfd and at last this process will release the monitored + * area. + * For the anonymous and shared memory the area is divided into two + * parts, the first part is accessed before mremap, and the second + * part is accessed after mremap. Since hugetlbfs does not support + * mremap, the entire monitored area is accessed in a single pass for + * HUGETLB_TEST. + * The release of the pages currently generates event for shmem and + * anonymous memory (UFFD_EVENT_REMOVE), hence it is not checked + * for hugetlb. + * For signal test(UFFD_FEATURE_SIGBUS), signal_test = 1, we register + * monitored area, generate pagefaults and test that signal is delivered. + * Use UFFDIO_COPY to allocate missing page and retry. For signal_test = 2 + * test robustness use case - we release monitored area, fork a process + * that will generate pagefaults and verify signal is generated. + * This also tests UFFD_FEATURE_EVENT_FORK event along with the signal + * feature. Using monitor thread, verify no userfault events are generated. + */ +static int faulting_process(int signal_test, bool wp) +{ + unsigned long nr, i; + unsigned long long count; + unsigned long split_nr_pages; + unsigned long lastnr; + struct sigaction act; + volatile unsigned long signalled = 0; + + split_nr_pages = (nr_pages + 1) / 2; + + if (signal_test) { + sigbuf = &jbuf; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = sighndl; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGBUS, &act, 0)) + err("sigaction"); + lastnr = (unsigned long)-1; + } + + for (nr = 0; nr < split_nr_pages; nr++) { + volatile int steps = 1; + unsigned long offset = nr * page_size; + + if (signal_test) { + if (sigsetjmp(*sigbuf, 1) != 0) { + if (steps == 1 && nr == lastnr) + err("Signal repeated"); + + lastnr = nr; + if (signal_test == 1) { + if (steps == 1) { + /* This is a MISSING request */ + steps++; + if (copy_page(uffd, offset, wp)) + signalled++; + } else { + /* This is a WP request */ + assert(steps == 2); + wp_range(uffd, + (__u64)area_dst + + offset, + page_size, false); + } + } else { + signalled++; + continue; + } + } + } + + count = *area_count(area_dst, nr); + if (count != count_verify[nr]) + err("nr %lu memory corruption %llu %llu\n", + nr, count, count_verify[nr]); + /* + * Trigger write protection if there is by writing + * the same value back. + */ + *area_count(area_dst, nr) = count; + } + + if (signal_test) + return signalled != split_nr_pages; + + area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size, + MREMAP_MAYMOVE | MREMAP_FIXED, area_src); + if (area_dst == MAP_FAILED) + err("mremap"); + /* Reset area_src since we just clobbered it */ + area_src = NULL; + + for (; nr < nr_pages; nr++) { + count = *area_count(area_dst, nr); + if (count != count_verify[nr]) { + err("nr %lu memory corruption %llu %llu\n", + nr, count, count_verify[nr]); + } + /* + * Trigger write protection if there is by writing + * the same value back. + */ + *area_count(area_dst, nr) = count; + } + + uffd_test_ops->release_pages(area_dst); + + for (nr = 0; nr < nr_pages; nr++) + for (i = 0; i < page_size; i++) + if (*(area_dst + nr * page_size + i) != 0) + err("page %lu offset %lu is not zero", nr, i); + + return 0; +} + +static void uffd_sigbus_test_common(bool wp) +{ + unsigned long userfaults; + pthread_t uffd_mon; + pid_t pid; + int err; + char c; + struct uffd_args args = { 0 }; + + fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); + + if (uffd_register(uffd, area_dst, nr_pages * page_size, + true, wp, false)) + err("register failure"); + + if (faulting_process(1, wp)) + err("faulting process failed"); + + uffd_test_ops->release_pages(area_dst); + + args.apply_wp = wp; + if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args)) + err("uffd_poll_thread create"); + + pid = fork(); + if (pid < 0) + err("fork"); + + if (!pid) + exit(faulting_process(2, wp)); + + waitpid(pid, &err, 0); + if (err) + err("faulting process failed"); + if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) + err("pipe write"); + if (pthread_join(uffd_mon, (void **)&userfaults)) + err("pthread_join()"); + + if (userfaults) + uffd_test_fail("Signal test failed, userfaults: %ld", userfaults); + else + uffd_test_pass(); +} + +static void uffd_sigbus_test(void) +{ + uffd_sigbus_test_common(false); +} + +static void uffd_sigbus_wp_test(void) +{ + uffd_sigbus_test_common(true); +} + +static void uffd_events_test_common(bool wp) +{ + pthread_t uffd_mon; + pid_t pid; + int err; + char c; + struct uffd_args args = { 0 }; + + fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); + if (uffd_register(uffd, area_dst, nr_pages * page_size, + true, wp, false)) + err("register failure"); + + args.apply_wp = wp; + if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args)) + err("uffd_poll_thread create"); + + pid = fork(); + if (pid < 0) + err("fork"); + + if (!pid) + exit(faulting_process(0, wp)); + + waitpid(pid, &err, 0); + if (err) + err("faulting process failed"); + if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) + err("pipe write"); + if (pthread_join(uffd_mon, NULL)) + err("pthread_join()"); + + if (args.missing_faults != nr_pages) + uffd_test_fail("Fault counts wrong"); + else + uffd_test_pass(); +} + +static void uffd_events_test(void) +{ + uffd_events_test_common(false); +} + +static void uffd_events_wp_test(void) +{ + uffd_events_test_common(true); +} + uffd_test_case_t uffd_tests[] = { { .name = "pagemap", @@ -463,6 +697,36 @@ uffd_test_case_t uffd_tests[] = { /* We can't test MADV_COLLAPSE, so try our luck */ .uffd_feature_required = UFFD_FEATURE_MINOR_SHMEM, }, + { + .name = "sigbus", + .uffd_fn = uffd_sigbus_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_SIGBUS | + UFFD_FEATURE_EVENT_FORK, + }, + { + .name = "sigbus-wp", + .uffd_fn = uffd_sigbus_wp_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_SIGBUS | + UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_PAGEFAULT_FLAG_WP, + }, + { + .name = "events", + .uffd_fn = uffd_events_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_EVENT_FORK | + UFFD_FEATURE_EVENT_REMAP | UFFD_FEATURE_EVENT_REMOVE, + }, + { + .name = "events-wp", + .uffd_fn = uffd_events_wp_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_EVENT_FORK | + UFFD_FEATURE_EVENT_REMAP | UFFD_FEATURE_EVENT_REMOVE | + UFFD_FEATURE_PAGEFAULT_FLAG_WP | + UFFD_FEATURE_WP_HUGETLBFS_SHMEM, + }, }; int main(int argc, char *argv[])