Message ID | 20230821183733.106619-5-hannes@cmpxchg.org (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | mm: page_alloc: freelist migratetype hygiene | expand |
On 21 Aug 2023, at 14:33, 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. > > Update the subblocks when merging a larger chunk, such that a later > expand() will maintain freelist type hygiene. > > Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> > --- > mm/page_alloc.c | 37 ++++++++++++++++++++++--------------- > 1 file changed, 22 insertions(+), 15 deletions(-) > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index a5e36d186893..6c9f565b2613 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -438,6 +438,17 @@ void set_pageblock_migratetype(struct page *page, int migratetype) > page_to_pfn(page), MIGRATETYPE_MASK); > } > > +static void change_pageblock_range(struct page *pageblock_page, > + int start_order, int migratetype) > +{ > + int nr_pageblocks = 1 << (start_order - pageblock_order); > + > + while (nr_pageblocks--) { > + set_pageblock_migratetype(pageblock_page, migratetype); > + pageblock_page += pageblock_nr_pages; > + } > +} > + Is this code move included by accident? > #ifdef CONFIG_DEBUG_VM > static int page_outside_zone_boundaries(struct zone *zone, struct page *page) > { > @@ -808,10 +819,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); > + } > } > > /* > @@ -1687,17 +1705,6 @@ int move_freepages_block(struct zone *zone, struct page *page, > num_movable); > } > > -static void change_pageblock_range(struct page *pageblock_page, > - int start_order, int migratetype) > -{ > - int nr_pageblocks = 1 << (start_order - pageblock_order); > - > - while (nr_pageblocks--) { > - set_pageblock_migratetype(pageblock_page, migratetype); > - pageblock_page += pageblock_nr_pages; > - } > -} > - > /* > * When we are falling back to another migratetype during allocation, try to > * steal extra free pages from the same pageblocks to satisfy further > -- > 2.41.0 -- Best Regards, Yan, Zi
On Mon, Aug 21, 2023 at 04:41:44PM -0400, Zi Yan wrote: > On 21 Aug 2023, at 14:33, 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. > > > > Update the subblocks when merging a larger chunk, such that a later > > expand() will maintain freelist type hygiene. > > > > Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> > > --- > > mm/page_alloc.c | 37 ++++++++++++++++++++++--------------- > > 1 file changed, 22 insertions(+), 15 deletions(-) > > > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > > index a5e36d186893..6c9f565b2613 100644 > > --- a/mm/page_alloc.c > > +++ b/mm/page_alloc.c > > @@ -438,6 +438,17 @@ void set_pageblock_migratetype(struct page *page, int migratetype) > > page_to_pfn(page), MIGRATETYPE_MASK); > > } > > > > +static void change_pageblock_range(struct page *pageblock_page, > > + int start_order, int migratetype) > > +{ > > + int nr_pageblocks = 1 << (start_order - pageblock_order); > > + > > + while (nr_pageblocks--) { > > + set_pageblock_migratetype(pageblock_page, migratetype); > > + pageblock_page += pageblock_nr_pages; > > + } > > +} > > + > > Is this code move included by accident? Ah, yes, my bad. I used to call change_pageblock_range() at the end of the merge, before adding the coalesced chunk to the freelist, for which I needed this further up. Then I changed it to dealing with individual buddies instead, and forgot to drop this part. I'll remove it in the next version.
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a5e36d186893..6c9f565b2613 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -438,6 +438,17 @@ void set_pageblock_migratetype(struct page *page, int migratetype) page_to_pfn(page), MIGRATETYPE_MASK); } +static void change_pageblock_range(struct page *pageblock_page, + int start_order, int migratetype) +{ + int nr_pageblocks = 1 << (start_order - pageblock_order); + + while (nr_pageblocks--) { + set_pageblock_migratetype(pageblock_page, migratetype); + pageblock_page += pageblock_nr_pages; + } +} + #ifdef CONFIG_DEBUG_VM static int page_outside_zone_boundaries(struct zone *zone, struct page *page) { @@ -808,10 +819,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); + } } /* @@ -1687,17 +1705,6 @@ int move_freepages_block(struct zone *zone, struct page *page, num_movable); } -static void change_pageblock_range(struct page *pageblock_page, - int start_order, int migratetype) -{ - int nr_pageblocks = 1 << (start_order - pageblock_order); - - while (nr_pageblocks--) { - set_pageblock_migratetype(pageblock_page, migratetype); - pageblock_page += pageblock_nr_pages; - } -} - /* * When we are falling back to another migratetype during allocation, try to * steal extra free pages from the same pageblocks to satisfy further
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. Update the subblocks when merging a larger chunk, such that a later expand() will maintain freelist type hygiene. Signed-off-by: Johannes Weiner <hannes@cmpxchg.org> --- mm/page_alloc.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-)