diff options
| -rw-r--r-- | include/linux/huge_mm.h | 7 | ||||
| -rw-r--r-- | mm/huge_memory.c | 20 | ||||
| -rw-r--r-- | mm/vmscan.c | 4 |
3 files changed, 27 insertions, 4 deletions
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index a3762d49ba39..d3b3e8fcc717 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h | |||
| @@ -113,6 +113,7 @@ extern unsigned long thp_get_unmapped_area(struct file *filp, | |||
| 113 | extern void prep_transhuge_page(struct page *page); | 113 | extern void prep_transhuge_page(struct page *page); |
| 114 | extern void free_transhuge_page(struct page *page); | 114 | extern void free_transhuge_page(struct page *page); |
| 115 | 115 | ||
| 116 | bool can_split_huge_page(struct page *page, int *pextra_pins); | ||
| 116 | int split_huge_page_to_list(struct page *page, struct list_head *list); | 117 | int split_huge_page_to_list(struct page *page, struct list_head *list); |
| 117 | static inline int split_huge_page(struct page *page) | 118 | static inline int split_huge_page(struct page *page) |
| 118 | { | 119 | { |
| @@ -231,6 +232,12 @@ static inline void prep_transhuge_page(struct page *page) {} | |||
| 231 | 232 | ||
| 232 | #define thp_get_unmapped_area NULL | 233 | #define thp_get_unmapped_area NULL |
| 233 | 234 | ||
| 235 | static inline bool | ||
| 236 | can_split_huge_page(struct page *page, int *pextra_pins) | ||
| 237 | { | ||
| 238 | BUILD_BUG(); | ||
| 239 | return false; | ||
| 240 | } | ||
| 234 | static inline int | 241 | static inline int |
| 235 | split_huge_page_to_list(struct page *page, struct list_head *list) | 242 | split_huge_page_to_list(struct page *page, struct list_head *list) |
| 236 | { | 243 | { |
diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 1a168e4bac4b..86975dec0ba1 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c | |||
| @@ -2390,6 +2390,21 @@ int page_trans_huge_mapcount(struct page *page, int *total_mapcount) | |||
| 2390 | return ret; | 2390 | return ret; |
| 2391 | } | 2391 | } |
| 2392 | 2392 | ||
| 2393 | /* Racy check whether the huge page can be split */ | ||
| 2394 | bool can_split_huge_page(struct page *page, int *pextra_pins) | ||
| 2395 | { | ||
| 2396 | int extra_pins; | ||
| 2397 | |||
| 2398 | /* Additional pins from radix tree */ | ||
| 2399 | if (PageAnon(page)) | ||
| 2400 | extra_pins = PageSwapCache(page) ? HPAGE_PMD_NR : 0; | ||
| 2401 | else | ||
| 2402 | extra_pins = HPAGE_PMD_NR; | ||
| 2403 | if (pextra_pins) | ||
| 2404 | *pextra_pins = extra_pins; | ||
| 2405 | return total_mapcount(page) == page_count(page) - extra_pins - 1; | ||
| 2406 | } | ||
| 2407 | |||
| 2393 | /* | 2408 | /* |
| 2394 | * This function splits huge page into normal pages. @page can point to any | 2409 | * This function splits huge page into normal pages. @page can point to any |
| 2395 | * subpage of huge page to split. Split doesn't change the position of @page. | 2410 | * subpage of huge page to split. Split doesn't change the position of @page. |
| @@ -2437,7 +2452,6 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) | |||
| 2437 | ret = -EBUSY; | 2452 | ret = -EBUSY; |
| 2438 | goto out; | 2453 | goto out; |
| 2439 | } | 2454 | } |
| 2440 | extra_pins = PageSwapCache(page) ? HPAGE_PMD_NR : 0; | ||
| 2441 | mapping = NULL; | 2455 | mapping = NULL; |
| 2442 | anon_vma_lock_write(anon_vma); | 2456 | anon_vma_lock_write(anon_vma); |
| 2443 | } else { | 2457 | } else { |
| @@ -2449,8 +2463,6 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) | |||
| 2449 | goto out; | 2463 | goto out; |
| 2450 | } | 2464 | } |
| 2451 | 2465 | ||
| 2452 | /* Addidional pins from radix tree */ | ||
| 2453 | extra_pins = HPAGE_PMD_NR; | ||
| 2454 | anon_vma = NULL; | 2466 | anon_vma = NULL; |
| 2455 | i_mmap_lock_read(mapping); | 2467 | i_mmap_lock_read(mapping); |
| 2456 | } | 2468 | } |
| @@ -2459,7 +2471,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) | |||
| 2459 | * Racy check if we can split the page, before freeze_page() will | 2471 | * Racy check if we can split the page, before freeze_page() will |
| 2460 | * split PMDs | 2472 | * split PMDs |
| 2461 | */ | 2473 | */ |
| 2462 | if (total_mapcount(head) != page_count(head) - extra_pins - 1) { | 2474 | if (!can_split_huge_page(head, &extra_pins)) { |
| 2463 | ret = -EBUSY; | 2475 | ret = -EBUSY; |
| 2464 | goto out_unlock; | 2476 | goto out_unlock; |
| 2465 | } | 2477 | } |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 729e37f02de6..abaf0215a352 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
| @@ -1125,6 +1125,10 @@ static unsigned long shrink_page_list(struct list_head *page_list, | |||
| 1125 | !PageSwapCache(page)) { | 1125 | !PageSwapCache(page)) { |
| 1126 | if (!(sc->gfp_mask & __GFP_IO)) | 1126 | if (!(sc->gfp_mask & __GFP_IO)) |
| 1127 | goto keep_locked; | 1127 | goto keep_locked; |
| 1128 | /* cannot split THP, skip it */ | ||
| 1129 | if (PageTransHuge(page) && | ||
| 1130 | !can_split_huge_page(page, NULL)) | ||
| 1131 | goto activate_locked; | ||
| 1128 | if (!add_to_swap(page)) { | 1132 | if (!add_to_swap(page)) { |
| 1129 | if (!PageTransHuge(page)) | 1133 | if (!PageTransHuge(page)) |
| 1130 | goto activate_locked; | 1134 | goto activate_locked; |
