On Fri, Nov 6, 2020 at 12:17 PM Zi Yan <ziy@xxxxxxxxxx> wrote: > > On 3 Nov 2020, at 8:03, Yang Shi wrote: > > > In the current implementation unmap_and_move() would return -ENOMEM if > > THP migration is unsupported, then the THP will be split. If split is > > failed just exit without trying to migrate other pages. It doesn't make > > too much sense since there may be enough free memory to migrate other > > pages and there may be a lot base pages on the list. > > > > Return -ENOSYS to make consistent with hugetlb. And if THP split is > > failed just skip and try other pages on the list. > > > > Just skip the whole list and exit when free memory is really low. > > > > Signed-off-by: Yang Shi <shy828301@xxxxxxxxx> > > --- > > mm/migrate.c | 62 ++++++++++++++++++++++++++++++++++++++-------------- > > 1 file changed, 46 insertions(+), 16 deletions(-) > > > > diff --git a/mm/migrate.c b/mm/migrate.c > > index 8f6a61c9274b..b3466d8c7f03 100644 > > --- a/mm/migrate.c > > +++ b/mm/migrate.c > > @@ -1172,7 +1172,7 @@ static int unmap_and_move(new_page_t get_new_page, > > struct page *newpage = NULL; > > > > if (!thp_migration_supported() && PageTransHuge(page)) > > - return -ENOMEM; > > + return -ENOSYS; > > > > if (page_count(page) == 1) { > > /* page was freed from under us. So we are done. */ > > @@ -1376,6 +1376,20 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, > > return rc; > > } > > > > +static inline int try_split_thp(struct page *page, struct page *page2, > > + struct list_head *from) > > +{ > > + int rc = 0; > > + > > + lock_page(page); > > + rc = split_huge_page_to_list(page, from); > > + unlock_page(page); > > + if (!rc) > > + list_safe_reset_next(page, page2, lru); > > This does not work as expected, right? After macro expansion, we have > page2 = list_next_entry(page, lru). Since page2 is passed as a pointer, the change > does not return back the caller. You need to use the pointer to page2 here. Yes, I should pass in **page2. Thanks for catching this. > > > + > > + return rc; > > +} > > + > > /* > > * migrate_pages - migrate the pages specified in a list, to the free pages > > * supplied as the target for the page migration > > @@ -1445,24 +1459,40 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page, > > reason, &ret_pages); > > > > switch(rc) { > > + /* > > + * THP migration might be unsupported or the > > + * allocation could've failed so we should > > + * retry on the same page with the THP split > > + * to base pages. > > + * > > + * Head page is retried immediately and tail > > + * pages are added to the tail of the list so > > + * we encounter them after the rest of the list > > + * is processed. > > + */ > > + case -ENOSYS: > > + /* THP migration is unsupported */ > > + if (is_thp) { > > + if (!try_split_thp(page, page2, from)) { > > + nr_thp_split++; > > + goto retry; > > + } > > + > > + nr_thp_failed++; > > + nr_failed += nr_subpages; > > + break; > > + } > > + > > + /* Hugetlb migration is unsupported */ > > + nr_failed++; > > + break; > > case -ENOMEM: > > /* > > - * THP migration might be unsupported or the > > - * allocation could've failed so we should > > - * retry on the same page with the THP split > > - * to base pages. > > - * > > - * Head page is retried immediately and tail > > - * pages are added to the tail of the list so > > - * we encounter them after the rest of the list > > - * is processed. > > + * When memory is low, don't bother to try to migrate > > + * other pages, just exit. > > The comment does not match the code below. For THPs, the code tries to split the THP > and migrate the base pages if the split is successful. The comment here just covers the "goto out" rather than the split because I thought it is covered by the comment before "case -ENOSYS", which says "unsupported or the allocation could've failed", so I didn't repeat here. > > > */ > > if (is_thp) { > > - lock_page(page); > > - rc = split_huge_page_to_list(page, from); > > - unlock_page(page); > > - if (!rc) { > > - list_safe_reset_next(page, page2, lru); > > + if (!try_split_thp(page, page2, from)) { > > nr_thp_split++; > > goto retry; > > } > > @@ -1490,7 +1520,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page, > > break; > > default: > > /* > > - * Permanent failure (-EBUSY, -ENOSYS, etc.): > > + * Permanent failure (-EBUSY, etc.): > > * unlike -EAGAIN case, the failed page is > > * removed from migration page list and not > > * retried in the next outer loop. > > -- > > 2.26.2 > > > — > Best Regards, > Yan Zi