summaryrefslogtreecommitdiff
path: root/mm/gup.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-09-07 11:03:45 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-09-07 11:03:45 -0700
commitcd1adf1b63a112d762832e9c64b0a886fbb840d6 (patch)
tree67c7a3693acfb4e4c618121aec4e943b9cd2265b /mm/gup.c
parent4b93c544e90e2b28326182d31ee008eb80e02074 (diff)
Revert "mm/gup: remove try_get_page(), call try_get_compound_head() directly"
This reverts commit 9857a17f206ff374aea78bccfb687f145368be2e. That commit was completely broken, and I should have caught on to it earlier. But happily, the kernel test robot noticed the breakage fairly quickly. The breakage is because "try_get_page()" is about avoiding the page reference count overflow case, but is otherwise the exact same as a plain "get_page()". In contrast, "try_get_compound_head()" is an entirely different beast, and uses __page_cache_add_speculative() because it's not just about the page reference count, but also about possibly racing with the underlying page going away. So all the commentary about how "try_get_page() has fallen a little behind in terms of maintenance, try_get_compound_head() handles speculative page references more thoroughly" was just completely wrong: yes, try_get_compound_head() handles speculative page references, but the point is that try_get_page() does not, and must not. So there's no lack of maintainance - there are fundamentally different semantics. A speculative page reference would be entirely wrong in "get_page()", and it's entirely wrong in "try_get_page()". It's not about speculation, it's purely about "uhhuh, you can't get this page because you've tried to increment the reference count too much already". The reason the kernel test robot noticed this bug was that it hit the VM_BUG_ON() in __page_cache_add_speculative(), which is all about verifying that the context of any speculative page access is correct. But since that isn't what try_get_page() is all about, the VM_BUG_ON() tests things that are not correct to test for try_get_page(). Reported-by: kernel test robot <oliver.sang@intel.com> Cc: John Hubbard <jhubbard@nvidia.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/gup.c')
-rw-r--r--mm/gup.c21
1 files changed, 4 insertions, 17 deletions
diff --git a/mm/gup.c b/mm/gup.c
index 9935a4480710..886d6148d3d0 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -62,24 +62,11 @@ static void put_page_refs(struct page *page, int refs)
put_page(page);
}
-/**
- * try_get_compound_head() - return the compound head page with refcount
- * appropriately incremented, or NULL if that failed.
- *
- * This handles potential refcount overflow correctly. It also works correctly
- * for various lockless get_user_pages()-related callers, due to the use of
- * page_cache_add_speculative().
- *
- * Even though the name includes "compound_head", this function is still
- * appropriate for callers that have a non-compound @page to get.
- *
- * @page: pointer to page to be gotten
- * @refs: the value to add to the page's refcount
- *
- * Return: head page (with refcount appropriately incremented) for success, or
- * NULL upon failure.
+/*
+ * Return the compound head page with ref appropriately incremented,
+ * or NULL if that failed.
*/
-struct page *try_get_compound_head(struct page *page, int refs)
+static inline struct page *try_get_compound_head(struct page *page, int refs)
{
struct page *head = compound_head(page);