diff options
Diffstat (limited to 'mm/gup.c')
-rw-r--r-- | mm/gup.c | 46 |
1 files changed, 40 insertions, 6 deletions
@@ -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 | ||
1403 | static int gup_pud_range(pgd_t pgd, unsigned long addr, unsigned long end, | 1412 | static 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 | ||
1440 | static 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); |