diff options
author | Kirill A. Shutemov <kirill.shutemov@linux.intel.com> | 2016-11-30 18:54:19 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-11-30 19:32:52 -0500 |
commit | 5cbc198ae08d84bd416b672ad8bd1222acd0855c (patch) | |
tree | 985d1c3e96076aaca0f485d26f04b9957f920013 | |
parent | 828347f8f9a558cf1af2faa46387a26564f2ac3e (diff) |
mm: fix false-positive WARN_ON() in truncate/invalidate for hugetlb
Hugetlb pages have ->index in size of the huge pages (PMD_SIZE or
PUD_SIZE), not in PAGE_SIZE as other types of pages. This means we
cannot user page_to_pgoff() to check whether we've got the right page
for the radix-tree index.
Let's introduce page_to_index() which would return radix-tree index for
given page.
We will be able to get rid of this once hugetlb will be switched to
multi-order entries.
Fixes: fc127da085c2 ("truncate: handle file thp")
Link: http://lkml.kernel.org/r/20161123093053.mjbnvn5zwxw5e6lk@black.fi.intel.com
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reported-by: Doug Nelson <doug.nelson@intel.com>
Tested-by: Doug Nelson <doug.nelson@intel.com>
Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: <stable@vger.kernel.org> [4.8+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/pagemap.h | 21 | ||||
-rw-r--r-- | mm/truncate.c | 8 |
2 files changed, 19 insertions, 10 deletions
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index dd15d39e1985..7dbe9148b2f8 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h | |||
@@ -374,16 +374,13 @@ static inline struct page *read_mapping_page(struct address_space *mapping, | |||
374 | } | 374 | } |
375 | 375 | ||
376 | /* | 376 | /* |
377 | * Get the offset in PAGE_SIZE. | 377 | * Get index of the page with in radix-tree |
378 | * (TODO: hugepage should have ->index in PAGE_SIZE) | 378 | * (TODO: remove once hugetlb pages will have ->index in PAGE_SIZE) |
379 | */ | 379 | */ |
380 | static inline pgoff_t page_to_pgoff(struct page *page) | 380 | static inline pgoff_t page_to_index(struct page *page) |
381 | { | 381 | { |
382 | pgoff_t pgoff; | 382 | pgoff_t pgoff; |
383 | 383 | ||
384 | if (unlikely(PageHeadHuge(page))) | ||
385 | return page->index << compound_order(page); | ||
386 | |||
387 | if (likely(!PageTransTail(page))) | 384 | if (likely(!PageTransTail(page))) |
388 | return page->index; | 385 | return page->index; |
389 | 386 | ||
@@ -397,6 +394,18 @@ static inline pgoff_t page_to_pgoff(struct page *page) | |||
397 | } | 394 | } |
398 | 395 | ||
399 | /* | 396 | /* |
397 | * Get the offset in PAGE_SIZE. | ||
398 | * (TODO: hugepage should have ->index in PAGE_SIZE) | ||
399 | */ | ||
400 | static inline pgoff_t page_to_pgoff(struct page *page) | ||
401 | { | ||
402 | if (unlikely(PageHeadHuge(page))) | ||
403 | return page->index << compound_order(page); | ||
404 | |||
405 | return page_to_index(page); | ||
406 | } | ||
407 | |||
408 | /* | ||
400 | * Return byte-offset into filesystem object for page. | 409 | * Return byte-offset into filesystem object for page. |
401 | */ | 410 | */ |
402 | static inline loff_t page_offset(struct page *page) | 411 | static inline loff_t page_offset(struct page *page) |
diff --git a/mm/truncate.c b/mm/truncate.c index a01cce450a26..8d8c62d89e6d 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -283,7 +283,7 @@ void truncate_inode_pages_range(struct address_space *mapping, | |||
283 | 283 | ||
284 | if (!trylock_page(page)) | 284 | if (!trylock_page(page)) |
285 | continue; | 285 | continue; |
286 | WARN_ON(page_to_pgoff(page) != index); | 286 | WARN_ON(page_to_index(page) != index); |
287 | if (PageWriteback(page)) { | 287 | if (PageWriteback(page)) { |
288 | unlock_page(page); | 288 | unlock_page(page); |
289 | continue; | 289 | continue; |
@@ -371,7 +371,7 @@ void truncate_inode_pages_range(struct address_space *mapping, | |||
371 | } | 371 | } |
372 | 372 | ||
373 | lock_page(page); | 373 | lock_page(page); |
374 | WARN_ON(page_to_pgoff(page) != index); | 374 | WARN_ON(page_to_index(page) != index); |
375 | wait_on_page_writeback(page); | 375 | wait_on_page_writeback(page); |
376 | truncate_inode_page(mapping, page); | 376 | truncate_inode_page(mapping, page); |
377 | unlock_page(page); | 377 | unlock_page(page); |
@@ -492,7 +492,7 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping, | |||
492 | if (!trylock_page(page)) | 492 | if (!trylock_page(page)) |
493 | continue; | 493 | continue; |
494 | 494 | ||
495 | WARN_ON(page_to_pgoff(page) != index); | 495 | WARN_ON(page_to_index(page) != index); |
496 | 496 | ||
497 | /* Middle of THP: skip */ | 497 | /* Middle of THP: skip */ |
498 | if (PageTransTail(page)) { | 498 | if (PageTransTail(page)) { |
@@ -612,7 +612,7 @@ int invalidate_inode_pages2_range(struct address_space *mapping, | |||
612 | } | 612 | } |
613 | 613 | ||
614 | lock_page(page); | 614 | lock_page(page); |
615 | WARN_ON(page_to_pgoff(page) != index); | 615 | WARN_ON(page_to_index(page) != index); |
616 | if (page->mapping != mapping) { | 616 | if (page->mapping != mapping) { |
617 | unlock_page(page); | 617 | unlock_page(page); |
618 | continue; | 618 | continue; |