diff options
| author | Keith Busch <keith.busch@intel.com> | 2018-10-26 18:10:28 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-10-26 19:38:15 -0400 |
| commit | df06b37ffe5a442503b7095b77b0a970df515459 (patch) | |
| tree | 2fdb301eba4b4c2240595040496096b3a6489c79 | |
| parent | 9fd61bc95130d4971568b89c9548b5e0a4e18e0e (diff) | |
mm/gup: cache dev_pagemap while pinning pages
Getting pages from ZONE_DEVICE memory needs to check the backing device's
live-ness, which is tracked in the device's dev_pagemap metadata. This
metadata is stored in a radix tree and looking it up adds measurable
software overhead.
This patch avoids repeating this relatively costly operation when
dev_pagemap is used by caching the last dev_pagemap while getting user
pages. The gup_benchmark kernel self test reports this reduces time to
get user pages to as low as 1/3 of the previous time.
Link: http://lkml.kernel.org/r/20181012173040.15669-1-keith.busch@intel.com
Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/huge_mm.h | 8 | ||||
| -rw-r--r-- | include/linux/mm.h | 12 | ||||
| -rw-r--r-- | mm/gup.c | 110 | ||||
| -rw-r--r-- | mm/huge_memory.c | 16 | ||||
| -rw-r--r-- | mm/nommu.c | 6 |
5 files changed, 79 insertions, 73 deletions
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index fdcb45999b26..4663ee96cf59 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h | |||
| @@ -213,9 +213,9 @@ static inline int hpage_nr_pages(struct page *page) | |||
| 213 | } | 213 | } |
| 214 | 214 | ||
| 215 | struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr, | 215 | struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr, |
| 216 | pmd_t *pmd, int flags); | 216 | pmd_t *pmd, int flags, struct dev_pagemap **pgmap); |
| 217 | struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr, | 217 | struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr, |
| 218 | pud_t *pud, int flags); | 218 | pud_t *pud, int flags, struct dev_pagemap **pgmap); |
| 219 | 219 | ||
| 220 | extern vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd); | 220 | extern vm_fault_t do_huge_pmd_numa_page(struct vm_fault *vmf, pmd_t orig_pmd); |
| 221 | 221 | ||
| @@ -344,13 +344,13 @@ static inline void mm_put_huge_zero_page(struct mm_struct *mm) | |||
| 344 | } | 344 | } |
| 345 | 345 | ||
| 346 | static inline struct page *follow_devmap_pmd(struct vm_area_struct *vma, | 346 | static inline struct page *follow_devmap_pmd(struct vm_area_struct *vma, |
| 347 | unsigned long addr, pmd_t *pmd, int flags) | 347 | unsigned long addr, pmd_t *pmd, int flags, struct dev_pagemap **pgmap) |
| 348 | { | 348 | { |
| 349 | return NULL; | 349 | return NULL; |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | static inline struct page *follow_devmap_pud(struct vm_area_struct *vma, | 352 | static inline struct page *follow_devmap_pud(struct vm_area_struct *vma, |
| 353 | unsigned long addr, pud_t *pud, int flags) | 353 | unsigned long addr, pud_t *pud, int flags, struct dev_pagemap **pgmap) |
| 354 | { | 354 | { |
| 355 | return NULL; | 355 | return NULL; |
| 356 | } | 356 | } |
diff --git a/include/linux/mm.h b/include/linux/mm.h index a023c5ce71fa..1e52b8fd1685 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
| @@ -2536,16 +2536,8 @@ static inline vm_fault_t vmf_error(int err) | |||
| 2536 | return VM_FAULT_SIGBUS; | 2536 | return VM_FAULT_SIGBUS; |
| 2537 | } | 2537 | } |
| 2538 | 2538 | ||
| 2539 | struct page *follow_page_mask(struct vm_area_struct *vma, | 2539 | struct page *follow_page(struct vm_area_struct *vma, unsigned long address, |
| 2540 | unsigned long address, unsigned int foll_flags, | 2540 | unsigned int foll_flags); |
| 2541 | unsigned int *page_mask); | ||
| 2542 | |||
| 2543 | static inline struct page *follow_page(struct vm_area_struct *vma, | ||
| 2544 | unsigned long address, unsigned int foll_flags) | ||
| 2545 | { | ||
| 2546 | unsigned int unused_page_mask; | ||
| 2547 | return follow_page_mask(vma, address, foll_flags, &unused_page_mask); | ||
| 2548 | } | ||
| 2549 | 2541 | ||
| 2550 | #define FOLL_WRITE 0x01 /* check pte is writable */ | 2542 | #define FOLL_WRITE 0x01 /* check pte is writable */ |
| 2551 | #define FOLL_TOUCH 0x02 /* mark page accessed */ | 2543 | #define FOLL_TOUCH 0x02 /* mark page accessed */ |
| @@ -20,6 +20,11 @@ | |||
| 20 | 20 | ||
| 21 | #include "internal.h" | 21 | #include "internal.h" |
| 22 | 22 | ||
| 23 | struct follow_page_context { | ||
| 24 | struct dev_pagemap *pgmap; | ||
| 25 | unsigned int page_mask; | ||
| 26 | }; | ||
| 27 | |||
| 23 | static struct page *no_page_table(struct vm_area_struct *vma, | 28 | static struct page *no_page_table(struct vm_area_struct *vma, |
| 24 | unsigned int flags) | 29 | unsigned int flags) |
| 25 | { | 30 | { |
| @@ -71,10 +76,10 @@ static inline bool can_follow_write_pte(pte_t pte, unsigned int flags) | |||
| 71 | } | 76 | } |
| 72 | 77 | ||
| 73 | static struct page *follow_page_pte(struct vm_area_struct *vma, | 78 | static struct page *follow_page_pte(struct vm_area_struct *vma, |
| 74 | unsigned long address, pmd_t *pmd, unsigned int flags) | 79 | unsigned long address, pmd_t *pmd, unsigned int flags, |
| 80 | struct dev_pagemap **pgmap) | ||
| 75 | { | 81 | { |
| 76 | struct mm_struct *mm = vma->vm_mm; | 82 | struct mm_struct *mm = vma->vm_mm; |
| 77 | struct dev_pagemap *pgmap = NULL; | ||
| 78 | struct page *page; | 83 | struct page *page; |
| 79 | spinlock_t *ptl; | 84 | spinlock_t *ptl; |
| 80 | pte_t *ptep, pte; | 85 | pte_t *ptep, pte; |
| @@ -116,8 +121,8 @@ retry: | |||
| 116 | * Only return device mapping pages in the FOLL_GET case since | 121 | * Only return device mapping pages in the FOLL_GET case since |
| 117 | * they are only valid while holding the pgmap reference. | 122 | * they are only valid while holding the pgmap reference. |
| 118 | */ | 123 | */ |
| 119 | pgmap = get_dev_pagemap(pte_pfn(pte), NULL); | 124 | *pgmap = get_dev_pagemap(pte_pfn(pte), *pgmap); |
| 120 | if (pgmap) | 125 | if (*pgmap) |
| 121 | page = pte_page(pte); | 126 | page = pte_page(pte); |
| 122 | else | 127 | else |
| 123 | goto no_page; | 128 | goto no_page; |
| @@ -152,15 +157,8 @@ retry: | |||
| 152 | goto retry; | 157 | goto retry; |
| 153 | } | 158 | } |
| 154 | 159 | ||
| 155 | if (flags & FOLL_GET) { | 160 | if (flags & FOLL_GET) |
| 156 | get_page(page); | 161 | get_page(page); |
| 157 | |||
| 158 | /* drop the pgmap reference now that we hold the page */ | ||
| 159 | if (pgmap) { | ||
| 160 | put_dev_pagemap(pgmap); | ||
| 161 | pgmap = NULL; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | if (flags & FOLL_TOUCH) { | 162 | if (flags & FOLL_TOUCH) { |
| 165 | if ((flags & FOLL_WRITE) && | 163 | if ((flags & FOLL_WRITE) && |
| 166 | !pte_dirty(pte) && !PageDirty(page)) | 164 | !pte_dirty(pte) && !PageDirty(page)) |
| @@ -210,7 +208,8 @@ no_page: | |||
| 210 | 208 | ||
| 211 | static struct page *follow_pmd_mask(struct vm_area_struct *vma, | 209 | static struct page *follow_pmd_mask(struct vm_area_struct *vma, |
| 212 | unsigned long address, pud_t *pudp, | 210 | unsigned long address, pud_t *pudp, |
| 213 | unsigned int flags, unsigned int *page_mask) | 211 | unsigned int flags, |
| 212 | struct follow_page_context *ctx) | ||
| 214 | { | 213 | { |
| 215 | pmd_t *pmd, pmdval; | 214 | pmd_t *pmd, pmdval; |
| 216 | spinlock_t *ptl; | 215 | spinlock_t *ptl; |
| @@ -258,13 +257,13 @@ retry: | |||
| 258 | } | 257 | } |
| 259 | if (pmd_devmap(pmdval)) { | 258 | if (pmd_devmap(pmdval)) { |
| 260 | ptl = pmd_lock(mm, pmd); | 259 | ptl = pmd_lock(mm, pmd); |
| 261 | page = follow_devmap_pmd(vma, address, pmd, flags); | 260 | page = follow_devmap_pmd(vma, address, pmd, flags, &ctx->pgmap); |
| 262 | spin_unlock(ptl); | 261 | spin_unlock(ptl); |
| 263 | if (page) | 262 | if (page) |
| 264 | return page; | 263 | return page; |
| 265 | } | 264 | } |
| 266 | if (likely(!pmd_trans_huge(pmdval))) | 265 | if (likely(!pmd_trans_huge(pmdval))) |
| 267 | return follow_page_pte(vma, address, pmd, flags); | 266 | return follow_page_pte(vma, address, pmd, flags, &ctx->pgmap); |
| 268 | 267 | ||
| 269 | if ((flags & FOLL_NUMA) && pmd_protnone(pmdval)) | 268 | if ((flags & FOLL_NUMA) && pmd_protnone(pmdval)) |
| 270 | return no_page_table(vma, flags); | 269 | return no_page_table(vma, flags); |
| @@ -284,7 +283,7 @@ retry_locked: | |||
| 284 | } | 283 | } |
| 285 | if (unlikely(!pmd_trans_huge(*pmd))) { | 284 | if (unlikely(!pmd_trans_huge(*pmd))) { |
| 286 | spin_unlock(ptl); | 285 | spin_unlock(ptl); |
| 287 | return follow_page_pte(vma, address, pmd, flags); | 286 | return follow_page_pte(vma, address, pmd, flags, &ctx->pgmap); |
| 288 | } | 287 | } |
| 289 | if (flags & FOLL_SPLIT) { | 288 | if (flags & FOLL_SPLIT) { |
| 290 | int ret; | 289 | int ret; |
| @@ -307,18 +306,18 @@ retry_locked: | |||
| 307 | } | 306 | } |
| 308 | 307 | ||
| 309 | return ret ? ERR_PTR(ret) : | 308 | return ret ? ERR_PTR(ret) : |
| 310 | follow_page_pte(vma, address, pmd, flags); | 309 | follow_page_pte(vma, address, pmd, flags, &ctx->pgmap); |
| 311 | } | 310 | } |
| 312 | page = follow_trans_huge_pmd(vma, address, pmd, flags); | 311 | page = follow_trans_huge_pmd(vma, address, pmd, flags); |
| 313 | spin_unlock(ptl); | 312 | spin_unlock(ptl); |
| 314 | *page_mask = HPAGE_PMD_NR - 1; | 313 | ctx->page_mask = HPAGE_PMD_NR - 1; |
| 315 | return page; | 314 | return page; |
| 316 | } | 315 | } |
| 317 | 316 | ||
| 318 | |||
| 319 | static struct page *follow_pud_mask(struct vm_area_struct *vma, | 317 | static struct page *follow_pud_mask(struct vm_area_struct *vma, |
| 320 | unsigned long address, p4d_t *p4dp, | 318 | unsigned long address, p4d_t *p4dp, |
| 321 | unsigned int flags, unsigned int *page_mask) | 319 | unsigned int flags, |
| 320 | struct follow_page_context *ctx) | ||
| 322 | { | 321 | { |
| 323 | pud_t *pud; | 322 | pud_t *pud; |
| 324 | spinlock_t *ptl; | 323 | spinlock_t *ptl; |
| @@ -344,7 +343,7 @@ static struct page *follow_pud_mask(struct vm_area_struct *vma, | |||
| 344 | } | 343 | } |
| 345 | if (pud_devmap(*pud)) { | 344 | if (pud_devmap(*pud)) { |
| 346 | ptl = pud_lock(mm, pud); | 345 | ptl = pud_lock(mm, pud); |
| 347 | page = follow_devmap_pud(vma, address, pud, flags); | 346 | page = follow_devmap_pud(vma, address, pud, flags, &ctx->pgmap); |
| 348 | spin_unlock(ptl); | 347 | spin_unlock(ptl); |
| 349 | if (page) | 348 | if (page) |
| 350 | return page; | 349 | return page; |
| @@ -352,13 +351,13 @@ static struct page *follow_pud_mask(struct vm_area_struct *vma, | |||
| 352 | if (unlikely(pud_bad(*pud))) | 351 | if (unlikely(pud_bad(*pud))) |
