From patchwork Mon Apr 17 19:53:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Xu X-Patchwork-Id: 13214493 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 44755C77B72 for ; Mon, 17 Apr 2023 19:53:40 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id E9E9F8E0003; Mon, 17 Apr 2023 15:53:38 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id DE4E28E0002; Mon, 17 Apr 2023 15:53:38 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id BB6B66B0085; Mon, 17 Apr 2023 15:53:38 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0013.hostedemail.com [216.40.44.13]) by kanga.kvack.org (Postfix) with ESMTP id 9BCD38E0002 for ; Mon, 17 Apr 2023 15:53:38 -0400 (EDT) Received: from smtpin22.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay06.hostedemail.com (Postfix) with ESMTP id 4AB6FABEDB for ; Mon, 17 Apr 2023 19:53:38 +0000 (UTC) X-FDA: 80691932916.22.F7E5DD4 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by imf16.hostedemail.com (Postfix) with ESMTP id 247F318001C for ; Mon, 17 Apr 2023 19:53:35 +0000 (UTC) Authentication-Results: imf16.hostedemail.com; dkim=pass header.d=redhat.com header.s=mimecast20190719 header.b=Pk4NV7oU; spf=pass (imf16.hostedemail.com: domain of peterx@redhat.com designates 170.10.129.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=1681761216; 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=y7gltokQzFShDFjL1ksnnTsH6QcBiNJdLCM4WiStCBA=; b=DKG+UFPnpFjv4KRdjw10taQaDvS1PMf0T0CoeQGO+1ekV5X3ETYUWf+5cRbjMtLBOHP2As AEo29KUCSMhhppEUpMvYN7ai1/2AUuUtnzlF3BeLcdCtH3NLJ7r2zc08n5PhuqsbMCtv9T BCxPwfif7rJXi2UZVtRWICuvvgN69kc= ARC-Authentication-Results: i=1; imf16.hostedemail.com; dkim=pass header.d=redhat.com header.s=mimecast20190719 header.b=Pk4NV7oU; spf=pass (imf16.hostedemail.com: domain of peterx@redhat.com designates 170.10.129.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=1681761216; a=rsa-sha256; cv=none; b=KKZeEpJsSv05kCfrEln7DJvE6X5P+g8QCCNG3QSdR+UnWX2eV4X0Uxp8qBXUa4ckenoq3w eHRGHoXDJBNF2QmUkHIOm+qlZQ8MSqEIGXfj2oO1IHCzBN3EgUHNSoV1mD2ABMWSsx+ZY3 2LUiuwGnyZgiq3RWVR1KaQCoH/jDq7Q= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681761215; 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=y7gltokQzFShDFjL1ksnnTsH6QcBiNJdLCM4WiStCBA=; b=Pk4NV7oUz4DgOZxvBuwnr8LJ1ph6mirwx6YH7eq8VZf3Qm3hpAFINCvRQeddPc1/rWe3Jz yWviLhObqD4Cyt7GvJON30cw+UCKpNvKMQvNKoIYyUJ5A3OaYM7Wt/Jidwiwdjp+ARyQDm QUHHy1r6Isr2bvPVjF2xvf9Sy0Vx7VY= 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-455-VOa5ZpRoMa-AVjf2DCg3lA-1; Mon, 17 Apr 2023 15:53:34 -0400 X-MC-Unique: VOa5ZpRoMa-AVjf2DCg3lA-1 Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-3ef2cb3bfbfso4069031cf.0 for ; Mon, 17 Apr 2023 12:53:34 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681761213; x=1684353213; 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=y7gltokQzFShDFjL1ksnnTsH6QcBiNJdLCM4WiStCBA=; b=KicB+/2faOCHbEMpD/xClx3Y6PXM+EKZ8cwN54idkrhGxXeGoF5p6QPt/jkvWS+rkn 8ITWwLXVsGFABVUom7mGpj+5vB/VjN20jwAhls6xsYK9U0AljQ6L2FaotFf4W8vXw+Fq 8pf3kkHeOwq/pUV7c4MXCif7+IMYN0UQg4GwM7Qm1ey+7bPWxjdXSqNnxgeEePLl3nIN he2HhKdArLW0hZMBhOf4qKCXUWemfr2XDcEYRCEdHDaWKbdFkwH8KRvf4Pu5UMd6iJjp 1eATHP9NNgPQd+mo6gU3LF+6wkjegwMQuF6L6LzrzrLVvMF7K9Yf5ohoOv7KJCubOk4e eU8g== X-Gm-Message-State: AAQBX9e6sglk9dIUX1VD/sl5Kxo1tobpLzzi4JL1uQo+VzW2etjr2MgF jLRzNR9HVFlWcqjX5NK99hvgxi4/nquPphPq4q3IOxSaY+fiKH2Ci7fexMhj/+4UcQqNTd0Iag5 KUmDTwkKVcRQ= X-Received: by 2002:a05:622a:1aaa:b0:3ee:5637:29cc with SMTP id s42-20020a05622a1aaa00b003ee563729ccmr9226099qtc.0.1681761213573; Mon, 17 Apr 2023 12:53:33 -0700 (PDT) X-Google-Smtp-Source: AKy350b6uFndWE+tj9O/onYcGkZ0fK6zrVlztfCRN9qHsXGXyZEKmCVQ7jywbyXKWj+B4rqOxPDQOw== X-Received: by 2002:a05:622a:1aaa:b0:3ee:5637:29cc with SMTP id s42-20020a05622a1aaa00b003ee563729ccmr9226079qtc.0.1681761213267; Mon, 17 Apr 2023 12:53:33 -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 r17-20020ac87ef1000000b003edfb5d7637sm1731278qtc.73.2023.04.17.12.53.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Apr 2023 12:53:32 -0700 (PDT) From: Peter Xu To: linux-kernel@vger.kernel.org, linux-mm@kvack.org Cc: Mike Kravetz , Andrea Arcangeli , =?utf-8?q?Mika_Penttil=C3=A4?= , Andrew Morton , peterx@redhat.com, Axel Rasmussen , Nadav Amit , David Hildenbrand Subject: [PATCH v2 6/6] selftests/mm: Add tests for RO pinning vs fork() Date: Mon, 17 Apr 2023 15:53:17 -0400 Message-Id: <20230417195317.898696-7-peterx@redhat.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230417195317.898696-1-peterx@redhat.com> References: <20230417195317.898696-1-peterx@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Rspamd-Queue-Id: 247F318001C X-Stat-Signature: 1midgxi4yssq8a19aoe9p1tkqhhodckw X-Rspam-User: X-Rspamd-Server: rspam08 X-HE-Tag: 1681761215-554936 X-HE-Meta: U2FsdGVkX18ETYmoPbUOTD1M5ohGKCXbQ0RU70/pm4NL9tjkIRrkTx7ls7xVrPklM/ivvbWiIBdoU5ULQRMPHcYbQiY1ujWVXKHmvbIlkD3bBc/d1k2xIRT35G+GuUwHGSdTHBhkxTKuyw80NMwKwEzset9LPGOCRQmr1tm1pz2AtxmCl8fA+Uf+7jQ8zHjQyJWfYlFQ6eBqhNnEngCOP81Hg21LYFwQZqll72Ix+O77ATqH0e/hnL2nkvCC/QZKPsINSNnXG9jQwejuw+lpNNpSRELMFAzuFCFb3uSNgrJhxxOINdXT+Y391z98Vscv3OM/7XLFTdrlch7xfi51thQT27ftdNv38SOoy4+j54+lMQELeMSZiyx89F9rcjZdH5PpgX9moMB3OU03HanJmif7i33utMhqQhkD3pBCeC0idrvwUm7UrksTzOWsdYQ1W8IF8B3qTOUWM3hEsqncGLxj0Wb5Xw25zxHLVmgoIcMXQ8ep+df8vN0C+7nLiwRy3vARay+TmlWjXftBpJnFLifhDAU8lbs8MH/WEQEalpIngCb98yJ0U0XJzCD2yqVoAbQ6XuJn2VZ/matovRtqfaY1dZtrDYUf96p/ig5aDiyySNS+j53QySUGfvO1Sxh+0Mp9hmvc/Vo51JtHRtcycW3p7DsKYfWPd+3qytq24TMPoFSz/YsYgNaxOAXh3C1+s4BQ0Oa4K29agM7w6oxjZMFM4nricay9BnMJhssqQ6m/2+imj2NFWzWlj+dogZCA3E9qLBjjONVzt4hD74DeRXu6ca7jI+IL3kzGhULWwhqL/1LsveUqruSVUH8/G4Cb0PNUowIrijCaJdG/A0qMKESoZceUeFUpyMGl10nIaUCJaNtxx6qCc0IF1P8h0dgQQvAfpgi9qXz9rA9D4+5Vz9/42ug5pMAa2p7/QTyWn3jWNnRYdnDI2T1n1khJ1CIP5jRF1RuHkW8nUKTWZwe 0TLo8cue DowQj87GRRAys+JyV0TTZ1Nd91u0PWgTxTu512jQYr0ShR4LlDhQU40is/2UehaZ4ZexBgDCr4NusyaCOa1KSmMmhWNzoad3JmjmlodQMFnCzlyCmKTRoxvNFN15osybBomniXK17Noch4yqDz42D3n2ATwqGaiMsIyd4xSH8HJ6amUybOmAOlTpJgntKtIREM7BV+03NJdbuVqiQJ6+KFjC/ZjyK8KbYCUzrfe171G5HaEOtVBx/TF9usU2/P871F+pogHu2SLMGU96HdprcG9IwS5YcX3/D7cbsu/l4j/3HERltsCYFbXtNz05RcwdbtByaO/f/aRw1TJbBlZMs8JoW3AFzmVehZGu3GdaWyDO18bkDRTEkWW0KnxVfcBiHi0kZTmVfJo1yeQvJrw7fUt4z0CGnugPQ1G7xaf/abvbmSjvLf0UAvr/m2NDr7mY73UE5+O1njr0aj/bfy5ZqZ0s2gxsPKitUMffk 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: Add a test suite (with 10 more sub-tests) to cover RO pinning against fork() over uffd-wp. It covers both: (1) Early CoW test in fork() when page pinned, (2) page unshare due to RO longterm pin. They are: Testing wp-fork-pin on anon... done Testing wp-fork-pin on shmem... done Testing wp-fork-pin on shmem-private... done Testing wp-fork-pin on hugetlb... done Testing wp-fork-pin on hugetlb-private... done Testing wp-fork-pin-with-event on anon... done Testing wp-fork-pin-with-event on shmem... done Testing wp-fork-pin-with-event on shmem-private... done Testing wp-fork-pin-with-event on hugetlb... done Testing wp-fork-pin-with-event on hugetlb-private... done CONFIG_GUP_TEST needed or they'll be skipped. Testing wp-fork-pin on anon... skipped [reason: Possibly CONFIG_GUP_TEST missing or unprivileged] Note that the major test goal is on private memory, but no hurt to also run all of them over shared because shared memory should work the same. Signed-off-by: Peter Xu --- tools/testing/selftests/mm/uffd-unit-tests.c | 144 ++++++++++++++++++- 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c index 739fc4d30342..269c86768a02 100644 --- a/tools/testing/selftests/mm/uffd-unit-tests.c +++ b/tools/testing/selftests/mm/uffd-unit-tests.c @@ -7,6 +7,8 @@ #include "uffd-common.h" +#include "../../../../mm/gup_test.h" + #ifdef __NR_userfaultfd /* The unit test doesn't need a large or random size, make it 32MB for now */ @@ -247,7 +249,53 @@ static void *fork_event_consumer(void *data) return NULL; } -static int pagemap_test_fork(int uffd, bool with_event) +typedef struct { + int gup_fd; + bool pinned; +} pin_args; + +/* + * Returns 0 if succeed, <0 for errors. pin_pages() needs to be paired + * with unpin_pages(). Currently it needs to be RO longterm pin to satisfy + * all needs of the test cases (e.g., trigger unshare, trigger fork() early + * CoW, etc.). + */ +static int pin_pages(pin_args *args, void *buffer, size_t size) +{ + struct pin_longterm_test test = { + .addr = (uintptr_t)buffer, + .size = size, + /* Read-only pins */ + .flags = 0, + }; + + if (args->pinned) + err("already pinned"); + + args->gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR); + if (args->gup_fd < 0) + return -errno; + + if (ioctl(args->gup_fd, PIN_LONGTERM_TEST_START, &test)) { + /* Even if gup_test existed, can be an old gup_test / kernel */ + close(args->gup_fd); + return -errno; + } + args->pinned = true; + return 0; +} + +static void unpin_pages(pin_args *args) +{ + if (!args->pinned) + err("unpin without pin first"); + if (ioctl(args->gup_fd, PIN_LONGTERM_TEST_STOP)) + err("PIN_LONGTERM_TEST_STOP"); + close(args->gup_fd); + args->pinned = false; +} + +static int pagemap_test_fork(int uffd, bool with_event, bool test_pin) { fork_event_args args = { .parent_uffd = uffd, .child_uffd = -1 }; pthread_t thread; @@ -264,7 +312,17 @@ static int pagemap_test_fork(int uffd, bool with_event) child = fork(); if (!child) { /* Open the pagemap fd of the child itself */ + pin_args args = {}; + fd = pagemap_open(); + + if (test_pin && pin_pages(&args, area_dst, page_size)) + /* + * Normally when reach here we have pinned in + * previous tests, so shouldn't fail anymore + */ + err("pin page failed in child"); + value = pagemap_get_entry(fd, area_dst); /* * After fork(), we should handle uffd-wp bit differently: @@ -273,6 +331,8 @@ static int pagemap_test_fork(int uffd, bool with_event) * (2) when without EVENT_FORK, it should be dropped */ pagemap_check_wp(value, with_event); + if (test_pin) + unpin_pages(&args); /* Succeed */ exit(0); } @@ -352,7 +412,7 @@ static void uffd_wp_fork_test_common(uffd_test_args_t *args, wp_range(uffd, (uint64_t)area_dst, page_size, true); value = pagemap_get_entry(pagemap_fd, area_dst); pagemap_check_wp(value, true); - if (pagemap_test_fork(uffd, with_event)) { + if (pagemap_test_fork(uffd, with_event, false)) { uffd_test_fail("Detected %s uffd-wp bit in child in present pte", with_event ? "missing" : "stall"); goto out; @@ -383,7 +443,7 @@ static void uffd_wp_fork_test_common(uffd_test_args_t *args, /* Uffd-wp should persist even swapped out */ value = pagemap_get_entry(pagemap_fd, area_dst); pagemap_check_wp(value, true); - if (pagemap_test_fork(uffd, with_event)) { + if (pagemap_test_fork(uffd, with_event, false)) { uffd_test_fail("Detected %s uffd-wp bit in child in zapped pte", with_event ? "missing" : "stall"); goto out; @@ -415,6 +475,68 @@ static void uffd_wp_fork_with_event_test(uffd_test_args_t *args) uffd_wp_fork_test_common(args, true); } +static void uffd_wp_fork_pin_test_common(uffd_test_args_t *args, + bool with_event) +{ + int pagemap_fd; + pin_args pin_args = {}; + + if (uffd_register(uffd, area_dst, page_size, false, true, false)) + err("register failed"); + + pagemap_fd = pagemap_open(); + + /* Touch the page */ + *area_dst = 1; + wp_range(uffd, (uint64_t)area_dst, page_size, true); + + /* + * 1. First pin, then fork(). This tests fork() special path when + * doing early CoW if the page is private. + */ + if (pin_pages(&pin_args, area_dst, page_size)) { + uffd_test_skip("Possibly CONFIG_GUP_TEST missing " + "or unprivileged"); + close(pagemap_fd); + uffd_unregister(uffd, area_dst, page_size); + return; + } + + if (pagemap_test_fork(uffd, with_event, false)) { + uffd_test_fail("Detected %s uffd-wp bit in early CoW of fork()", + with_event ? "missing" : "stall"); + unpin_pages(&pin_args); + goto out; + } + + unpin_pages(&pin_args); + + /* + * 2. First fork(), then pin (in the child, where test_pin==true). + * This tests COR, aka, page unsharing on private memories. + */ + if (pagemap_test_fork(uffd, with_event, true)) { + uffd_test_fail("Detected %s uffd-wp bit when RO pin", + with_event ? "missing" : "stall"); + goto out; + } + uffd_test_pass(); +out: + if (uffd_unregister(uffd, area_dst, page_size)) + err("register failed"); + close(pagemap_fd); +} + +static void uffd_wp_fork_pin_test(uffd_test_args_t *args) +{ + uffd_wp_fork_pin_test_common(args, false); +} + +static void uffd_wp_fork_pin_with_event_test(uffd_test_args_t *args) +{ + uffd_wp_fork_pin_test_common(args, true); +} + static void check_memory_contents(char *p) { unsigned long i, j; @@ -923,6 +1045,22 @@ uffd_test_case_t uffd_tests[] = { /* when set, child process should inherit uffd-wp bits */ UFFD_FEATURE_EVENT_FORK, }, + { + .name = "wp-fork-pin", + .uffd_fn = uffd_wp_fork_pin_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_PAGEFAULT_FLAG_WP | + UFFD_FEATURE_WP_HUGETLBFS_SHMEM, + }, + { + .name = "wp-fork-pin-with-event", + .uffd_fn = uffd_wp_fork_pin_with_event_test, + .mem_targets = MEM_ALL, + .uffd_feature_required = UFFD_FEATURE_PAGEFAULT_FLAG_WP | + UFFD_FEATURE_WP_HUGETLBFS_SHMEM | + /* when set, child process should inherit uffd-wp bits */ + UFFD_FEATURE_EVENT_FORK, + }, { .name = "wp-unpopulated", .uffd_fn = uffd_wp_unpopulated_test,