Skip to content

Commit 2d32996

Browse files
djbwgregkh
authored andcommitted
mm: fix __gup_device_huge vs unmap
commit a9b6de7 upstream. get_user_pages_fast() for device pages is missing the typical validation that all page references have been taken while the mapping was valid. Without this validation truncate operations can not reliably coordinate against new page reference events like O_DIRECT. Cc: <[email protected]> Fixes: 3565fce ("mm, x86: get_user_pages() for dax mappings") Reported-by: Jan Kara <[email protected]> Reviewed-by: Jan Kara <[email protected]> Signed-off-by: Dan Williams <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5d6ad5a commit 2d32996

File tree

1 file changed

+26
-10
lines changed

1 file changed

+26
-10
lines changed

mm/gup.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,32 +1469,48 @@ static int __gup_device_huge(unsigned long pfn, unsigned long addr,
14691469
return 1;
14701470
}
14711471

1472-
static int __gup_device_huge_pmd(pmd_t pmd, unsigned long addr,
1472+
static int __gup_device_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
14731473
unsigned long end, struct page **pages, int *nr)
14741474
{
14751475
unsigned long fault_pfn;
1476+
int nr_start = *nr;
1477+
1478+
fault_pfn = pmd_pfn(orig) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
1479+
if (!__gup_device_huge(fault_pfn, addr, end, pages, nr))
1480+
return 0;
14761481

1477-
fault_pfn = pmd_pfn(pmd) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
1478-
return __gup_device_huge(fault_pfn, addr, end, pages, nr);
1482+
if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) {
1483+
undo_dev_pagemap(nr, nr_start, pages);
1484+
return 0;
1485+
}
1486+
return 1;
14791487
}
14801488

1481-
static int __gup_device_huge_pud(pud_t pud, unsigned long addr,
1489+
static int __gup_device_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
14821490
unsigned long end, struct page **pages, int *nr)
14831491
{
14841492
unsigned long fault_pfn;
1493+
int nr_start = *nr;
1494+
1495+
fault_pfn = pud_pfn(orig) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
1496+
if (!__gup_device_huge(fault_pfn, addr, end, pages, nr))
1497+
return 0;
14851498

1486-
fault_pfn = pud_pfn(pud) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
1487-
return __gup_device_huge(fault_pfn, addr, end, pages, nr);
1499+
if (unlikely(pud_val(orig) != pud_val(*pudp))) {
1500+
undo_dev_pagemap(nr, nr_start, pages);
1501+
return 0;
1502+
}
1503+
return 1;
14881504
}
14891505
#else
1490-
static int __gup_device_huge_pmd(pmd_t pmd, unsigned long addr,
1506+
static int __gup_device_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
14911507
unsigned long end, struct page **pages, int *nr)
14921508
{
14931509
BUILD_BUG();
14941510
return 0;
14951511
}
14961512

1497-
static int __gup_device_huge_pud(pud_t pud, unsigned long addr,
1513+
static int __gup_device_huge_pud(pud_t pud, pud_t *pudp, unsigned long addr,
14981514
unsigned long end, struct page **pages, int *nr)
14991515
{
15001516
BUILD_BUG();
@@ -1512,7 +1528,7 @@ static int gup_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
15121528
return 0;
15131529

15141530
if (pmd_devmap(orig))
1515-
return __gup_device_huge_pmd(orig, addr, end, pages, nr);
1531+
return __gup_device_huge_pmd(orig, pmdp, addr, end, pages, nr);
15161532

15171533
refs = 0;
15181534
page = pmd_page(orig) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
@@ -1550,7 +1566,7 @@ static int gup_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
15501566
return 0;
15511567

15521568
if (pud_devmap(orig))
1553-
return __gup_device_huge_pud(orig, addr, end, pages, nr);
1569+
return __gup_device_huge_pud(orig, pudp, addr, end, pages, nr);
15541570

15551571
refs = 0;
15561572
page = pud_page(orig) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);

0 commit comments

Comments
 (0)