diff options
| -rw-r--r-- | arch/x86/mm/pat.c | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c index f88ac80530c0..8b08fb955274 100644 --- a/arch/x86/mm/pat.c +++ b/arch/x86/mm/pat.c | |||
| @@ -601,12 +601,13 @@ void unmap_devmem(unsigned long pfn, unsigned long size, pgprot_t vma_prot) | |||
| 601 | * Reserved non RAM regions only and after successful reserve_memtype, | 601 | * Reserved non RAM regions only and after successful reserve_memtype, |
| 602 | * this func also keeps identity mapping (if any) in sync with this new prot. | 602 | * this func also keeps identity mapping (if any) in sync with this new prot. |
| 603 | */ | 603 | */ |
| 604 | static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot) | 604 | static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot, |
| 605 | int strict_prot) | ||
| 605 | { | 606 | { |
| 606 | int is_ram = 0; | 607 | int is_ram = 0; |
| 607 | int id_sz, ret; | 608 | int id_sz, ret; |
| 608 | unsigned long flags; | 609 | unsigned long flags; |
| 609 | unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK); | 610 | unsigned long want_flags = (pgprot_val(*vma_prot) & _PAGE_CACHE_MASK); |
| 610 | 611 | ||
| 611 | is_ram = pagerange_is_ram(paddr, paddr + size); | 612 | is_ram = pagerange_is_ram(paddr, paddr + size); |
| 612 | 613 | ||
| @@ -625,15 +626,24 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot) | |||
| 625 | return ret; | 626 | return ret; |
| 626 | 627 | ||
| 627 | if (flags != want_flags) { | 628 | if (flags != want_flags) { |
| 628 | free_memtype(paddr, paddr + size); | 629 | if (strict_prot || !is_new_memtype_allowed(want_flags, flags)) { |
| 629 | printk(KERN_ERR | 630 | free_memtype(paddr, paddr + size); |
| 630 | "%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n", | 631 | printk(KERN_ERR "%s:%d map pfn expected mapping type %s" |
| 631 | current->comm, current->pid, | 632 | " for %Lx-%Lx, got %s\n", |
| 632 | cattr_name(want_flags), | 633 | current->comm, current->pid, |
| 633 | (unsigned long long)paddr, | 634 | cattr_name(want_flags), |
| 634 | (unsigned long long)(paddr + size), | 635 | (unsigned long long)paddr, |
| 635 | cattr_name(flags)); | 636 | (unsigned long long)(paddr + size), |
| 636 | return -EINVAL; | 637 | cattr_name(flags)); |
| 638 | return -EINVAL; | ||
| 639 | } | ||
| 640 | /* | ||
| 641 | * We allow returning different type than the one requested in | ||
| 642 | * non strict case. | ||
| 643 | */ | ||
| 644 | *vma_prot = __pgprot((pgprot_val(*vma_prot) & | ||
| 645 | (~_PAGE_CACHE_MASK)) | | ||
| 646 | flags); | ||
| 637 | } | 647 | } |
| 638 | 648 | ||
| 639 | /* Need to keep identity mapping in sync */ | 649 | /* Need to keep identity mapping in sync */ |
| @@ -689,6 +699,7 @@ int track_pfn_vma_copy(struct vm_area_struct *vma) | |||
| 689 | unsigned long vma_start = vma->vm_start; | 699 | unsigned long vma_start = vma->vm_start; |
| 690 | unsigned long vma_end = vma->vm_end; | 700 | unsigned long vma_end = vma->vm_end; |
| 691 | unsigned long vma_size = vma_end - vma_start; | 701 | unsigned long vma_size = vma_end - vma_start; |
| 702 | pgprot_t pgprot; | ||
| 692 | 703 | ||
| 693 | if (!pat_enabled) | 704 | if (!pat_enabled) |
| 694 | return 0; | 705 | return 0; |
| @@ -702,7 +713,8 @@ int track_pfn_vma_copy(struct vm_area_struct *vma) | |||
| 702 | WARN_ON_ONCE(1); | 713 | WARN_ON_ONCE(1); |
| 703 | return -EINVAL; | 714 | return -EINVAL; |
| 704 | } | 715 | } |
| 705 | return reserve_pfn_range(paddr, vma_size, __pgprot(prot)); | 716 | pgprot = __pgprot(prot); |
| 717 | return reserve_pfn_range(paddr, vma_size, &pgprot, 1); | ||
| 706 | } | 718 | } |
| 707 | 719 | ||
| 708 | /* reserve entire vma page by page, using pfn and prot from pte */ | 720 | /* reserve entire vma page by page, using pfn and prot from pte */ |
| @@ -710,7 +722,8 @@ int track_pfn_vma_copy(struct vm_area_struct *vma) | |||
| 710 | if (follow_phys(vma, vma_start + i, 0, &prot, &paddr)) | 722 | if (follow_phys(vma, vma_start + i, 0, &prot, &paddr)) |
| 711 | continue; | 723 | continue; |
| 712 | 724 | ||
| 713 | retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot)); | 725 | pgprot = __pgprot(prot); |
| 726 | retval = reserve_pfn_range(paddr, PAGE_SIZE, &pgprot, 1); | ||
| 714 | if (retval) | 727 | if (retval) |
| 715 | goto cleanup_ret; | 728 | goto cleanup_ret; |
| 716 | } | 729 | } |
| @@ -758,14 +771,14 @@ int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, | |||
| 758 | if (is_linear_pfn_mapping(vma)) { | 771 | if (is_linear_pfn_mapping(vma)) { |
| 759 | /* reserve the whole chunk starting from vm_pgoff */ | 772 | /* reserve the whole chunk starting from vm_pgoff */ |
| 760 | paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; | 773 | paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; |
| 761 | return reserve_pfn_range(paddr, vma_size, *prot); | 774 | return reserve_pfn_range(paddr, vma_size, prot, 0); |
| 762 | } | 775 | } |
| 763 | 776 | ||
| 764 | /* reserve page by page using pfn and size */ | 777 | /* reserve page by page using pfn and size */ |
| 765 | base_paddr = (resource_size_t)pfn << PAGE_SHIFT; | 778 | base_paddr = (resource_size_t)pfn << PAGE_SHIFT; |
| 766 | for (i = 0; i < size; i += PAGE_SIZE) { | 779 | for (i = 0; i < size; i += PAGE_SIZE) { |
| 767 | paddr = base_paddr + i; | 780 | paddr = base_paddr + i; |
| 768 | retval = reserve_pfn_range(paddr, PAGE_SIZE, *prot); | 781 | retval = reserve_pfn_range(paddr, PAGE_SIZE, prot, 0); |
| 769 | if (retval) | 782 | if (retval) |
| 770 | goto cleanup_ret; | 783 | goto cleanup_ret; |
| 771 | } | 784 | } |
