From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from fout-a1-smtp.messagingengine.com (fout-a1-smtp.messagingengine.com [103.168.172.144]) (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 A4FCE41CB39; Sun, 16 Aug 2026 22:48:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.168.172.144 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786920483; cv=none; b=XE9kGvic00UGjPwMWF9TfdXcYCpJ+D1GKojoXQmItIWjCsKDvGZKEkWKnnB7TQ3XHxVnUE/s3lZTOChSqKWxYxAwBoCtRQr1/hiudUtavjW2bRmhpzy2NrhOXB5mOk/B5zi0fXK4g9A/wHmvzKuXmZolj1jPZq3sSgxpZEwVlPs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786920483; c=relaxed/simple; bh=fx4uMDLnQu7YiIWADrslnkPRqCeym9piQn5W0aKziC4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d6AgVunN4oTbUrBAOpH65D0jIsFXZjwoFKCPuNUMdwzTZmHd5rs5y0Vh1Qt+CdGDZudD0tOOqc32CVoFQL5rrv5Gln5rncj1NE5C4xMhaz3vEyaffrsCaVBAol5yRE9/UhTOaAyeo6OyhUXoZf6JmsXNdYwmYgPhB9XDUf3eyUw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=shutemov.name; spf=pass smtp.mailfrom=shutemov.name; dkim=pass (2048-bit key) header.d=shutemov.name header.i=@shutemov.name header.b=Q1nl18jy; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b=HBZrNw5X; arc=none smtp.client-ip=103.168.172.144 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=shutemov.name Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=shutemov.name Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=shutemov.name header.i=@shutemov.name header.b="Q1nl18jy"; dkim=pass (2048-bit key) header.d=messagingengine.com header.i=@messagingengine.com header.b="HBZrNw5X" Received: from phl-compute-05.internal (phl-compute-05.internal [10.202.2.45]) by mailfout.phl.internal (Postfix) with ESMTP id 080F7EC0242; Sun, 16 Aug 2026 18:48:01 -0400 (EDT) Received: from phl-frontend-04 ([10.202.2.163]) by phl-compute-05.internal (MEProxy); Sun, 16 Aug 2026 18:48:01 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=shutemov.name; h=cc:cc:content-transfer-encoding:content-type:date:date:from :from:in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm1; t=1786920481; x= 1787006881; bh=GxeIcugRbXVZfwL3UIFb4N2+J7ON7LziwuBbRMyDD8k=; b=Q 1nl18jyTLYzI3yx4j+jwVY/KO10w2lM3QYtsyjfOG4YOYBwTTnXPm4LRNOpGJ7DC dmKxYGXrkLCioU5z597fuaWe1KK1SoBHdOqe8jVVxx8MR5EmpfiZNAoZN4ptk6Do AKXYSDxDlff8PElQJPNGo9Gwn5a5iZywwlOUcAyZ4qnWz5NmpDoicDuH9UgfEJKu oiDVD5rHzcOmcKWYCWbFK1KMpMgUEKeMjzMZzKOtq5veaoK8uWxWeGfaDkyJ99Bt eGtKMBK+kMBbdqMD4Gydwilbq39tES6G7BP86WARBy1JZkAP/jnsRdituYma7+ac lvecsLUJu69i55sDnPcPA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-transfer-encoding :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1786920481; x=1787006881; bh=G xeIcugRbXVZfwL3UIFb4N2+J7ON7LziwuBbRMyDD8k=; b=HBZrNw5XGXWsoSjY/ l2Ty9l7Y2HQdxrIGH/GmBuDYsahOR7E3ZhJ7jrSBaXi6PaevxI/Qf6bG5rxg+k3K 3UvOZ5FlVYhUKt8uwxUk/RSSKVXcVEz01RWIOyi1e2oSV9DwG6jdLF4CFvyFXKGf fiOCfCaV0iuAw9DbMeMee4rZHJ79Do9HuXLHLTBAzscPu8eXBw+dHBbSq+8GQj8q 5PqkmoLrGd3iwwReLQvYbgkfL1D5f+nq/be1sa9A8Fwi/pdDNVC5hAkCmeOVJ2wl iC4dGym4EQV4Gf/TD9kygnf/BmERNW97vZnmtxLZufRwGB+cbK2zMGe+dliAX3uP bo9ug== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: dmFkZTGU6CsvhMdBtF1sno4coJRY9LqZz27w5X62/KcvcmaHPoOW0E1BwYMGQvImxADkeF Vrd/NWvQclh95CfVK4mxB+x5IwibSBkynFlxvp5GF5/z4jepOu57IyLHUvB8DzXymwpMun cyoWwWLP4hjAaD7bSTsoDmYKe03ryBERSg0IuR8ZqJolHrsaFnqWf5mL2byVsL9qLL9zPI 9yLFlvwwZlCiJbT4IVFlRVV/9A2lZNuDFrGWvpQWw3cjqX9EcFrpnMQcn/0MdQygQWXSYC yXHZy2pRvgBssh5qObsL1GM1XSEE6m2uHX2m5CwJlvTSIfOodU1appS1XFI/CBVlhtQ7bu WKduOy5KF3rkObzFMiEUvs0Uj+LfiCh6TivtFsv9ZCNMbNZlGDBoD70ARSLp0XqawKgcWO 4RiWTFPOoz+vIHDroCYLvSEQINMB4dtDTXJ4bPtSoOC1A7Uuzl+tv2HVHx+vABiXweeL5Y B3sGlUM6WH7yS7YE4BSgR6uka3P965uA8/oqj2x+5VFy/Ho1o+YZlJ6AzrIDQ2AOtpBe1f oON1he5PaB+Gj11+twxr/dYpMSHdLv7yT7mnf/dci59b0UPnqF9iMScbflIoYgvrHP20+G HvUS/ei21AytaZzWxcEuOCYs0kXcHbXn/msGaoTEwH1OUh7FsC6dL8NQXoJg X-ME-Proxy: Feedback-ID: ie3994620:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Sun, 16 Aug 2026 18:48:00 -0400 (EDT) From: Kiryl Shutsemau To: akpm@linux-foundation.org, david@kernel.org, ljs@kernel.org, nico.pache@linux.dev Cc: baolin.wang@linux.alibaba.com, baohua@kernel.org, dev.jain@arm.com, hughd@google.com, lance.yang@linux.dev, liam@infradead.org, mhocko@suse.com, rppt@kernel.org, ryan.roberts@arm.com, shuah@kernel.org, surenb@google.com, usama.arif@linux.dev, vbabka@kernel.org, ziy@nvidia.com, usama.anjum@arm.com, agordeev@linux.ibm.com, linux-mm@kvack.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, kas@kernel.org, jannh@google.com, willy@infradead.org, pfalcato@suse.de, rostedt@goodmis.org, mhiramat@kernel.org, linux-trace-kernel@vger.kernel.org, bpf@vger.kernel.org Subject: [RFC PATCH 56/57] selftests/mm: cover the scaled max_ptes_shared limit Date: Sun, 16 Aug 2026 23:46:08 +0100 Message-ID: <20260816224609.308019-57-kirill@shutemov.name> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260816224609.308019-1-kirill@shutemov.name> References: <20260816224609.308019-1-kirill@shutemov.name> Precedence: bulk X-Mailing-List: linux-trace-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: "Kiryl Shutsemau (Meta)" max_ptes_shared is written in PTEs of a whole PMD, but a range smaller than a PMD is scanned in full and judged by the same setting. Compared raw the limit is then unreachable: on arm64 with 64K base pages a 2M range holds 32 PTEs and can never exceed a 4096-PTE budget. So it is scaled to what was actually scanned, and what decides is the shared fraction rather than the count. Cover both directions in one sub-PMD range, with a forked child holding the sources shared. One PTE past the scaled budget: nothing in the range collapses. Break CoW on one more page, bringing it inside: it collapses. The counts come from the current max_ptes_shared rather than being hardcoded, so the case follows the setting and the order it runs at. Without the scaling the first half passes wrongly: a few dozen shared PTEs never reach a limit expressed in PMD units, so the range collapses when it should not. Assisted-by: Claude-Code:claude-opus-5 Signed-off-by: Kiryl Shutsemau (Meta) --- tools/testing/selftests/mm/khugepaged.c | 85 +++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index bd684cd25fed..dde24756a3a2 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -1791,6 +1791,90 @@ static void collapse_order_pinned_window(struct collapse_context *c, ksft_test_result_report(exit_status, "%s\n", __func__); } +/* + * max_ptes_shared counts PTEs of a whole PMD, but a VMA smaller than one is + * scanned in full and judged against the same setting, so the limit is scaled + * to the range actually scanned: what decides is the shared *fraction*, not + * the raw count. An unscaled comparison against HPAGE_PMD_NR/2 could never + * refuse a range this small, so both directions are checked here. + */ +static void collapse_order_sub_pmd_shared(struct collapse_context *c, + struct mem_ops *ops) +{ + size_t window = mthp_window_size(); + size_t size = 4 * window; + unsigned long nr_ptes = size / page_size; + unsigned long budget, cow; + int max_shared, wstatus; + void *p; + + /* + * The range has to stay strictly below a PMD to say anything about the + * scaling: at exactly one PMD the scaled limit is the raw one, and the + * case would pass without testing what it is here for. + */ + if (size >= hpage_pmd_size) { + ksft_test_result_skip("%s: four windows do not fit below the PMD\n", + __func__); + return; + } + + max_shared = thp_read_num("khugepaged/max_ptes_shared"); + /* The same fraction of this range as max_shared is of a PMD. */ + budget = (unsigned long)max_shared * nr_ptes / hpage_pmd_nr; + if (budget + 1 > nr_ptes) { + ksft_test_result_skip("%s: max_ptes_shared leaves nothing to exceed\n", + __func__); + return; + } + + mthp_push_target_order(); + + p = mmap(BASE_ADDR, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p != BASE_ADDR) + ksft_exit_fail_msg("Failed to allocate VMA at %p\n", BASE_ADDR); + fill_memory(p, 0, size); + madvise(p, size, MADV_HUGEPAGE); + + if (!fork()) { + /* + * Everything is shared with the parent now. Break CoW on all + * but budget + 1 PTEs: one PTE over the scaled limit, and far + * below the unscaled one. + */ + cow = nr_ptes - budget - 1; + fill_memory(p, 0, cow * page_size); + ksft_print_msg("Refuse a sub-PMD range over the scaled max_ptes_shared..."); + if (!khugepaged_wait_full_pass()) + fail("Timeout"); + else if (window_not_collapsed(p, size)) + success("OK"); + else + fail("Fail"); + + /* One fewer shared PTE brings it back within the limit. */ + fill_memory(p, cow * page_size, (cow + 1) * page_size); + ksft_print_msg("Collapse once inside it..."); + if (!khugepaged_wait_full_pass()) + fail("Timeout"); + else if (window_collapsed(p, size)) + success("OK"); + else + fail("Fail"); + + validate_memory(p, 0, size); + _exit(exit_status); + } + wait(&wstatus); + if (WEXITSTATUS(wstatus)) + exit_status = WEXITSTATUS(wstatus); + + munmap(p, size); + thp_pop_settings(); + ksft_test_result_report(exit_status, "%s\n", __func__); +} + static void usage(void) { fprintf(stderr, "\nUsage: ./khugepaged [OPTIONS] [dir]\n\n"); @@ -2084,6 +2168,7 @@ int main(int argc, char **argv) TEST(collapse_order_mlocked, mthp_khugepaged_context, anon_ops); TEST(collapse_order_lazyfree_window, mthp_khugepaged_context, anon_ops); TEST(collapse_order_pinned_window, mthp_khugepaged_context, anon_ops); + TEST(collapse_order_sub_pmd_shared, mthp_khugepaged_context, anon_ops); } TEST(collapse_full, madvise_context, anon_ops); -- 2.54.0