diff options
-rw-r--r-- | arch/x86/mm/pat.c | 47 | ||||
-rw-r--r-- | include/asm-generic/pgtable.h | 55 | ||||
-rw-r--r-- | mm/memory.c | 13 |
3 files changed, 73 insertions, 42 deletions
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index de36c886cd38..74a702674e86 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c | |||
@@ -664,13 +664,13 @@ static void free_pfn_range(u64 paddr, unsigned long size) | |||
664 | } | 664 | } |
665 | 665 | ||
666 | /* | 666 | /* |
667 | * track_pfn_vma_copy is called when vma that is covering the pfnmap gets | 667 | * track_pfn_copy is called when vma that is covering the pfnmap gets |
668 | * copied through copy_page_range(). | 668 | * copied through copy_page_range(). |
669 | * | 669 | * |
670 | * If the vma has a linear pfn mapping for the entire range, we get the prot | 670 | * If the vma has a linear pfn mapping for the entire range, we get the prot |
671 | * from pte and reserve the entire vma range with single reserve_pfn_range call. | 671 | * from pte and reserve the entire vma range with single reserve_pfn_range call. |
672 | */ | 672 | */ |
673 | int track_pfn_vma_copy(struct vm_area_struct *vma) | 673 | int track_pfn_copy(struct vm_area_struct *vma) |
674 | { | 674 | { |
675 | resource_size_t paddr; | 675 | resource_size_t paddr; |
676 | unsigned long prot; | 676 | unsigned long prot; |
@@ -694,15 +694,12 @@ int track_pfn_vma_copy(struct vm_area_struct *vma) | |||
694 | } | 694 | } |
695 | 695 | ||
696 | /* | 696 | /* |
697 | * track_pfn_vma_new is called when a _new_ pfn mapping is being established | ||
698 | * for physical range indicated by pfn and size. | ||
699 | * | ||
700 | * prot is passed in as a parameter for the new mapping. If the vma has a | 697 | * prot is passed in as a parameter for the new mapping. If the vma has a |
701 | * linear pfn mapping for the entire range reserve the entire vma range with | 698 | * linear pfn mapping for the entire range reserve the entire vma range with |
702 | * single reserve_pfn_range call. | 699 | * single reserve_pfn_range call. |
703 | */ | 700 | */ |
704 | int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, | 701 | int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot, |
705 | unsigned long pfn, unsigned long size) | 702 | unsigned long pfn, unsigned long size) |
706 | { | 703 | { |
707 | resource_size_t paddr = (resource_size_t)pfn << PAGE_SHIFT; | 704 | resource_size_t paddr = (resource_size_t)pfn << PAGE_SHIFT; |
708 | unsigned long flags; | 705 | unsigned long flags; |
@@ -714,8 +711,36 @@ int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, | |||
714 | if (!pat_enabled) | 711 | if (!pat_enabled) |
715 | return 0; | 712 | return 0; |
716 | 713 | ||
717 | /* for vm_insert_pfn and friends, we set prot based on lookup */ | 714 | /* |
715 | * For anything smaller than the vma size we set prot based on the | ||
716 | * lookup. | ||
717 | */ | ||
718 | flags = lookup_memtype(paddr); | 718 | flags = lookup_memtype(paddr); |
719 | |||
720 | /* Check memtype for the remaining pages */ | ||
721 | while (size > PAGE_SIZE) { | ||
722 | size -= PAGE_SIZE; | ||
723 | paddr += PAGE_SIZE; | ||
724 | if (flags != lookup_memtype(paddr)) | ||
725 | return -EINVAL; | ||
726 | } | ||
727 | |||
728 | *prot = __pgprot((pgprot_val(vma->vm_page_prot) & (~_PAGE_CACHE_MASK)) | | ||
729 | flags); | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot, | ||
735 | unsigned long pfn) | ||
736 | { | ||
737 | unsigned long flags; | ||
738 | |||
739 | if (!pat_enabled) | ||
740 | return 0; | ||
741 | |||
742 | /* Set prot based on lookup */ | ||
743 | flags = lookup_memtype((resource_size_t)pfn << PAGE_SHIFT); | ||
719 | *prot = __pgprot((pgprot_val(vma->vm_page_prot) & (~_PAGE_CACHE_MASK)) | | 744 | *prot = __pgprot((pgprot_val(vma->vm_page_prot) & (~_PAGE_CACHE_MASK)) | |
720 | flags); | 745 | flags); |
721 | 746 | ||
@@ -723,12 +748,12 @@ int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, | |||
723 | } | 748 | } |
724 | 749 | ||
725 | /* | 750 | /* |
726 | * untrack_pfn_vma is called while unmapping a pfnmap for a region. | 751 | * untrack_pfn is called while unmapping a pfnmap for a region. |
727 | * untrack can be called for a specific region indicated by pfn and size or | 752 | * untrack can be called for a specific region indicated by pfn and size or |
728 | * can be for the entire vma (in which case pfn, size are zero). | 753 | * can be for the entire vma (in which case pfn, size are zero). |
729 | */ | 754 | */ |
730 | void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, | 755 | void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, |
731 | unsigned long size) | 756 | unsigned long size) |
732 | { | 757 | { |
733 | resource_size_t paddr; | 758 | resource_size_t paddr; |
734 | unsigned long prot; | 759 | unsigned long prot; |
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index ff4947b7a976..d4d4592c97fc 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h | |||
@@ -381,48 +381,57 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, | |||
381 | 381 | ||
382 | #ifndef __HAVE_PFNMAP_TRACKING | 382 | #ifndef __HAVE_PFNMAP_TRACKING |
383 | /* | 383 | /* |
384 | * Interface that can be used by architecture code to keep track of | 384 | * Interfaces that can be used by architecture code to keep track of |
385 | * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) | 385 | * memory type of pfn mappings specified by the remap_pfn_range, |
386 | * | 386 | * vm_insert_pfn. |
387 | * track_pfn_vma_new is called when a _new_ pfn mapping is being established | 387 | */ |
388 | * for physical range indicated by pfn and size. | 388 | |
389 | /* | ||
390 | * track_pfn_remap is called when a _new_ pfn mapping is being established | ||
391 | * by remap_pfn_range() for physical range indicated by pfn and size. | ||
389 | */ | 392 | */ |
390 | static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, | 393 | static inline int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot, |
391 | unsigned long pfn, unsigned long size) | 394 | unsigned long pfn, unsigned long size) |
392 | { | 395 | { |
393 | return 0; | 396 | return 0; |
394 | } | 397 | } |
395 | 398 | ||
396 | /* | 399 | /* |
397 | * Interface that can be used by architecture code to keep track of | 400 | * track_pfn_insert is called when a _new_ single pfn is established |
398 | * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) | 401 | * by vm_insert_pfn(). |
399 | * | 402 | */ |
400 | * track_pfn_vma_copy is called when vma that is covering the pfnmap gets | 403 | static inline int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot, |
404 | unsigned long pfn) | ||
405 | { | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * track_pfn_copy is called when vma that is covering the pfnmap gets | ||
401 | * copied through copy_page_range(). | 411 | * copied through copy_page_range(). |
402 | */ | 412 | */ |
403 | static inline int track_pfn_vma_copy(struct vm_area_struct *vma) | 413 | static inline int track_pfn_copy(struct vm_area_struct *vma) |
404 | { | 414 | { |
405 | return 0; | 415 | return 0; |
406 | } | 416 | } |
407 | 417 | ||
408 | /* | 418 | /* |
409 | * Interface that can be used by architecture code to keep track of | ||
410 | * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) | ||
411 | * | ||
412 | * untrack_pfn_vma is called while unmapping a pfnmap for a region. | 419 | * untrack_pfn_vma is called while unmapping a pfnmap for a region. |
413 | * untrack can be called for a specific region indicated by pfn and size or | 420 | * untrack can be called for a specific region indicated by pfn and size or |
414 | * can be for the entire vma (in which case size can be zero). | 421 | * can be for the entire vma (in which case pfn, size are zero). |
415 | */ | 422 | */ |
416 | static inline void untrack_pfn_vma(struct vm_area_struct *vma, | 423 | static inline void untrack_pfn(struct vm_area_struct *vma, |
417 | unsigned long pfn, unsigned long size) | 424 | unsigned long pfn, unsigned long size) |
418 | { | 425 | { |
419 | } | 426 | } |
420 | #else | 427 | #else |
421 | extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, | 428 | extern int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot, |
422 | unsigned long pfn, unsigned long size); | 429 | unsigned long pfn, unsigned long size); |
423 | extern int track_pfn_vma_copy(struct vm_area_struct *vma); | 430 | extern int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot, |
424 | extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, | 431 | unsigned long pfn); |
425 | unsigned long size); | 432 | extern int track_pfn_copy(struct vm_area_struct *vma); |
433 | extern void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn, | ||
434 | unsigned long size); | ||
426 | #endif | 435 | #endif |
427 | 436 | ||
428 | #ifdef CONFIG_MMU | 437 | #ifdef CONFIG_MMU |
diff --git a/mm/memory.c b/mm/memory.c index 57361708d1a5..6bef278ad303 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -1060,7 +1060,7 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, | |||
1060 | * We do not free on error cases below as remove_vma | 1060 | * We do not free on error cases below as remove_vma |
1061 | * gets called on error from higher level routine | 1061 | * gets called on error from higher level routine |
1062 | */ | 1062 | */ |
1063 | ret = track_pfn_vma_copy(vma); | 1063 | ret = track_pfn_copy(vma); |
1064 | if (ret) | 1064 | if (ret) |
1065 | return ret; | 1065 | return ret; |
1066 | } | 1066 | } |
@@ -1328,7 +1328,7 @@ static void unmap_single_vma(struct mmu_gather *tlb, | |||
1328 | uprobe_munmap(vma, start, end); | 1328 | uprobe_munmap(vma, start, end); |
1329 | 1329 | ||
1330 | if (unlikely(is_pfn_mapping(vma))) | 1330 | if (unlikely(is_pfn_mapping(vma))) |
1331 | untrack_pfn_vma(vma, 0, 0); | 1331 | untrack_pfn(vma, 0, 0); |
1332 | 1332 | ||
1333 | if (start != end) { | 1333 | if (start != end) { |
1334 | if (unlikely(is_vm_hugetlb_page(vma))) { | 1334 | if (unlikely(is_vm_hugetlb_page(vma))) { |
@@ -2162,14 +2162,11 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, | |||
2162 | 2162 | ||
2163 | if (addr < vma->vm_start || addr >= vma->vm_end) | 2163 | if (addr < vma->vm_start || addr >= vma->vm_end) |
2164 | return -EFAULT; | 2164 | return -EFAULT; |
2165 | if (track_pfn_vma_new(vma, &pgprot, pfn, PAGE_SIZE)) | 2165 | if (track_pfn_insert(vma, &pgprot, pfn)) |
2166 | return -EINVAL; | 2166 | return -EINVAL; |
2167 | 2167 | ||
2168 | ret = insert_pfn(vma, addr, pfn, pgprot); | 2168 | ret = insert_pfn(vma, addr, pfn, pgprot); |
2169 | 2169 | ||
2170 | if (ret) | ||
2171 | untrack_pfn_vma(vma, pfn, PAGE_SIZE); | ||
2172 | |||
2173 | return ret; | 2170 | return ret; |
2174 | } | 2171 | } |
2175 | EXPORT_SYMBOL(vm_insert_pfn); | 2172 | EXPORT_SYMBOL(vm_insert_pfn); |
@@ -2311,7 +2308,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | |||
2311 | 2308 | ||
2312 | vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; | 2309 | vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; |
2313 | 2310 | ||
2314 | err = track_pfn_vma_new(vma, &prot, pfn, PAGE_ALIGN(size)); | 2311 | err = track_pfn_remap(vma, &prot, pfn, PAGE_ALIGN(size)); |
2315 | if (err) { | 2312 | if (err) { |
2316 | /* | 2313 | /* |
2317 | * To indicate that track_pfn related cleanup is not | 2314 | * To indicate that track_pfn related cleanup is not |
@@ -2335,7 +2332,7 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | |||
2335 | } while (pgd++, addr = next, addr != end); | 2332 | } while (pgd++, addr = next, addr != end); |
2336 | 2333 | ||
2337 | if (err) | 2334 | if (err) |
2338 | untrack_pfn_vma(vma, pfn, PAGE_ALIGN(size)); | 2335 | untrack_pfn(vma, pfn, PAGE_ALIGN(size)); |
2339 | 2336 | ||
2340 | return err; | 2337 | return err; |
2341 | } | 2338 | } |