diff --git c/mm/internal.h i/mm/internal.h --- c/mm/internal.h +++ i/mm/internal.h @@ -1778,12 +1778,24 @@ void __meminit __init_page_from_nid(unsigned long pfn, int nid); * @order is the order the caller asked for, not the order reclaim was * promoted to. defrag_mode raises the reclaim order to pageblock_order, * which says nothing about what the caller can tolerate. + * + * A transparent huge page allocation sets neither flag, yet a failed + * collapse or fault falls back to small pages. Recognise it by its mask. + * In this tree the complete GFP_TRANSHUGE_LIGHT set is carried by the + * huge page fault path, khugepaged and the large folio migration + * targets. The huge zero folio clears __GFP_MOVABLE and so keeps normal + * shrinker behaviour. The three other sites that combine __GFP_COMP + * with __GFP_NOMEMALLOC do not carry the movable, highmem and + * filesystem bits, so they do not match. */ static inline bool gfp_opportunistic_compaction(gfp_t gfp_flags, int order) { if (!order || (gfp_flags & __GFP_NOFAIL)) return false; + if ((gfp_flags & GFP_TRANSHUGE_LIGHT) == GFP_TRANSHUGE_LIGHT) + return true; + return !!(gfp_flags & (__GFP_NORETRY | __GFP_RETRY_MAYFAIL)); }