mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	mm: migrate: fix the return value of migrate_pages()
Patch series "Improve the migration stats". According to talk with Zi Yan [1], this patch set changes the return value of migrate_pages() to avoid returning a number which is larger than the number of pages the users tried to migrate by move_pages() syscall. Also fix the hugetlb migration stats and migration stats in trace_mm_compaction_migratepages(). [1] https://lore.kernel.org/linux-mm/7E44019D-2A5D-4BA7-B4D5-00D4712F1687@nvidia.com/ This patch (of 3): As Zi Yan pointed out, the syscall move_pages() can return a non-migrated number larger than the number of pages the users tried to migrate, when a THP page is failed to migrate. This is confusing for users. Since other migration scenarios do not care about the actual non-migrated number of pages except the memory compaction migration which will fix in following patch. Thus we can change the return value to return the number of {normal page, THP, hugetlb} instead to avoid this issue, and the number of THP splits will be considered as the number of non-migrated THP, no matter how many subpages of the THP are migrated successfully. Meanwhile we should still keep the migration counters using the number of normal pages. Link: https://lkml.kernel.org/r/cover.1636275127.git.baolin.wang@linux.alibaba.com Link: https://lkml.kernel.org/r/6486fabc3e8c66ff613e150af25e89b3147977a6.1636275127.git.baolin.wang@linux.alibaba.com Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com> Signed-off-by: Zi Yan <ziy@nvidia.com> Co-developed-by: Zi Yan <ziy@nvidia.com> Cc: Steven Rostedt (VMware) <rostedt@goodmis.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									d6aba4c8e2
								
							
						
					
					
						commit
						b5bade978e
					
				
					 1 changed files with 47 additions and 16 deletions
				
			
		
							
								
								
									
										57
									
								
								mm/migrate.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								mm/migrate.c
									
									
									
									
									
								
							| 
						 | 
					@ -1421,7 +1421,7 @@ static inline int try_split_thp(struct page *page, struct page **page2,
 | 
				
			||||||
 * @mode:		The migration mode that specifies the constraints for
 | 
					 * @mode:		The migration mode that specifies the constraints for
 | 
				
			||||||
 *			page migration, if any.
 | 
					 *			page migration, if any.
 | 
				
			||||||
 * @reason:		The reason for page migration.
 | 
					 * @reason:		The reason for page migration.
 | 
				
			||||||
 * @ret_succeeded:	Set to the number of pages migrated successfully if
 | 
					 * @ret_succeeded:	Set to the number of normal pages migrated successfully if
 | 
				
			||||||
 *			the caller passes a non-NULL pointer.
 | 
					 *			the caller passes a non-NULL pointer.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * The function returns after 10 attempts or if no pages are movable any more
 | 
					 * The function returns after 10 attempts or if no pages are movable any more
 | 
				
			||||||
| 
						 | 
					@ -1429,7 +1429,9 @@ static inline int try_split_thp(struct page *page, struct page **page2,
 | 
				
			||||||
 * It is caller's responsibility to call putback_movable_pages() to return pages
 | 
					 * It is caller's responsibility to call putback_movable_pages() to return pages
 | 
				
			||||||
 * to the LRU or free list only if ret != 0.
 | 
					 * to the LRU or free list only if ret != 0.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Returns the number of pages that were not migrated, or an error code.
 | 
					 * Returns the number of {normal page, THP} that were not migrated, or an error code.
 | 
				
			||||||
 | 
					 * The number of THP splits will be considered as the number of non-migrated THP,
 | 
				
			||||||
 | 
					 * no matter how many subpages of the THP are migrated successfully.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
					int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
		free_page_t put_new_page, unsigned long private,
 | 
							free_page_t put_new_page, unsigned long private,
 | 
				
			||||||
| 
						 | 
					@ -1438,6 +1440,7 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
	int retry = 1;
 | 
						int retry = 1;
 | 
				
			||||||
	int thp_retry = 1;
 | 
						int thp_retry = 1;
 | 
				
			||||||
	int nr_failed = 0;
 | 
						int nr_failed = 0;
 | 
				
			||||||
 | 
						int nr_failed_pages = 0;
 | 
				
			||||||
	int nr_succeeded = 0;
 | 
						int nr_succeeded = 0;
 | 
				
			||||||
	int nr_thp_succeeded = 0;
 | 
						int nr_thp_succeeded = 0;
 | 
				
			||||||
	int nr_thp_failed = 0;
 | 
						int nr_thp_failed = 0;
 | 
				
			||||||
| 
						 | 
					@ -1449,13 +1452,16 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
	int swapwrite = current->flags & PF_SWAPWRITE;
 | 
						int swapwrite = current->flags & PF_SWAPWRITE;
 | 
				
			||||||
	int rc, nr_subpages;
 | 
						int rc, nr_subpages;
 | 
				
			||||||
	LIST_HEAD(ret_pages);
 | 
						LIST_HEAD(ret_pages);
 | 
				
			||||||
 | 
						LIST_HEAD(thp_split_pages);
 | 
				
			||||||
	bool nosplit = (reason == MR_NUMA_MISPLACED);
 | 
						bool nosplit = (reason == MR_NUMA_MISPLACED);
 | 
				
			||||||
 | 
						bool no_subpage_counting = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_mm_migrate_pages_start(mode, reason);
 | 
						trace_mm_migrate_pages_start(mode, reason);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!swapwrite)
 | 
						if (!swapwrite)
 | 
				
			||||||
		current->flags |= PF_SWAPWRITE;
 | 
							current->flags |= PF_SWAPWRITE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					thp_subpage_migration:
 | 
				
			||||||
	for (pass = 0; pass < 10 && (retry || thp_retry); pass++) {
 | 
						for (pass = 0; pass < 10 && (retry || thp_retry); pass++) {
 | 
				
			||||||
		retry = 0;
 | 
							retry = 0;
 | 
				
			||||||
		thp_retry = 0;
 | 
							thp_retry = 0;
 | 
				
			||||||
| 
						 | 
					@ -1504,18 +1510,20 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
			case -ENOSYS:
 | 
								case -ENOSYS:
 | 
				
			||||||
				/* THP migration is unsupported */
 | 
									/* THP migration is unsupported */
 | 
				
			||||||
				if (is_thp) {
 | 
									if (is_thp) {
 | 
				
			||||||
					if (!try_split_thp(page, &page2, from)) {
 | 
										nr_thp_failed++;
 | 
				
			||||||
 | 
										if (!try_split_thp(page, &page2, &thp_split_pages)) {
 | 
				
			||||||
						nr_thp_split++;
 | 
											nr_thp_split++;
 | 
				
			||||||
						goto retry;
 | 
											goto retry;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					nr_thp_failed++;
 | 
										nr_failed_pages += nr_subpages;
 | 
				
			||||||
					nr_failed += nr_subpages;
 | 
					 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* Hugetlb migration is unsupported */
 | 
									/* Hugetlb migration is unsupported */
 | 
				
			||||||
 | 
									if (!no_subpage_counting)
 | 
				
			||||||
					nr_failed++;
 | 
										nr_failed++;
 | 
				
			||||||
 | 
									nr_failed_pages++;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case -ENOMEM:
 | 
								case -ENOMEM:
 | 
				
			||||||
				/*
 | 
									/*
 | 
				
			||||||
| 
						 | 
					@ -1524,16 +1532,19 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
				 * THP NUMA faulting doesn't split THP to retry.
 | 
									 * THP NUMA faulting doesn't split THP to retry.
 | 
				
			||||||
				 */
 | 
									 */
 | 
				
			||||||
				if (is_thp && !nosplit) {
 | 
									if (is_thp && !nosplit) {
 | 
				
			||||||
					if (!try_split_thp(page, &page2, from)) {
 | 
										nr_thp_failed++;
 | 
				
			||||||
 | 
										if (!try_split_thp(page, &page2, &thp_split_pages)) {
 | 
				
			||||||
						nr_thp_split++;
 | 
											nr_thp_split++;
 | 
				
			||||||
						goto retry;
 | 
											goto retry;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					nr_thp_failed++;
 | 
										nr_failed_pages += nr_subpages;
 | 
				
			||||||
					nr_failed += nr_subpages;
 | 
					 | 
				
			||||||
					goto out;
 | 
										goto out;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!no_subpage_counting)
 | 
				
			||||||
					nr_failed++;
 | 
										nr_failed++;
 | 
				
			||||||
 | 
									nr_failed_pages++;
 | 
				
			||||||
				goto out;
 | 
									goto out;
 | 
				
			||||||
			case -EAGAIN:
 | 
								case -EAGAIN:
 | 
				
			||||||
				if (is_thp) {
 | 
									if (is_thp) {
 | 
				
			||||||
| 
						 | 
					@ -1559,17 +1570,37 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
				 */
 | 
									 */
 | 
				
			||||||
				if (is_thp) {
 | 
									if (is_thp) {
 | 
				
			||||||
					nr_thp_failed++;
 | 
										nr_thp_failed++;
 | 
				
			||||||
					nr_failed += nr_subpages;
 | 
										nr_failed_pages += nr_subpages;
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (!no_subpage_counting)
 | 
				
			||||||
					nr_failed++;
 | 
										nr_failed++;
 | 
				
			||||||
 | 
									nr_failed_pages++;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nr_failed += retry + thp_retry;
 | 
						nr_failed += retry;
 | 
				
			||||||
	nr_thp_failed += thp_retry;
 | 
						nr_thp_failed += thp_retry;
 | 
				
			||||||
	rc = nr_failed;
 | 
						/*
 | 
				
			||||||
 | 
						 * Try to migrate subpages of fail-to-migrate THPs, no nr_failed
 | 
				
			||||||
 | 
						 * counting in this round, since all subpages of a THP is counted
 | 
				
			||||||
 | 
						 * as 1 failure in the first round.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (!list_empty(&thp_split_pages)) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Move non-migrated pages (after 10 retries) to ret_pages
 | 
				
			||||||
 | 
							 * to avoid migrating them again.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							list_splice_init(from, &ret_pages);
 | 
				
			||||||
 | 
							list_splice_init(&thp_split_pages, from);
 | 
				
			||||||
 | 
							no_subpage_counting = true;
 | 
				
			||||||
 | 
							retry = 1;
 | 
				
			||||||
 | 
							goto thp_subpage_migration;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = nr_failed + nr_thp_failed;
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Put the permanent failure page back to migration list, they
 | 
						 * Put the permanent failure page back to migration list, they
 | 
				
			||||||
| 
						 | 
					@ -1578,11 +1609,11 @@ int migrate_pages(struct list_head *from, new_page_t get_new_page,
 | 
				
			||||||
	list_splice(&ret_pages, from);
 | 
						list_splice(&ret_pages, from);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	count_vm_events(PGMIGRATE_SUCCESS, nr_succeeded);
 | 
						count_vm_events(PGMIGRATE_SUCCESS, nr_succeeded);
 | 
				
			||||||
	count_vm_events(PGMIGRATE_FAIL, nr_failed);
 | 
						count_vm_events(PGMIGRATE_FAIL, nr_failed_pages);
 | 
				
			||||||
	count_vm_events(THP_MIGRATION_SUCCESS, nr_thp_succeeded);
 | 
						count_vm_events(THP_MIGRATION_SUCCESS, nr_thp_succeeded);
 | 
				
			||||||
	count_vm_events(THP_MIGRATION_FAIL, nr_thp_failed);
 | 
						count_vm_events(THP_MIGRATION_FAIL, nr_thp_failed);
 | 
				
			||||||
	count_vm_events(THP_MIGRATION_SPLIT, nr_thp_split);
 | 
						count_vm_events(THP_MIGRATION_SPLIT, nr_thp_split);
 | 
				
			||||||
	trace_mm_migrate_pages(nr_succeeded, nr_failed, nr_thp_succeeded,
 | 
						trace_mm_migrate_pages(nr_succeeded, nr_failed_pages, nr_thp_succeeded,
 | 
				
			||||||
			       nr_thp_failed, nr_thp_split, mode, reason);
 | 
								       nr_thp_failed, nr_thp_split, mode, reason);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!swapwrite)
 | 
						if (!swapwrite)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue