Skip to content

Commit a9b6de7

Browse files
committed
mm: fix __gup_device_huge vs unmap
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]>
1 parent e763848 commit a9b6de7

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
@@ -1456,32 +1456,48 @@ static int __gup_device_huge(unsigned long pfn, unsigned long addr,
14561456
return 1;
14571457
}
14581458

1459-
static int __gup_device_huge_pmd(pmd_t pmd, unsigned long addr,
1459+
static int __gup_device_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
14601460
unsigned long end, struct page **pages, int *nr)
14611461
{
14621462
unsigned long fault_pfn;
1463+
int nr_start = *nr;
1464+
1465+
fault_pfn = pmd_pfn(orig) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
1466+
if (!__gup_device_huge(fault_pfn, addr, end, pages, nr))
1467+
return 0;
14631468

1464-
fault_pfn = pmd_pfn(pmd) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
1465-
return __gup_device_huge(fault_pfn, addr, end, pages, nr);
1469+
if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) {
1470+
undo_dev_pagemap(nr, nr_start, pages);
1471+
return 0;
1472+
}
1473+
return 1;
14661474
}
14671475

1468-
static int __gup_device_huge_pud(pud_t pud, unsigned long addr,
1476+
static int __gup_device_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
14691477
unsigned long end, struct page **pages, int *nr)
14701478
{
14711479
unsigned long fault_pfn;
1480+
int nr_start = *nr;
1481+
1482+
fault_pfn = pud_pfn(orig) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
1483+
if (!__gup_device_huge(fault_pfn, addr, end, pages, nr))
1484+
return 0;
14721485

1473-
fault_pfn = pud_pfn(pud) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
1474-
return __gup_device_huge(fault_pfn, addr, end, pages, nr);
1486+
if (unlikely(pud_val(orig) != pud_val(*pudp))) {
1487+
undo_dev_pagemap(nr, nr_start, pages);
1488+
return 0;
1489+
}
1490+
return 1;
14751491
}
14761492
#else
1477-
static int __gup_device_huge_pmd(pmd_t pmd, unsigned long addr,
1493+
static int __gup_device_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
14781494
unsigned long end, struct page **pages, int *nr)
14791495
{
14801496
BUILD_BUG();
14811497
return 0;
14821498
}
14831499

1484-
static int __gup_device_huge_pud(pud_t pud, unsigned long addr,
1500+
static int __gup_device_huge_pud(pud_t pud, pud_t *pudp, unsigned long addr,
14851501
unsigned long end, struct page **pages, int *nr)
14861502
{
14871503
BUILD_BUG();
@@ -1499,7 +1515,7 @@ static int gup_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr,
14991515
return 0;
15001516

15011517
if (pmd_devmap(orig))
1502-
return __gup_device_huge_pmd(orig, addr, end, pages, nr);
1518+
return __gup_device_huge_pmd(orig, pmdp, addr, end, pages, nr);
15031519

15041520
refs = 0;
15051521
page = pmd_page(orig) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
@@ -1537,7 +1553,7 @@ static int gup_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr,
15371553
return 0;
15381554

15391555
if (pud_devmap(orig))
1540-
return __gup_device_huge_pud(orig, addr, end, pages, nr);
1556+
return __gup_device_huge_pud(orig, pudp, addr, end, pages, nr);
15411557

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

0 commit comments

Comments
 (0)