Re: [RFC PATCH v2 2/4] mm: compaction: target mTHP order for proactive compaction

From: Dev Jain

Date: Mon Oct 19 2026 - 04:17:02 EST




On 01/10/26 5:44 am, Bo Zhang wrote:
> Proactive compaction triggers against COMPACTION_HPAGE_ORDER (order-9),
> which is too heavyweight and misdirected for mTHP systems where the
> pressure is on smaller orders like order-2.
>
> Add compact_hpage_order() to return the highest always-enabled anonymous
> mTHP order, and use it as the proactive compaction target in
> should_proactive_compact_node(), __compact_finished() and kcompactd().
>
> Fragmentation scores are not comparable across orders, a small order
> scores much lower for the same state, so scale the proactive watermarks
> down in proportion to the target order, otherwise order-2 would rarely
> reach the order-9 threshold.
>
> Signed-off-by: Bo Zhang <zhangbo56@xxxxxxxxxx>
> ---
> mm/compaction.c | 40 ++++++++++++++++++++++++++++++++--------
> 1 file changed, 32 insertions(+), 8 deletions(-)
>
> diff --git a/mm/compaction.c b/mm/compaction.c
> index ecd3e6cf54fc..3928a5483efc 100644
> --- a/mm/compaction.c
> +++ b/mm/compaction.c
> @@ -24,6 +24,7 @@
> #include <linux/page_owner.h>
> #include <linux/psi.h>
> #include <linux/cpuset.h>
> +#include <linux/huge_mm.h>
> #include "page_alloc.h"
> #include "internal.h"
>
> @@ -81,6 +82,17 @@ static inline bool is_via_compact_memory(int order) { return false; }
> #define COMPACTION_HPAGE_ORDER (PMD_SHIFT - PAGE_SHIFT)
> #endif
>
> +static inline int compact_hpage_order(void)
> +{
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> + unsigned long orders = READ_ONCE(huge_anon_orders_always);
> +
> + if (orders)
> + return __fls(orders);
> +#endif
> + return COMPACTION_HPAGE_ORDER;
> +}
> +
> static struct page *mark_allocated_noprof(struct page *page, unsigned int order, gfp_t gfp_flags)
> {
> post_alloc_hook(page, order, __GFP_MOVABLE, ALLOC_DEFAULT);
> @@ -2257,24 +2269,36 @@ static unsigned int fragmentation_score_node(pg_data_t *pgdat, unsigned int orde
> return score;
> }
>
> -static unsigned int fragmentation_score_wmark(bool low)
> +/*
> + * Fragmentation scores are not comparable across orders: a lower order
> + * scores much lower than a higher order for the same fragmentation state.
> + * Scale the watermark proportionally to the target order so a small order
> + * can still trigger proactive compaction; COMPACTION_HPAGE_ORDER keeps the
> + * original threshold.
> + */
> +static unsigned int fragmentation_score_wmark(bool low, unsigned int order)
> {
> - unsigned int wmark_low, leeway;
> + unsigned int wmark_low, leeway, wmark;
>
> wmark_low = 100U - sysctl_compaction_proactiveness;
> leeway = min(10U, wmark_low / 2);
> - return low ? wmark_low : min(wmark_low + leeway, 100U);
> + wmark = low ? wmark_low : min(wmark_low + leeway, 100U);
> +
> + if (order < COMPACTION_HPAGE_ORDER)
> + wmark = wmark * order / COMPACTION_HPAGE_ORDER;
> +
> + return wmark;
> }

I wonder if this function should be left alone?

compaction_proactiveness, as I understand is given as a sysfs knob to
ensure that the user can control how much power they want to invest into
compacting. So suppose the value is 20, and the highest mTHP order is
only 2.

This gives wmark = 90 * 2 / 9 = 20. So, even if 80% of the memory can
serve order-2 allocations, we will still perform compaction for order-2.
This seems "extremely proactive" :)

>
> static bool should_proactive_compact_node(pg_data_t *pgdat)
> {
> int wmark_high;
> - unsigned int order = COMPACTION_HPAGE_ORDER;
> + unsigned int order = compact_hpage_order();
>
> if (!sysctl_compaction_proactiveness || kswapd_is_running(pgdat))
> return false;
>
> - wmark_high = fragmentation_score_wmark(false);
> + wmark_high = fragmentation_score_wmark(false, order);
> return fragmentation_score_node(pgdat, order) > wmark_high;
> }
>
> @@ -2306,7 +2330,7 @@ static enum compact_result __compact_finished(struct compact_control *cc)
>
> if (cc->proactive_compaction) {
> int score, wmark_low;
> - unsigned int order = COMPACTION_HPAGE_ORDER;
> + unsigned int order = compact_hpage_order();
> pg_data_t *pgdat;
>
> pgdat = cc->zone->zone_pgdat;
> @@ -2314,7 +2338,7 @@ static enum compact_result __compact_finished(struct compact_control *cc)
> return COMPACT_PARTIAL_SKIPPED;
>
> score = fragmentation_score_zone(cc->zone, order);
> - wmark_low = fragmentation_score_wmark(true);
> + wmark_low = fragmentation_score_wmark(true, order);
>
> if (score > wmark_low)
> ret = COMPACT_CONTINUE;
> @@ -3239,7 +3263,7 @@ static int kcompactd(void *p)
> timeout = default_timeout;
> if (should_proactive_compact_node(pgdat)) {
> unsigned int prev_score, score;
> - unsigned int order = COMPACTION_HPAGE_ORDER;
> + unsigned int order = compact_hpage_order();
>
> prev_score = fragmentation_score_node(pgdat, order);
> compact_node(pgdat, true);