From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 50391309DDB for ; Fri, 17 Jul 2026 13:37:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784295439; cv=none; b=eHlbvnwZvaVOMiZxErc9vKmzoJSkdUxvzaSbbuFnTi4+jgq+eYPFEs5m9LqI9XWskPm81I8NC5L7ECpBbfSXjS4srclo1n9daJ9ywJYXu0GCa7uhwM5RAD9q8c5uzHHPJclXtju0eS3Wl3BehLKs1r7X9twt4h77ms7he9zVd2s= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784295439; c=relaxed/simple; bh=hkJZi/CaXKcH9FFt8iYCKP9oKLu5eG5nKQ5ZLeIz9U8=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=gDMkCLel4oEAPa0/oRl3cW8aPQogoWJ8Ih09I2PPrKsg/E5OS30DdLEpm/VvDS6ql1hN7bA+uUiFUU+F6EkbeCRxAOon2S+AkO4jTgdVuHZKaPSBpgvpShIfeBgIa6JWYtlFGkUVunDjkDWs+dIS4Zq6BzaNJiyv/7K7S4JgxZI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=B1JoYE6/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="B1JoYE6/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 00E971F000E9; Fri, 17 Jul 2026 13:37:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784295435; bh=48oZHUU9fg5G11xfD8S7BpPTRgtHVfmfE+3MIs4yOlk=; h=Date:From:To:Cc:Subject:References:In-Reply-To; b=B1JoYE6/cI4a/HhoKKJga2jG7TaHxlh9BO31Wzd30QRDmAUUuhdDKaPO+KQFmDzGr UaNLtY+uvL6eeiYZzlfq9kTGRTmM7qeAyAMmS4OoUl2E9nU3jbUmQj9XPkYqkAVyuo C9sro9NMD1RdviV5z3fvQ3S5fzciOS0kBPplCpW7Nq2/b5MCeT8tlVzP4kMBFMJgkY zy+8jRm0+WjeR1+nsuVXPelT/mHEe6AZ91AG3hF7UgDBda+9+N02mzxbTCNbEHRuP1 wKDc7t0MBHqG6IwvYJ9m4qyRMr7UXKOOzeSmFcBNszFrkrFFVQXvZgUw88EdLYF+Nb c0p+B2wwb/EHA== Date: Fri, 17 Jul 2026 14:36:59 +0100 From: "Lorenzo Stoakes (ARM)" To: Nimrod Oren Cc: Andrew Morton , David Hildenbrand , Zi Yan , Baolin Wang , "Liam R. Howlett" , Nico Pache , Ryan Roberts , Dev Jain , Barry Song , Lance Yang , Usama Arif , Liu Song , Hao Peng , linux-mm@kvack.org, linux-kernel@vger.kernel.org Subject: Re: [RFC PATCH] mm/khugepaged: cap pageblock contribution to min_free_kbytes Message-ID: References: <20260716173504.760369-1-noren@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20260716173504.760369-1-noren@nvidia.com> On Thu, Jul 16, 2026 at 08:35:04PM +0300, Nimrod Oren wrote: > When THP is enabled, khugepaged can raise min_free_kbytes through > set_recommended_min_free_kbytes(), using a heuristic that scales with Nitty, but that makes it sound like khugepaged (the process) itself comes in and changes it, rather set_recommended_min_free_kbytes() sets it higher if hugepage_enabled(). > pageblock_nr_pages. With MIGRATE_PCPTYPES equal to 3, the formula > accounts for 11 pageblocks for every eligible populated zone before > capping the result at 5% of low memory. > > This is reasonable when a pageblock is 2MB, as on common 4KB page > configurations, but scales poorly with larger base page sizes. With > the default arm64 pageblock sizes, the contribution per eligible zone > before the 5% cap is: > > 4KB pages: 2MB pageblock, 22MB per zone > 16KB pages: 32MB pageblock, 352MB per zone > 64KB pages: 512MB pageblock, 5.5GB per zone > > The 5% limit does not make this harmless. On an arm64 system with > 128GB of RAM and 64KB pages, the current calculation resulted in > roughly 6.3GB for min_free_kbytes. The corresponding aggregate min, low > and high watermarks were 6.3GB, 7.8GB and 9.4GB. Yeah this is an ongoing issue (as you note below). We definitely need to find a better solution. > > Automatically maintaining multiple GB of additional free memory to > improve the probability of 512MB THP allocations seems disproportionate > for an opportunistic background optimization. The fact it's in khugepaged.c doesn't mean it's just about khugepaged, it's about avoiding fragmentation for the purposes of obtaining THP pages which can also be obtained from page fault and MADV_COLLAPSE. > > This RFC proposes capping the pageblock contribution to the > recommendation at 2MB. This would limit the contribution to 22MB per > eligible zone while leaving configurations with pageblocks of 2MB or > smaller unchanged. I guess you're trying to do the most conservative thing possible - but it's a bit of a cheat really. I mean does it really make sense to reserve 2 MB for the purposes of obtaining 512 MB PMDs? It's essentially giving up on the idea of getting PMD THP altogether but pretending you are still doing so. I wonder if it wouldn't just be better to explicitly check the page table size and to not even bother trying for > 4 KiB with a comment explaining that it's not a worthwhile trade-off. I think that'd be the more honest version of this patch wouldn't it? Or maybe we need not be so concerneda bout scaling across zones... > > Earlier proposals to resolve this problem involved disabling default > THP on affected systems [1], adding a knob to disable the > recommendation [2], or deriving the recommendation from the largest > enabled THP order [3]. A fixed cap requires no new knob and avoids > large changes to global watermarks when THP policy is changed at > runtime. How often are people turning THP completely off or on again though? > > Reducing PAGE_BLOCK_MAX_ORDER remains possible, but it is a build-time > policy that changes pageblock granularity globally. Its effects extend > beyond the scope of adjusting min_free_kbytes. > > [1] https://lore.kernel.org/all/CAPm50aLPxJCiVTqqwiz00oMNiqHggB84sXB3x=tv_HUAd5UktQ@mail.gmail.com/ > [2] https://lore.kernel.org/all/20230817035155.84230-1-liusong@linux.alibaba.com/ > [3] https://lore.kernel.org/all/20250606143700.3256414-1-usamaarif642@gmail.com/ > > Signed-off-by: Nimrod Oren Thanks in any case for taking a more pragmatic approach here. > --- > mm/khugepaged.c | 17 +++++++++++++---- > 1 file changed, 13 insertions(+), 4 deletions(-) > > diff --git a/mm/khugepaged.c b/mm/khugepaged.c > index 27e8f3077e80..c0570bbb1f4e 100644 > --- a/mm/khugepaged.c > +++ b/mm/khugepaged.c > @@ -23,6 +23,7 @@ > #include > #include > #include > +#include > > #include > #include "internal.h" > @@ -3066,6 +3067,7 @@ void set_recommended_min_free_kbytes(void) > { > struct zone *zone; > int nr_zones = 0; > + unsigned long capped_pageblock_nr_pages; > unsigned long recommended_min; > > if (!hugepage_enabled()) { > @@ -3084,16 +3086,23 @@ void set_recommended_min_free_kbytes(void) > nr_zones++; > } > > - /* Ensure 2 pageblocks are free to assist fragmentation avoidance */ > - recommended_min = pageblock_nr_pages * nr_zones * 2; > + /* > + * Limit the pageblock contribution to 2 MiB. Larger pageblocks can > + * otherwise make min_free_kbytes disproportionately large. > + */ > + capped_pageblock_nr_pages = > + min(pageblock_nr_pages, SZ_2M / PAGE_SIZE); Yeah this just feels like a hack, to be honest "let's pretend a page block is 2 MiB" essentially :) > + > + /* Ensure 2 capped units are free to assist fragmentation avoidance */ > + recommended_min = capped_pageblock_nr_pages * nr_zones * 2; > > /* > - * Make sure that on average at least two pageblocks are almost free > + * Make sure that on average at least two capped units are almost free > * of another type, one for a migratetype to fall back to and a > * second to avoid subsequent fallbacks of other types There are 3 > * MIGRATE_TYPES we care about. > */ > - recommended_min += pageblock_nr_pages * nr_zones * > + recommended_min += capped_pageblock_nr_pages * nr_zones * > MIGRATE_PCPTYPES * MIGRATE_PCPTYPES; we're really getting into faking stuff out realms here - pretending a page block is 2 MiB then doing calculations on it as if they were. We'd be better off (and more honest) with something like: - if (!hugepage_enabled()) { + /* Avoid excessive memory reservation if page blocks are very large. */ + if (!hugepage_enabled() || PAGE_SIZE > SZ_4K) { But that seems perhaps unwise. Maybe a simpler version would be to just attack the issue we actually have with it - it uses too much damn memory :) So maybe something like the attached? (you'd probably want to make this a sysctl or at least a defined value of course) General idea is - if we're going to cap, let's just cap instead of pretending that page blocks are of a different size. > > /* don't ever allow to reserve more than 5% of the lowmem */ > -- > 2.45.0 > Cheers, Lorenzo ----8<---- diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 617bca76db49..d9e1e7de5494 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -3092,9 +3092,13 @@ void set_recommended_min_free_kbytes(void) recommended_min += pageblock_nr_pages * nr_zones * MIGRATE_PCPTYPES * MIGRATE_PCPTYPES; - /* don't ever allow to reserve more than 5% of the lowmem */ + /* + * don't ever allow the reservation of more than 1 GiB or 5% of lowmem + * (whichever is smaller). + */ recommended_min = min(recommended_min, (unsigned long) nr_free_buffer_pages() / 20); + recommended_min = min(recommended_min, SZ_1G); recommended_min <<= (PAGE_SHIFT-10); if (recommended_min > min_free_kbytes) {