forked from mirrors/linux
		
	mm,hwpoison,hugetlb,memory_hotplug: hotremove memory section with hwpoisoned hugepage
Patch series "mm, hwpoison: improve handling workload related to hugetlb and memory_hotplug", v7. This patchset tries to solve the issue among memory_hotplug, hugetlb and hwpoison. In this patchset, memory hotplug handles hwpoison pages like below: - hwpoison pages should not prevent memory hotremove, - memory block with hwpoison pages should not be onlined. This patch (of 4): HWPoisoned page is not supposed to be accessed once marked, but currently such accesses can happen during memory hotremove because do_migrate_range() can be called before dissolve_free_huge_pages() is called. Clear HPageMigratable for hwpoisoned hugepages to prevent them from being migrated. This should be done in hugetlb_lock to avoid race against isolate_hugetlb(). get_hwpoison_huge_page() needs to have a flag to show it's called from unpoison to take refcount of hwpoisoned hugepages, so add it. [naoya.horiguchi@linux.dev: remove TestClearHPageMigratable and reduce to test and clear separately] Link: https://lkml.kernel.org/r/20221025053559.GA2104800@ik1-406-35019.vs.sakura.ne.jp Link: https://lkml.kernel.org/r/20221024062012.1520887-1-naoya.horiguchi@linux.dev Link: https://lkml.kernel.org/r/20221024062012.1520887-2-naoya.horiguchi@linux.dev Signed-off-by: Naoya Horiguchi <naoya.horiguchi@nec.com> Reported-by: Miaohe Lin <linmiaohe@huawei.com> Reviewed-by: Oscar Salvador <osalvador@suse.de> Reviewed-by: Miaohe Lin <linmiaohe@huawei.com> Cc: David Hildenbrand <david@redhat.com> Cc: Jane Chu <jane.chu@oracle.com> Cc: Mike Kravetz <mike.kravetz@oracle.com> Cc: Muchun Song <songmuchun@bytedance.com> Cc: Yang Shi <shy828301@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									fd4a7ac329
								
							
						
					
					
						commit
						e591ef7d96
					
				
					 4 changed files with 32 additions and 14 deletions
				
			
		|  | @ -183,8 +183,9 @@ bool hugetlb_reserve_pages(struct inode *inode, long from, long to, | ||||||
| long hugetlb_unreserve_pages(struct inode *inode, long start, long end, | long hugetlb_unreserve_pages(struct inode *inode, long start, long end, | ||||||
| 						long freed); | 						long freed); | ||||||
| int isolate_hugetlb(struct page *page, struct list_head *list); | int isolate_hugetlb(struct page *page, struct list_head *list); | ||||||
| int get_hwpoison_huge_page(struct page *page, bool *hugetlb); | int get_hwpoison_huge_page(struct page *page, bool *hugetlb, bool unpoison); | ||||||
| int get_huge_page_for_hwpoison(unsigned long pfn, int flags); | int get_huge_page_for_hwpoison(unsigned long pfn, int flags, | ||||||
|  | 				bool *migratable_cleared); | ||||||
| void putback_active_hugepage(struct page *page); | void putback_active_hugepage(struct page *page); | ||||||
| void move_hugetlb_state(struct page *oldpage, struct page *newpage, int reason); | void move_hugetlb_state(struct page *oldpage, struct page *newpage, int reason); | ||||||
| void free_huge_page(struct page *page); | void free_huge_page(struct page *page); | ||||||
|  | @ -391,12 +392,13 @@ static inline int isolate_hugetlb(struct page *page, struct list_head *list) | ||||||
| 	return -EBUSY; | 	return -EBUSY; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline int get_hwpoison_huge_page(struct page *page, bool *hugetlb) | static inline int get_hwpoison_huge_page(struct page *page, bool *hugetlb, bool unpoison) | ||||||
| { | { | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline int get_huge_page_for_hwpoison(unsigned long pfn, int flags) | static inline int get_huge_page_for_hwpoison(unsigned long pfn, int flags, | ||||||
|  | 					bool *migratable_cleared) | ||||||
| { | { | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3277,9 +3277,11 @@ extern void shake_page(struct page *p); | ||||||
| extern atomic_long_t num_poisoned_pages __read_mostly; | extern atomic_long_t num_poisoned_pages __read_mostly; | ||||||
| extern int soft_offline_page(unsigned long pfn, int flags); | extern int soft_offline_page(unsigned long pfn, int flags); | ||||||
| #ifdef CONFIG_MEMORY_FAILURE | #ifdef CONFIG_MEMORY_FAILURE | ||||||
| extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags); | extern int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, | ||||||
|  | 					bool *migratable_cleared); | ||||||
| #else | #else | ||||||
| static inline int __get_huge_page_for_hwpoison(unsigned long pfn, int flags) | static inline int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, | ||||||
|  | 					bool *migratable_cleared) | ||||||
| { | { | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7265,7 +7265,7 @@ int isolate_hugetlb(struct page *page, struct list_head *list) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int get_hwpoison_huge_page(struct page *page, bool *hugetlb) | int get_hwpoison_huge_page(struct page *page, bool *hugetlb, bool unpoison) | ||||||
| { | { | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -7275,7 +7275,7 @@ int get_hwpoison_huge_page(struct page *page, bool *hugetlb) | ||||||
| 		*hugetlb = true; | 		*hugetlb = true; | ||||||
| 		if (HPageFreed(page)) | 		if (HPageFreed(page)) | ||||||
| 			ret = 0; | 			ret = 0; | ||||||
| 		else if (HPageMigratable(page)) | 		else if (HPageMigratable(page) || unpoison) | ||||||
| 			ret = get_page_unless_zero(page); | 			ret = get_page_unless_zero(page); | ||||||
| 		else | 		else | ||||||
| 			ret = -EBUSY; | 			ret = -EBUSY; | ||||||
|  | @ -7284,12 +7284,13 @@ int get_hwpoison_huge_page(struct page *page, bool *hugetlb) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int get_huge_page_for_hwpoison(unsigned long pfn, int flags) | int get_huge_page_for_hwpoison(unsigned long pfn, int flags, | ||||||
|  | 				bool *migratable_cleared) | ||||||
| { | { | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irq(&hugetlb_lock); | 	spin_lock_irq(&hugetlb_lock); | ||||||
| 	ret = __get_huge_page_for_hwpoison(pfn, flags); | 	ret = __get_huge_page_for_hwpoison(pfn, flags, migratable_cleared); | ||||||
| 	spin_unlock_irq(&hugetlb_lock); | 	spin_unlock_irq(&hugetlb_lock); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1244,7 +1244,7 @@ static int __get_hwpoison_page(struct page *page, unsigned long flags) | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 	bool hugetlb = false; | 	bool hugetlb = false; | ||||||
| 
 | 
 | ||||||
| 	ret = get_hwpoison_huge_page(head, &hugetlb); | 	ret = get_hwpoison_huge_page(head, &hugetlb, false); | ||||||
| 	if (hugetlb) | 	if (hugetlb) | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
|  | @ -1334,7 +1334,7 @@ static int __get_unpoison_page(struct page *page) | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 	bool hugetlb = false; | 	bool hugetlb = false; | ||||||
| 
 | 
 | ||||||
| 	ret = get_hwpoison_huge_page(head, &hugetlb); | 	ret = get_hwpoison_huge_page(head, &hugetlb, true); | ||||||
| 	if (hugetlb) | 	if (hugetlb) | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
|  | @ -1785,7 +1785,8 @@ void hugetlb_clear_page_hwpoison(struct page *hpage) | ||||||
|  *   -EBUSY        - the hugepage is busy (try to retry) |  *   -EBUSY        - the hugepage is busy (try to retry) | ||||||
|  *   -EHWPOISON    - the hugepage is already hwpoisoned |  *   -EHWPOISON    - the hugepage is already hwpoisoned | ||||||
|  */ |  */ | ||||||
| int __get_huge_page_for_hwpoison(unsigned long pfn, int flags) | int __get_huge_page_for_hwpoison(unsigned long pfn, int flags, | ||||||
|  | 				 bool *migratable_cleared) | ||||||
| { | { | ||||||
| 	struct page *page = pfn_to_page(pfn); | 	struct page *page = pfn_to_page(pfn); | ||||||
| 	struct page *head = compound_head(page); | 	struct page *head = compound_head(page); | ||||||
|  | @ -1815,6 +1816,15 @@ int __get_huge_page_for_hwpoison(unsigned long pfn, int flags) | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Clearing HPageMigratable for hwpoisoned hugepages to prevent them | ||||||
|  | 	 * from being migrated by memory hotremove. | ||||||
|  | 	 */ | ||||||
|  | 	if (count_increased && HPageMigratable(head)) { | ||||||
|  | 		ClearHPageMigratable(head); | ||||||
|  | 		*migratable_cleared = true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return ret; | 	return ret; | ||||||
| out: | out: | ||||||
| 	if (count_increased) | 	if (count_increased) | ||||||
|  | @ -1834,10 +1844,11 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb | ||||||
| 	struct page *p = pfn_to_page(pfn); | 	struct page *p = pfn_to_page(pfn); | ||||||
| 	struct page *head; | 	struct page *head; | ||||||
| 	unsigned long page_flags; | 	unsigned long page_flags; | ||||||
|  | 	bool migratable_cleared = false; | ||||||
| 
 | 
 | ||||||
| 	*hugetlb = 1; | 	*hugetlb = 1; | ||||||
| retry: | retry: | ||||||
| 	res = get_huge_page_for_hwpoison(pfn, flags); | 	res = get_huge_page_for_hwpoison(pfn, flags, &migratable_cleared); | ||||||
| 	if (res == 2) { /* fallback to normal page handling */ | 	if (res == 2) { /* fallback to normal page handling */ | ||||||
| 		*hugetlb = 0; | 		*hugetlb = 0; | ||||||
| 		return 0; | 		return 0; | ||||||
|  | @ -1861,6 +1872,8 @@ static int try_memory_failure_hugetlb(unsigned long pfn, int flags, int *hugetlb | ||||||
| 
 | 
 | ||||||
| 	if (hwpoison_filter(p)) { | 	if (hwpoison_filter(p)) { | ||||||
| 		hugetlb_clear_page_hwpoison(head); | 		hugetlb_clear_page_hwpoison(head); | ||||||
|  | 		if (migratable_cleared) | ||||||
|  | 			SetHPageMigratable(head); | ||||||
| 		unlock_page(head); | 		unlock_page(head); | ||||||
| 		if (res == 1) | 		if (res == 1) | ||||||
| 			put_page(head); | 			put_page(head); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Naoya Horiguchi
						Naoya Horiguchi