On 9/11/23 21:41, Johannes Weiner wrote: > The buddy allocator coalesces compatible blocks during freeing, but it > doesn't update the types of the subblocks to match. When an allocation > later breaks the chunk down again, its pieces will be put on freelists > of the wrong type. This encourages incompatible page mixing (ask for > one type, get another), and thus long-term fragmentation. Yeah why not. Sould be pretty rare as this only affects >=pageblock_order, but then also the overhead in the otherwise hot function is limited to its colder part. > Update the subblocks when merging a larger chunk, such that a later > expand() will maintain freelist type hygiene. > > v2: > - remove spurious change_pageblock_range() move (Zi Yan) > > Signed-off-by: Johannes Weiner <hannes@xxxxxxxxxxx> Reviewed-by: Vlastimil Babka <vbabka@xxxxxxx> > --- > mm/page_alloc.c | 15 +++++++++++---- > 1 file changed, 11 insertions(+), 4 deletions(-) > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index e3f1c777feed..3db405414174 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -783,10 +783,17 @@ static inline void __free_one_page(struct page *page, > */ > int buddy_mt = get_pfnblock_migratetype(buddy, buddy_pfn); > > - if (migratetype != buddy_mt > - && (!migratetype_is_mergeable(migratetype) || > - !migratetype_is_mergeable(buddy_mt))) > - goto done_merging; > + if (migratetype != buddy_mt) { > + if (!migratetype_is_mergeable(migratetype) || > + !migratetype_is_mergeable(buddy_mt)) > + goto done_merging; > + /* > + * Match buddy type. This ensures that > + * an expand() down the line puts the > + * sub-blocks on the right freelists. > + */ > + set_pageblock_migratetype(buddy, migratetype); > + } > } > > /*