From patchwork Fri Apr 28 00:41:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiaqi Yan X-Patchwork-Id: 13225920 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 5B923C77B7C for ; Fri, 28 Apr 2023 00:42:00 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 17F216B007D; Thu, 27 Apr 2023 20:41:58 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 042DA6B007E; Thu, 27 Apr 2023 20:41:57 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id E73B36B0080; Thu, 27 Apr 2023 20:41:57 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0016.hostedemail.com [216.40.44.16]) by kanga.kvack.org (Postfix) with ESMTP id D16426B007D for ; Thu, 27 Apr 2023 20:41:57 -0400 (EDT) Received: from smtpin02.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay02.hostedemail.com (Postfix) with ESMTP id 9A5481203CA for ; Fri, 28 Apr 2023 00:41:57 +0000 (UTC) X-FDA: 80728947474.02.AA3B145 Received: from mail-yb1-f201.google.com (mail-yb1-f201.google.com [209.85.219.201]) by imf12.hostedemail.com (Postfix) with ESMTP id E460D4000B for ; Fri, 28 Apr 2023 00:41:55 +0000 (UTC) Authentication-Results: imf12.hostedemail.com; dkim=pass header.d=google.com header.s=20221208 header.b=lkIUSE2Y; spf=pass (imf12.hostedemail.com: domain of 3UxZLZAgKCNQ980G8O0D6EE6B4.2ECB8DKN-CCAL02A.EH6@flex--jiaqiyan.bounces.google.com designates 209.85.219.201 as permitted sender) smtp.mailfrom=3UxZLZAgKCNQ980G8O0D6EE6B4.2ECB8DKN-CCAL02A.EH6@flex--jiaqiyan.bounces.google.com; dmarc=pass (policy=reject) header.from=google.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1682642515; 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: in-reply-to:in-reply-to:references:references:dkim-signature; bh=83gQugE5cbaf4bRjalHdDmK4/joJvX/FU1hRUwFi3sU=; b=FkI35OBwySm2ef/JnDBHEzqp0DnTdDdUVc0cyQhVnR3+fYMwPd2YekGZJ51objT6UiUt/r Tj7B04U4GwOU5x+2MH/r2uh8CnthG0bHxOj2hh+FIVJHZNMoKdZKZqxyZCrJbJBGLfbdHT OFtgXjHxEkoz6nW5MqUDLk4MlwxIlLA= ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1682642515; a=rsa-sha256; cv=none; b=XNhZ8rEtslIwFG2+e45H+uyf+BBYR/0JWrqD8pJgB/IYhkk+atfdkCHFkgUdWWJxHlRwkA e7O+P/zbAMubfAmSJuoKOmytDpkwoTpipB76iQDWoJZSnRz6qLWOt3I2Ab13TYeCMTUFcn b5GCPuA+9C2yQ3vGjmuBFe36CiEh7ug= ARC-Authentication-Results: i=1; imf12.hostedemail.com; dkim=pass header.d=google.com header.s=20221208 header.b=lkIUSE2Y; spf=pass (imf12.hostedemail.com: domain of 3UxZLZAgKCNQ980G8O0D6EE6B4.2ECB8DKN-CCAL02A.EH6@flex--jiaqiyan.bounces.google.com designates 209.85.219.201 as permitted sender) smtp.mailfrom=3UxZLZAgKCNQ980G8O0D6EE6B4.2ECB8DKN-CCAL02A.EH6@flex--jiaqiyan.bounces.google.com; dmarc=pass (policy=reject) header.from=google.com Received: by mail-yb1-f201.google.com with SMTP id 3f1490d57ef6-b9a2abd8f7bso5532641276.2 for ; Thu, 27 Apr 2023 17:41:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1682642515; x=1685234515; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=83gQugE5cbaf4bRjalHdDmK4/joJvX/FU1hRUwFi3sU=; b=lkIUSE2Y29UDUiwiH/PXq03j0d2aozkiWLLGwRUxJKBzv349gzdyhvqftmJ2Fu62zY jMWxszNhpeRS5y3kImN5n2opTgBWFzDwuqKRzgYyjj6M0n66z7iMvb+tEcCceMdcsBOp Yeb8sup8/qzQ53OohH3ZK9C3z9mQqNbOrxs+PHi1sWeypkY3IZhF0GeGg2khyzhtLKeV DWO5IYJmpJdERF9S5Pot3hgD65WiP9Z2x3emz7ewMNj79q9jZZcAwhpTWXAelkXAdsUG qsrxPQRSj6bArSMm1jg0g9gbmM8D0ht2i0W9PUL5DphQLDMfi4v6IBF2iF6Uhh+G6NXh 45Zw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1682642515; x=1685234515; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=83gQugE5cbaf4bRjalHdDmK4/joJvX/FU1hRUwFi3sU=; b=jxZKK3LWZgMMZtpPCoHYiuG/kNg0+ZlePbelHPxNXY+Z6otQ5qEAND28a408bQwe21 ufdyt03QxY3uKPZ3xNoSJj5wKvPqVro9TDZXZNn1vDfczzYbVuAS8daQbE8VLxM3nCMC 5XM+KZLevd4MJoLxnjiaeSZdS6OMZ9vz1rqsHZ6Rz3Rs8Hm82wd1bph76Yqs0CfVPRFd c5Yt7lBl2weWuANeIo1FS3TJpOJ4pAKUzuE01jrO0XE63F3SKlCZsL2lYJWmzJ3yX7y8 nssLqdFjQeY7Ud/p8ziPDr7ag/3SF26f/1NhDGow4GEvF6jO2L/xlotZJUO0xdOm6xd8 aeQA== X-Gm-Message-State: AC+VfDwc4DyWVwdHUY119UYdGCt1K69DRe1/BV4jGMGi6K4wCbC/Kv4M +6FXyrExoXpuaU2gT527yqPwdmgnZ/3mZg== X-Google-Smtp-Source: ACHHUZ7xVswVxMlAbuXM0D1pvugkyEcjJjFYRWa+KqmIhde44CvOq6FK0bhBjRxTVvPgtVl4zq6KC5eExxNjOg== X-Received: from yjq3.c.googlers.com ([fda3:e722:ac3:cc00:24:72f4:c0a8:272f]) (user=jiaqiyan job=sendgmr) by 2002:a25:c041:0:b0:b96:5b8a:3c34 with SMTP id c62-20020a25c041000000b00b965b8a3c34mr1217514ybf.11.1682642515107; Thu, 27 Apr 2023 17:41:55 -0700 (PDT) Date: Fri, 28 Apr 2023 00:41:38 +0000 In-Reply-To: <20230428004139.2899856-1-jiaqiyan@google.com> Mime-Version: 1.0 References: <20230428004139.2899856-1-jiaqiyan@google.com> X-Mailer: git-send-email 2.40.1.495.gc816e09b53d-goog Message-ID: <20230428004139.2899856-7-jiaqiyan@google.com> Subject: [RFC PATCH v1 6/7] selftest/mm: test PAGESIZE unmapping HWPOISON pages From: Jiaqi Yan To: mike.kravetz@oracle.com, peterx@redhat.com, naoya.horiguchi@nec.com Cc: songmuchun@bytedance.com, duenwen@google.com, axelrasmussen@google.com, jthoughton@google.com, rientjes@google.com, linmiaohe@huawei.com, shy828301@gmail.com, baolin.wang@linux.alibaba.com, wangkefeng.wang@huawei.com, akpm@linux-foundation.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Jiaqi Yan X-Rspam-User: X-Rspamd-Queue-Id: E460D4000B X-Rspamd-Server: rspam09 X-Stat-Signature: p5duudewzacqcnduuy5exuwguzbzwmbg X-HE-Tag: 1682642515-660481 X-HE-Meta: U2FsdGVkX1+WdfPlBCE8juoL3VL0xrOY1rJSh5EpsOOzIJrisQd2s6jGBZisupXhJEzxvtORwwM6E3oPYO1TQbZncLJFBl/Ynt+Dyw9nkRJiUsYS9kB6sOA8GshMzgNJT3mROoC/zd98ZP1Y7JigxogyOaxsG//NsumQ9CWfXUhH6eHregRhg9ul9R7TPaAea5tl2vPjgKUz8eDO7MII91jNeEjLi5ZWAe+k1Z/QAPEOJvpmMONXoTvrAYdE69hSkglnRNC6lmGvRwJrXWPGCBFpW3fYaDOAVYKsoIIxgrnBYwfoCXNy/scrwxwez3L8QmpweENl9OaXJLV3gQscumLtz5ckYAMXRdkrlRfUxfqWc2TzX74D56MaWVFOlYemGKNNeM6PSrUCay09RN96GRoU9wYrZ2XfhKeb/DldE/1/zTwdxptvGtvyuF09vRjE/acu3d2qRIntkZDFTIYwLDo5YlT8ZRkNDycvEOYCTOt14AJaW7RHiNWPWfvbXOsnKp9lb2MqgLj8SJ/ID6ht8OWiFu9+WSJ4UOdwTSWM1cNvUYmgQ2swMZ8qvKUhmn+YHto2u7lfonKHUWjIzXIU9H2PUoQjq+JUe2RQ4TTb0SLBetO6JilD9GuTgaRqdDp2OIlTMNiJSpVNdXRE0Uk4DQLZCm42eY12sHjTbGt2j/wuqV3kQQJHevGvl/BjwdAe/Js7UQk8et5RoIXc3EH3qJzuIi85ij2rasT3jTsydZSO8jVjJ6SyNFnqxqw+5pI2XRlitioF2eu7WaDiTA4hhEyQYWVQP9icirUyBabfPGStQHAtUbU6fwy8SNsay4CJvJluil+8fLch0ldMvupz78SjwWRLx0L45B0HEYeCuRHZpdjnbtxfhld92D+k7D8VCXNoKjSHlp40b3rLOaXDxN3esP7J6f6uJR0JLo7hfEDOQWGnliIn59QP1UrfZT4r0XJz4qDmuoF3rpnO/pV M1WKfA5Y yuLY7Zi3XwVLrVsuMHOMnVPr0SmkI2UhyPLBTXMscTyfvE5YcmTvkExu9AQ37DkTHlzqfCwh6ORzzXxLFFlahNGHeGSSUyUXjv1TWo5IZsQnxKMGfvDkF/KxuYfcUsN1tjKI31u8gUvKL9RZhbaAzWjmDQl788fwh01es4dVYVCcGgPAXgONeZ7vRtKtYU/VtCwXkvZKmhjx9E+ovQW5V65ozcsGgFgD+rLKbDe1FbsRHKA24EnmIleyda3BD6aQewTbiMT6CrpToPryTvf+AC5KZRym96LApn8XpDPsDwfyxxxMw7xWNM++/qfBQQTQv+XxJwe5WcpORpZZ0wNRIE7Y4/VfN2FwJqzA2FLLBL3uNs3rAeLBd1S4lmAmI04+odE/WabkcgJ8GVz+I325t32Yo6QOSV3BVCTvocV5a+XJX5UZ5RXptImfa3s0WCUgHQMhYS7sbZLhZtf5zTbAhlL7uriQ+l/gZuLMI0HPg3OB17d01Wzc9F5OXekXAfqI6rgN+59XlZr6qLlUakmhDWdKT98AExqNMNdJIxzTM8hHLcH4BX9cWzkj2uZxeCZlB2lUi6hJv/0yc8YfGOin1Ri2HrEV/iszC5BYDgXz0TDaMpcSWiBO+/Fvcnw== 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: After injecting memory errors to byte addresses inside HugeTLB page, the updated test checks 1. only a raw page is unmapped, and userspace gets correct SIGBUS from kernel. 2. other subpages in the same hugepage are still mapped and data not corrupted. Signed-off-by: Jiaqi Yan --- tools/testing/selftests/mm/hugetlb-hgm.c | 194 +++++++++++++++++++---- 1 file changed, 167 insertions(+), 27 deletions(-) diff --git a/tools/testing/selftests/mm/hugetlb-hgm.c b/tools/testing/selftests/mm/hugetlb-hgm.c index c0ba6ad44005..bc9529986b66 100644 --- a/tools/testing/selftests/mm/hugetlb-hgm.c +++ b/tools/testing/selftests/mm/hugetlb-hgm.c @@ -39,6 +39,10 @@ #define MADV_SPLIT 26 #endif +#ifndef NUM_HWPOISON_PAGES +#define NUM_HWPOISON_PAGES 3UL +#endif + #define PREFIX " ... " #define ERROR_PREFIX " !!! " @@ -241,6 +245,9 @@ static int test_sigbus(char *addr, bool poison) sigbus_addr, addr); else if (poison && !was_mceerr) printf(ERROR_PREFIX "didn't get an MCEERR?\n"); + else if (!poison && was_mceerr) + printf(ERROR_PREFIX "got BUS_MCEERR_AR sigbus on expected healthy address: %p\n", + sigbus_addr); else ret = 0; out: @@ -272,43 +279,176 @@ static int read_event_from_uffd(int *uffd, pthread_t *pthread) return 0; } -static int test_sigbus_range(char *primary_map, size_t len, bool hwpoison) +struct range_exclude_pages { + /* Starting address of the buffer. */ + char *mapping; + /* Length of the buffer in bytes. */ + size_t length; + /* The value that each byte in buffer should equal to. */ + char value; + /* + * PAGESIZE aligned addresses excluded from the checking, + * e.g. if PAGE_SIZE=4k, for each addr in excludes, + * skips checking on [addr, addr + 4096). + */ + unsigned long excluded[NUM_HWPOISON_PAGES]; +}; + +static int check_range_exclude_pages(struct range_exclude_pages *range) +{ + const unsigned long pagesize = getpagesize(); + unsigned long excluded_index; + unsigned long page_index; + bool should_skip; + size_t i = 0; + size_t j = 0; + + while (i < range->length) { + page_index = ((unsigned long)(range->mapping + i)) / pagesize; + should_skip = false; + for (j = 0; j < NUM_HWPOISON_PAGES; ++j) { + excluded_index = range->excluded[j] / pagesize; + if (page_index == excluded_index) { + should_skip = true; + break; + } + } + if (should_skip) { + printf(PREFIX "skip excluded addr range [%#lx, %#lx)\n", + (unsigned long)(range->mapping + i), + (unsigned long)(range->mapping + i + pagesize)); + i += pagesize; + continue; + } + if (range->mapping[i] != range->value) { + printf(ERROR_PREFIX "mismatch at %p (%d != %d)\n", + &range->mapping[i], range->mapping[i], range->value); + return -1; + } + ++i; + } + + return 0; +} + +enum test_status verify_raw_pages(char *map, size_t len, + unsigned long excluded[NUM_HWPOISON_PAGES]) { const unsigned long pagesize = getpagesize(); - const int num_checks = 512; - unsigned long bytes_per_check = len/num_checks; - int i; + unsigned long size, offset, value; + size_t j = 0; + + for (size = len / 2, offset = 0, value = 1; size > pagesize; + offset += size, size /= 2, ++value) { + struct range_exclude_pages range = { + .mapping = map + offset, + .length = size, + .value = value, + }; + for (j = 0; j < NUM_HWPOISON_PAGES; ++j) + range.excluded[j] = excluded[j]; + + printf(PREFIX "checking non-poisoned range [%p, %p) " + "(len=%#lx) per-byte value=%lu\n", + range.mapping, range.mapping + range.length, + range.length, value); + if (check_range_exclude_pages(&range)) + return TEST_FAILED; + + printf(PREFIX PREFIX "good\n"); + } - printf(PREFIX "checking that we can't access " - "(%d addresses within %p -> %p)\n", - num_checks, primary_map, primary_map + len); + return TEST_PASSED; +} - if (pagesize > bytes_per_check) - bytes_per_check = pagesize; +static int read_hwpoison_pages(unsigned long *nr_hwp_pages) +{ + const unsigned long pagesize = getpagesize(); + char buffer[256] = {0}; + char *cmd = "cat /proc/meminfo | grep -i HardwareCorrupted | grep -o '[0-9]*'"; + FILE *cmdfile = popen(cmd, "r"); - for (i = 0; i < len; i += bytes_per_check) - if (test_sigbus(primary_map + i, hwpoison) < 0) - return 1; - /* check very last byte, because we left it unmapped */ - if (test_sigbus(primary_map + len - 1, hwpoison)) - return 1; + if (!(fgets(buffer, sizeof(buffer), cmdfile))) { + perror("failed to read HardwareCorrupted from /proc/meminfo\n"); + return -1; + } + pclose(cmdfile); + *nr_hwp_pages = atoll(buffer) * 1024 / pagesize; return 0; } -static enum test_status test_hwpoison(char *primary_map, size_t len) +static enum test_status test_hwpoison_one_raw_page(char *hwpoison_addr) { - printf(PREFIX "poisoning %p -> %p\n", primary_map, primary_map + len); - if (madvise(primary_map, len, MADV_HWPOISON) < 0) { + const unsigned long pagesize = getpagesize(); + + printf(PREFIX "poisoning [%p, %p) (len=%#lx)\n", + hwpoison_addr, hwpoison_addr + pagesize, pagesize); + if (madvise(hwpoison_addr, pagesize, MADV_HWPOISON) < 0) { perror(ERROR_PREFIX "MADV_HWPOISON failed"); return TEST_SKIPPED; } - return test_sigbus_range(primary_map, len, true) - ? TEST_FAILED : TEST_PASSED; + printf(PREFIX "checking poisoned range [%p, %p) (len=%#lx)\n", + hwpoison_addr, hwpoison_addr + pagesize, pagesize); + if (test_sigbus(hwpoison_addr, true) < 0) + return TEST_FAILED; + + return TEST_PASSED; } -static int test_fork(int uffd, char *primary_map, size_t len) +static enum test_status test_hwpoison_present(char *map, size_t len, + bool already_injected) +{ + const unsigned long pagesize = getpagesize(); + const unsigned long hwpoison_next = 128; + unsigned long nr_hwpoison_pages_before, nr_hwpoison_pages_after; + enum test_status ret; + size_t i; + char *hwpoison_addr = map; + unsigned long hwpoison_addrs[NUM_HWPOISON_PAGES]; + + if (hwpoison_next * (NUM_HWPOISON_PAGES - 1) >= (len / pagesize)) { + printf(ERROR_PREFIX "max hwpoison_addr out of range"); + return TEST_SKIPPED; + } + + for (i = 0; i < NUM_HWPOISON_PAGES; ++i) { + hwpoison_addrs[i] = (unsigned long)hwpoison_addr; + hwpoison_addr += hwpoison_next * pagesize; + } + + if (already_injected) + return verify_raw_pages(map, len, hwpoison_addrs); + + if (read_hwpoison_pages(&nr_hwpoison_pages_before)) { + printf(ERROR_PREFIX "check #HWPOISON pages\n"); + return TEST_SKIPPED; + } + printf(PREFIX "Before injections, #HWPOISON pages = %ld\n", nr_hwpoison_pages_before); + + for (i = 0; i < NUM_HWPOISON_PAGES; ++i) { + ret = test_hwpoison_one_raw_page((char *)hwpoison_addrs[i]); + if (ret != TEST_PASSED) + return ret; + } + + if (read_hwpoison_pages(&nr_hwpoison_pages_after)) { + printf(ERROR_PREFIX "check #HWPOISON pages\n"); + return TEST_SKIPPED; + } + printf(PREFIX "After injections, #HWPOISON pages = %ld\n", nr_hwpoison_pages_after); + + if (nr_hwpoison_pages_after - nr_hwpoison_pages_before != NUM_HWPOISON_PAGES) { + printf(ERROR_PREFIX "delta #HWPOISON pages != %ld", + NUM_HWPOISON_PAGES); + return TEST_FAILED; + } + + return verify_raw_pages(map, len, hwpoison_addrs); +} + +int test_fork(int uffd, char *primary_map, size_t len) { int status; int ret = 0; @@ -360,7 +500,6 @@ static int test_fork(int uffd, char *primary_map, size_t len) pthread_join(uffd_thd, NULL); return ret; - } static int uffd_register(int uffd, char *primary_map, unsigned long len, @@ -394,6 +533,7 @@ test_hgm(int fd, size_t hugepagesize, size_t len, enum test_type type) bool uffd_wp = type == TEST_UFFDWP; bool verify = type == TEST_DEFAULT; int register_args; + enum test_status hwp_status = TEST_SKIPPED; if (ftruncate(fd, len) < 0) { perror(ERROR_PREFIX "ftruncate failed"); @@ -489,10 +629,10 @@ test_hgm(int fd, size_t hugepagesize, size_t len, enum test_type type) * mapping. */ if (hwpoison) { - enum test_status new_status = test_hwpoison(primary_map, len); - - if (new_status != TEST_PASSED) { - status = new_status; + /* test_hwpoison can fail with TEST_SKIPPED. */ + hwp_status = test_hwpoison_present(primary_map, len, false); + if (hwp_status != TEST_PASSED) { + status = hwp_status; goto done; } } @@ -539,7 +679,7 @@ test_hgm(int fd, size_t hugepagesize, size_t len, enum test_type type) /* * Verify that memory is still poisoned. */ - if (hwpoison && test_sigbus_range(primary_map, len, true)) + if (hwpoison && test_hwpoison_present(primary_map, len, true)) goto done; status = TEST_PASSED;