From patchwork Thu Apr 13 23:12:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Xu X-Patchwork-Id: 13210723 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 25D6BC77B76 for ; Thu, 13 Apr 2023 23:12:20 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id BEBF8280004; Thu, 13 Apr 2023 19:12:19 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id B9BCA280001; Thu, 13 Apr 2023 19:12:19 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id A63E6280004; Thu, 13 Apr 2023 19:12:19 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0011.hostedemail.com [216.40.44.11]) by kanga.kvack.org (Postfix) with ESMTP id 999D4280001 for ; Thu, 13 Apr 2023 19:12:19 -0400 (EDT) Received: from smtpin22.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay06.hostedemail.com (Postfix) with ESMTP id 5DC30AB722 for ; Thu, 13 Apr 2023 23:12:19 +0000 (UTC) X-FDA: 80677918398.22.7AFF179 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by imf11.hostedemail.com (Postfix) with ESMTP id 6D5BA4000A for ; Thu, 13 Apr 2023 23:12:16 +0000 (UTC) Authentication-Results: imf11.hostedemail.com; dkim=pass header.d=redhat.com header.s=mimecast20190719 header.b=SnDFCiL0; spf=pass (imf11.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=1681427536; 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=U50mzc3x7opcua/Zq5xIU5DytHBPjAF3IuThARnv+MM=; b=0W1HqvlfmkPjAqA1fmeDu29XMcwz74as3nH9iXpvNZnUeeciEd52HN59OSAwHHmClVshgN q55mDWywB7gJlqHLlYFEZKqNy8K9TNRNwz414Xpjq4viOwcWtEHgb8TYeEP0QJE/4i8hhk wpMqEPiqUgVVt1n4hend58IhhTswLTA= ARC-Authentication-Results: i=1; imf11.hostedemail.com; dkim=pass header.d=redhat.com header.s=mimecast20190719 header.b=SnDFCiL0; spf=pass (imf11.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=1681427536; a=rsa-sha256; cv=none; b=Dk3cmuMCOdqJN66yu0SmPwTsQS6ozZ1U246SY4m6PgCt6rPMZbwdGTHz8ItuK7CudpTycu 1NAMOjjiUnKJ1oxxz3LxBMrS1B53ASYey046pE8HIOf+gjU+h+MVvSjNo22NBUsaogOLFL ggEukfeASj+x8vNCDIPbigwKrZaU/0U= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1681427535; 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=U50mzc3x7opcua/Zq5xIU5DytHBPjAF3IuThARnv+MM=; b=SnDFCiL0FiDlRm79lTrVqPohkXE+xMtx3eiNJc0d/u1iofU51OEcw/A7HFseB0akyyNxMd 0TdgsIDPXsNfk+QfGrw8tDxL24h1VDooob7LqaoF1b4viq+LSxSWauJXY28Z9P31D63b9V W+EjWpviibMbcnk1UT9D9GzpP0TXLW0= Received: from mail-qt1-f198.google.com (mail-qt1-f198.google.com [209.85.160.198]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-517-_UIwM3ccMSiOcTLcyzfSXg-1; Thu, 13 Apr 2023 19:12:14 -0400 X-MC-Unique: _UIwM3ccMSiOcTLcyzfSXg-1 Received: by mail-qt1-f198.google.com with SMTP id d75a77b69052e-3eab4df3ef0so823901cf.0 for ; Thu, 13 Apr 2023 16:12:14 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681427532; x=1684019532; 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=U50mzc3x7opcua/Zq5xIU5DytHBPjAF3IuThARnv+MM=; b=SnI256tqqpmyViqYSXDHWMV4leSxVNjViOMlyyefaBoaywJpBMzVcPyfIIoYTvQXhE qDGVPCPEveG4124t9YczvjEm7k+Zzxd9uV8qFBN+r4sdWxcQ2YZyLxRtyva90YHA38Uf YPoG8TS4ePWIN55/l25sfx9kNQey0LUkHmCaoE6tEtlUX1r/RN+k87YubSHo7RbHBW50 T3sdr0uw53iwPM7681stFg8ErlZi/OEU0rUdLRcdZZDssBtQxosFW+ZfPgQpRwvsZ38X XNlOWzDUuxyxgQy86xGTLLc62PBfiuVNgoehSc4pM4z6+PprezbAGF7Y8y4Z8cYB2Xku hB0Q== X-Gm-Message-State: AAQBX9fK6M9+6vlaZmHL8fZX67BusV1dGc5IBY7iXfHRBjMDYcnMm1jw RHZ60CFOYK+OVj9iPhKuRaP5FwlznaB1KV/o/3Xa3VjFAGQBDFJQQ+lTeyXXTZcBU7AQbuw4yZH y8T/YJRglU5V9PwjKltaLN5KGMIWKEkL9p6xYZxnCG3LM+WwDMdMcr5WNhMCobTq/bGIL X-Received: by 2002:a05:622a:1a0b:b0:3e6:3af1:de7a with SMTP id f11-20020a05622a1a0b00b003e63af1de7amr983591qtb.4.1681427532080; Thu, 13 Apr 2023 16:12:12 -0700 (PDT) X-Google-Smtp-Source: AKy350YlByEGJ4UdGFBHdGvGYtpq6JVyaUGtX1ideR+y+TXFaHJemqbrxo5x6y9sTphL6VxiZhrbGw== X-Received: by 2002:a05:622a:1a0b:b0:3e6:3af1:de7a with SMTP id f11-20020a05622a1a0b00b003e63af1de7amr983544qtb.4.1681427531653; Thu, 13 Apr 2023 16:12:11 -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 p26-20020ac8741a000000b003dd8ad765dcsm814463qtq.76.2023.04.13.16.12.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 13 Apr 2023 16:12:11 -0700 (PDT) From: Peter Xu To: linux-mm@kvack.org, linux-kernel@vger.kernel.org Cc: Nadav Amit , peterx@redhat.com, Mike Kravetz , Andrew Morton , Andrea Arcangeli , Axel Rasmussen , David Hildenbrand Subject: [PATCH 6/6] selftests/mm: Add tests for RO pinning vs fork() Date: Thu, 13 Apr 2023 19:12:09 -0400 Message-Id: <20230413231209.544825-1-peterx@redhat.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230413231120.544685-1-peterx@redhat.com> References: <20230413231120.544685-1-peterx@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Rspamd-Server: rspam07 X-Rspamd-Queue-Id: 6D5BA4000A X-Rspam-User: X-Stat-Signature: wy4dzwkzqamfyf7pz5omni1smdez1nk9 X-HE-Tag: 1681427536-750624 X-HE-Meta: U2FsdGVkX18atyOGKQEzOpTEvBuT8177e5kE82OWMVnj9rBvDnp78cgVDXxgQgHhrV+gupQdDA/uwfYMmqhJV6ap6MZlz0vqqO1IogEBfaNrkR2Odj0lzhsFuyvGHnKLPs2JEWO2lSRd1DCrlZtp0jB7qn0tRd3t/dcmXx+J7lSOqyrU0UoiAvID4Ji17SvAkSxQ2+5ovuFozNs0e1a5iS8BggEi6tQXb5lJj6HdKbaNCB8BhAGUizRFZH4JHk4rXSFFXXNujh8NrZija1RZzwaTNm6Xtlawegj7/J8cjWuaDydyrsbADJFnvi8K2t3fdTzyzhlh1JsBl73pEs/hwszUeDbfnAI6MdWQWnrvwiS4YIe+Y+Y4tfvqhrgm6xrMeHu3dItJ+L9kkPWkw43tpH7Okm+q84HUj1NOqV1rnFNSJOCx5OLAWG33AYoiUQWpSI1B6ga0Q5oAADxfBtf7jDC6PgxTOF1E5Js1mDyefroM1ihpLMcqxlSodtvasejtTu9DllZ/gfIvkLOOV2cdmcEsdxWLWlL3j2S7tTIHR856q1vgLxspiUUYGWtORgjshTaTARjebhQISRu+dk7PvK5pVcFOEuY6f3GxCGiolwQ6RgtqX3rlM0fUL2nzjclpAWWbxJPw983tz8AsCT5TJKoFcBIhUl285y2tLhbDT0aymmKbMzvbLgBmk1vWQPjYxnREGyMfQK7MQZQXoyoLSMVbWw0f4RqmzTbJsHJ4Ttpo4rohp+ubgCg6AaV4JAuTXAz1Of2kPD7mx7pzVe8EeO582VGhkqzovAtGFPDzUDZQ1238nW+xzJEdK7a28w7+aB+Rc4uKP8mZEbGovDSTU6gPRDUwbrBpaKZuhmB9dpQX8D6UoWouXx4x+o9MwgmKNZ9ZyX9TdzWg+XG8J677jMDxIEAAxAeDG7KpSIckbtNz9I+mW8ypAwRerp6qVdY/56K3jw4M0IHeP95S5Rc kF1kdhik pxfFryOma4HXhf03R8LF++9WG0PBK8vnM3Dr3t20HBCr21j0A0vQdbUQMmarHFLkA6cqgM2YDb1X1qxdN63igEDNCsVq27aqorRJiZv4McIM79zVMejulTdtPTbvmZl3pFyeJcwsINsTclSq4Z9YVNv3u6dNQmHGtw3nAlwbZILEY4kb6Mt8VCK4PgQ7hJ1XGc6sTOhHHKrLVI7q5sroVZMkOBK5iuTpbKDEAXrcBA6+QSVUp+ELZdSTo5IXZb78Rsxw0aKraxg4a6wkRHUBZkiaXrqKRUS1JDUPyluXwJlDnD2ylbG/5A6/UQHS+sKP8sM21+SYV43vWd8B2GRPV2ch1Qg2ttmp0UKawMDcXKZPQdEA4wwDypaaREm4eekBrxbvmn616dNC4cn3ye2Au5A7Xj91LOlTjxqKOxjqQRk0EZ5Ya2L5ZSDlXojRfBE5Ho5GRUjIP3aXAkjdQOEw7a7ojK8CjCv6DU9dX 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 10 one more test 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 only private pages matter here, but no hurt to also run all of them over shared. 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,