Commit d1e153fe authored by Pavel Tatashin's avatar Pavel Tatashin Committed by Linus Torvalds
Browse files

mm/gup: migrate pinned pages out of movable zone

We should not pin pages in ZONE_MOVABLE.  Currently, we do not pin only
movable CMA pages.  Generalize the function that migrates CMA pages to
migrate all movable pages.  Use is_pinnable_page() to check which pages
need to be migrated

Link: https://lkml.kernel.org/r/20210215161349.246722-10-pasha.tatashin@soleen.com


Signed-off-by: default avatarPavel Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: default avatarJohn Hubbard <jhubbard@nvidia.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: James Morris <jmorris@namei.org>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sasha Levin <sashal@kernel.org>
Cc: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Tyler Hicks <tyhicks@linux.microsoft.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9afaf30f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ enum migrate_reason {
	MR_MEMPOLICY_MBIND,
	MR_NUMA_MISPLACED,
	MR_CONTIG_RANGE,
	MR_LONGTERM_PIN,
	MR_TYPES
};

+7 −2
Original line number Diff line number Diff line
@@ -407,8 +407,13 @@ enum zone_type {
	 * to increase the number of THP/huge pages. Notable special cases are:
	 *
	 * 1. Pinned pages: (long-term) pinning of movable pages might
	 *    essentially turn such pages unmovable. Memory offlining might
	 *    retry a long time.
	 *    essentially turn such pages unmovable. Therefore, we do not allow
	 *    pinning long-term pages in ZONE_MOVABLE. When pages are pinned and
	 *    faulted, they come from the right zone right away. However, it is
	 *    still possible that address space already has pages in
	 *    ZONE_MOVABLE at the time when pages are pinned (i.e. user has
	 *    touches that memory before pinning). In such case we migrate them
	 *    to a different zone. When migration fails - pinning fails.
	 * 2. memblock allocations: kernelcore/movablecore setups might create
	 *    situations where ZONE_MOVABLE contains unmovable allocations
	 *    after boot. Memory offlining and allocations fail early.
+2 −1
Original line number Diff line number Diff line
@@ -20,7 +20,8 @@
	EM( MR_SYSCALL,		"syscall_or_cpuset")		\
	EM( MR_MEMPOLICY_MBIND,	"mempolicy_mbind")		\
	EM( MR_NUMA_MISPLACED,	"numa_misplaced")		\
	EMe(MR_CONTIG_RANGE,	"contig_range")
	EM( MR_CONTIG_RANGE,	"contig_range")			\
	EMe(MR_LONGTERM_PIN,	"longterm_pin")

/*
 * First define the enums in the above macros to be exported to userspace
+34 −33
Original line number Diff line number Diff line
@@ -87,11 +87,12 @@ __maybe_unused struct page *try_grab_compound_head(struct page *page,
		int orig_refs = refs;

		/*
		 * Can't do FOLL_LONGTERM + FOLL_PIN with CMA in the gup fast
		 * path, so fail and let the caller fall back to the slow path.
		 * Can't do FOLL_LONGTERM + FOLL_PIN gup fast path if not in a
		 * right zone, so fail and let the caller fall back to the slow
		 * path.
		 */
		if (unlikely(flags & FOLL_LONGTERM) &&
				is_migrate_cma_page(page))
		if (unlikely((flags & FOLL_LONGTERM) &&
			     !is_pinnable_page(page)))
			return NULL;

		/*
@@ -1600,8 +1601,8 @@ struct page *get_dump_page(unsigned long addr)
}
#endif /* CONFIG_ELF_CORE */

#ifdef CONFIG_CMA
static long check_and_migrate_cma_pages(struct mm_struct *mm,
#ifdef CONFIG_MIGRATION
static long check_and_migrate_movable_pages(struct mm_struct *mm,
					    unsigned long start,
					    unsigned long nr_pages,
					    struct page **pages,
@@ -1610,7 +1611,7 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
{
	unsigned long i, isolation_error_count;
	bool drain_allow;
	LIST_HEAD(cma_page_list);
	LIST_HEAD(movable_page_list);
	long ret = nr_pages;
	struct page *prev_head, *head;
	struct migration_target_control mtc = {
@@ -1628,13 +1629,12 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
			continue;
		prev_head = head;
		/*
		 * If we get a page from the CMA zone, since we are going to
		 * be pinning these entries, we might as well move them out
		 * of the CMA zone if possible.
		 * If we get a movable page, since we are going to be pinning
		 * these entries, try to move them out if possible.
		 */
		if (is_migrate_cma_page(head)) {
		if (!is_pinnable_page(head)) {
			if (PageHuge(head)) {
				if (!isolate_huge_page(head, &cma_page_list))
				if (!isolate_huge_page(head, &movable_page_list))
					isolation_error_count++;
			} else {
				if (!PageLRU(head) && drain_allow) {
@@ -1646,7 +1646,7 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
					isolation_error_count++;
					continue;
				}
				list_add_tail(&head->lru, &cma_page_list);
				list_add_tail(&head->lru, &movable_page_list);
				mod_node_page_state(page_pgdat(head),
						    NR_ISOLATED_ANON +
						    page_is_file_lru(head),
@@ -1659,10 +1659,10 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
	 * If list is empty, and no isolation errors, means that all pages are
	 * in the correct zone.
	 */
	if (list_empty(&cma_page_list) && !isolation_error_count)
	if (list_empty(&movable_page_list) && !isolation_error_count)
		return ret;

	if (!list_empty(&cma_page_list)) {
	if (!list_empty(&movable_page_list)) {
		/*
		 * drop the above get_user_pages reference.
		 */
@@ -1672,12 +1672,12 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
			for (i = 0; i < nr_pages; i++)
				put_page(pages[i]);

		ret = migrate_pages(&cma_page_list, alloc_migration_target,
		ret = migrate_pages(&movable_page_list, alloc_migration_target,
				    NULL, (unsigned long)&mtc, MIGRATE_SYNC,
				    MR_CONTIG_RANGE);
				    MR_LONGTERM_PIN);
		if (ret) {
			if (!list_empty(&cma_page_list))
				putback_movable_pages(&cma_page_list);
			if (!list_empty(&movable_page_list))
				putback_movable_pages(&movable_page_list);
			return ret > 0 ? -ENOMEM : ret;
		}

@@ -1696,7 +1696,7 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
	goto check_again;
}
#else
static long check_and_migrate_cma_pages(struct mm_struct *mm,
static long check_and_migrate_movable_pages(struct mm_struct *mm,
					    unsigned long start,
					    unsigned long nr_pages,
					    struct page **pages,
@@ -1705,7 +1705,7 @@ static long check_and_migrate_cma_pages(struct mm_struct *mm,
{
	return nr_pages;
}
#endif /* CONFIG_CMA */
#endif /* CONFIG_MIGRATION */

/*
 * __gup_longterm_locked() is a wrapper for __get_user_pages_locked which
@@ -1729,8 +1729,9 @@ static long __gup_longterm_locked(struct mm_struct *mm,

	if (gup_flags & FOLL_LONGTERM) {
		if (rc > 0)
			rc = check_and_migrate_cma_pages(mm, start, rc, pages,
							 vmas, gup_flags);
			rc = check_and_migrate_movable_pages(mm, start, rc,
							     pages, vmas,
							     gup_flags);
		memalloc_pin_restore(flags);
	}
	return rc;