diff options
-rw-r--r-- | kernel/futex.c | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 98a354dfbe8..8b467b4a437 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -264,17 +264,29 @@ again: page = compound_head(page); lock_page(page); + + /* + * If page->mapping is NULL, then it cannot be a PageAnon + * page; but it might be the ZERO_PAGE or in the gate area or + * in a special mapping (all cases which we are happy to fail); + * or it may have been a good file page when get_user_pages_fast + * found it, but truncated or holepunched or subjected to + * invalidate_complete_page2 before we got the page lock (also + * cases which we are happy to fail). And we hold a reference, + * so refcount care in invalidate_complete_page's remove_mapping + * prevents drop_caches from setting mapping to NULL beneath us. + * + * The case we do have to guard against is when memory pressure made + * shmem_writepage move it from filecache to swapcache beneath us: + * an unlikely race, but we do need to retry for page->mapping. + */ if (!page->mapping) { + int shmem_swizzled = PageSwapCache(page); unlock_page(page); put_page(page); - /* - * ZERO_PAGE pages don't have a mapping. Avoid a busy loop - * trying to find one. RW mapping would have COW'd (and thus - * have a mapping) so this page is RO and won't ever change. - */ - if ((page == ZERO_PAGE(address))) - return -EFAULT; - goto again; + if (shmem_swizzled) + goto again; + return -EFAULT; } /* |