aboutsummaryrefslogtreecommitdiffstats
path: root/mm/gup.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/gup.c')
-rw-r--r--mm/gup.c46
1 files changed, 40 insertions, 6 deletions
diff --git a/mm/gup.c b/mm/gup.c
index 9c047e951aa3..c74bad1bf6e8 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -226,6 +226,7 @@ struct page *follow_page_mask(struct vm_area_struct *vma,
226 unsigned int *page_mask) 226 unsigned int *page_mask)
227{ 227{
228 pgd_t *pgd; 228 pgd_t *pgd;
229 p4d_t *p4d;
229 pud_t *pud; 230 pud_t *pud;
230 pmd_t *pmd; 231 pmd_t *pmd;
231 spinlock_t *ptl; 232 spinlock_t *ptl;
@@ -243,8 +244,13 @@ struct page *follow_page_mask(struct vm_area_struct *vma,
243 pgd = pgd_offset(mm, address); 244 pgd = pgd_offset(mm, address);
244 if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) 245 if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
245 return no_page_table(vma, flags); 246 return no_page_table(vma, flags);
246 247 p4d = p4d_offset(pgd, address);
247 pud = pud_offset(pgd, address); 248 if (p4d_none(*p4d))
249 return no_page_table(vma, flags);
250 BUILD_BUG_ON(p4d_huge(*p4d));
251 if (unlikely(p4d_bad(*p4d)))
252 return no_page_table(vma, flags);
253 pud = pud_offset(p4d, address);
248 if (pud_none(*pud)) 254 if (pud_none(*pud))
249 return no_page_table(vma, flags); 255 return no_page_table(vma, flags);
250 if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) { 256 if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
@@ -325,6 +331,7 @@ static int get_gate_page(struct mm_struct *mm, unsigned long address,
325 struct page **page) 331 struct page **page)
326{ 332{
327 pgd_t *pgd; 333 pgd_t *pgd;
334 p4d_t *p4d;
328 pud_t *pud; 335 pud_t *pud;
329 pmd_t *pmd; 336 pmd_t *pmd;
330 pte_t *pte; 337 pte_t *pte;
@@ -338,7 +345,9 @@ static int get_gate_page(struct mm_struct *mm, unsigned long address,
338 else 345 else
339 pgd = pgd_offset_gate(mm, address); 346 pgd = pgd_offset_gate(mm, address);
340 BUG_ON(pgd_none(*pgd)); 347 BUG_ON(pgd_none(*pgd));
341 pud = pud_offset(pgd, address); 348 p4d = p4d_offset(pgd, address);
349 BUG_ON(p4d_none(*p4d));
350 pud = pud_offset(p4d, address);
342 BUG_ON(pud_none(*pud)); 351 BUG_ON(pud_none(*pud));
343 pmd = pmd_offset(pud, address); 352 pmd = pmd_offset(pud, address);
344 if (pmd_none(*pmd)) 353 if (pmd_none(*pmd))
@@ -1400,13 +1409,13 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
1400 return 1; 1409 return 1;
1401} 1410}
1402 1411
1403static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end, 1412static int gup_pud_range(p4d_t p4d, unsigned long addr, unsigned long end,
1404 int write, struct page **pages, int *nr) 1413 int write, struct page **pages, int *nr)
1405{ 1414{
1406 unsigned long next; 1415 unsigned long next;
1407 pud_t *pudp; 1416 pud_t *pudp;
1408 1417
1409 pudp = pud_offset(&pgd, addr); 1418 pudp = pud_offset(&p4d, addr);
1410 do { 1419 do {
1411 pud_t pud = READ_ONCE(*pudp); 1420 pud_t pud = READ_ONCE(*pudp);
1412 1421
@@ -1428,6 +1437,31 @@ static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end,
1428 return 1; 1437 return 1;
1429} 1438}
1430 1439
1440static int gup_p4d_range(pgd_t pgd, unsigned long addr, unsigned long end,
1441 int write, struct page **pages, int *nr)
1442{
1443 unsigned long next;
1444 p4d_t *p4dp;
1445
1446 p4dp = p4d_offset(&pgd, addr);
1447 do {
1448 p4d_t p4d = READ_ONCE(*p4dp);
1449
1450 next = p4d_addr_end(addr, end);
1451 if (p4d_none(p4d))
1452 return 0;
1453 BUILD_BUG_ON(p4d_huge(p4d));
1454 if (unlikely(is_hugepd(__hugepd(p4d_val(p4d))))) {
1455 if (!gup_huge_pd(__hugepd(p4d_val(p4d)), addr,
1456 P4D_SHIFT, next, write, pages, nr))
1457 return 0;
1458 } else if (!gup_p4d_range(p4d, addr, next, write, pages, nr))
1459 return 0;
1460 } while (p4dp++, addr = next, addr != end);
1461
1462 return 1;
1463}
1464
1431/* 1465/*
1432 * Like get_user_pages_fast() except it's IRQ-safe in that it won't fall back to 1466 * Like get_user_pages_fast() except it's IRQ-safe in that it won't fall back to
1433 * the regular GUP. It will only return non-negative values. 1467 * the regular GUP. It will only return non-negative values.
@@ -1478,7 +1512,7 @@ int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
1478 if (!gup_huge_pd(__hugepd(pgd_val(pgd)), addr, 1512 if (!gup_huge_pd(__hugepd(pgd_val(pgd)), addr,
1479 PGDIR_SHIFT, next, write, pages, &nr)) 1513 PGDIR_SHIFT, next, write, pages, &nr))
1480 break; 1514 break;
1481 } else if (!gup_pud_range(pgd, addr, next, write, pages, &nr)) 1515 } else if (!gup_p4d_range(pgd, addr, next, write, pages, &nr))
1482 break; 1516 break;
1483 } while (pgdp++, addr = next, addr != end); 1517 } while (pgdp++, addr = next, addr != end);
1484 local_irq_restore(flags); 1518 local_irq_restore(flags);