BACKPORT: mm, mempolicy: fix up gup usage in lookup_node
ba841078cd05 ("mm/mempolicy: Allow lookup_node() to handle fatal signal")
has added a special casing for 0 return value because that was a possible
gup return value when interrupted by fatal signal. This has been fixed by
ae46d2aa6a7f ("mm/gup: Let __get_user_pages_locked() return -EINTR for
fatal signal") in the mean time so ba841078cd05 can be reverted.
This patch however doesn't go all the way to revert it because the check
for 0 is wrong and confusing here. Firstly it is inherently unsafe to
access the page when get_user_pages_locked returns 0 (aka no page
returned).
Fortunatelly this will not happen because get_user_pages_locked will not
return 0 when nr_pages > 0 unless FOLL_NOWAIT is specified which is not
the case here. Document this potential error code in gup code while we
are at it.
Signed-off-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Peter Xu <peterx@redhat.com>
Link: http://lkml.kernel.org/r/20200421071026.18394-1-mhocko@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
(cherry picked from commit 2d3a36a47964371101d9a71691c18d59ee611e87)
[Kalesh Singh: Resolve conflict in mm/gup.c]
Bug: 176847924
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
Change-Id: Idac45e534bba1524a696993447220d12332ced05
This commit is contained in:
21
mm/gup.c
21
mm/gup.c
@@ -632,11 +632,18 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
|
||||
* Or NULL if the caller does not require them.
|
||||
* @locked: whether we're still with the mmap_sem held
|
||||
*
|
||||
* Returns number of pages pinned. This may be fewer than the number
|
||||
* requested. If nr_pages is 0 or negative, returns 0. If no pages
|
||||
* were pinned, returns -errno. Each page returned must be released
|
||||
* with a put_page() call when it is finished with. vmas will only
|
||||
* remain valid while mmap_sem is held.
|
||||
* Returns either number of pages pinned (which may be less than the
|
||||
* number requested), or an error. Details about the return value:
|
||||
*
|
||||
* -- If nr_pages is 0, returns 0.
|
||||
* -- If nr_pages is >0, but no pages were pinned, returns -errno.
|
||||
* -- If nr_pages is >0, and some pages were pinned, returns the number of
|
||||
* pages pinned. Again, this may be less than nr_pages.
|
||||
* -- 0 return value is possible when the fault would need to be retried.
|
||||
*
|
||||
* The caller is responsible for releasing returned @pages, via put_page().
|
||||
*
|
||||
* @vmas are valid only as long as mmap_sem is held.
|
||||
*
|
||||
* Must be called with mmap_sem held. It may be released. See below.
|
||||
*
|
||||
@@ -908,6 +915,10 @@ retry:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fixup_user_fault);
|
||||
|
||||
/*
|
||||
* Please note that this function, unlike __get_user_pages will not
|
||||
* return 0 for nr_pages > 0 without FOLL_NOWAIT
|
||||
*/
|
||||
static __always_inline long __get_user_pages_locked(struct task_struct *tsk,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start,
|
||||
|
||||
@@ -861,10 +861,7 @@ static int lookup_node(unsigned long addr)
|
||||
int err;
|
||||
|
||||
err = get_user_pages(addr & PAGE_MASK, 1, 0, &p, NULL);
|
||||
if (err == 0) {
|
||||
/* E.g. GUP interrupted by fatal signal */
|
||||
err = -EFAULT;
|
||||
} else if (err > 0) {
|
||||
if (err > 0) {
|
||||
err = page_to_nid(p);
|
||||
put_page(p);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user