diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/memory.c | 76 |
1 files changed, 75 insertions, 1 deletions
diff --git a/mm/memory.c b/mm/memory.c index 8ca6bbf34ad6..1e8f0d347c0e 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -99,6 +99,50 @@ int randomize_va_space __read_mostly = | |||
99 | 2; | 99 | 2; |
100 | #endif | 100 | #endif |
101 | 101 | ||
102 | #ifndef track_pfn_vma_new | ||
103 | /* | ||
104 | * Interface that can be used by architecture code to keep track of | ||
105 | * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) | ||
106 | * | ||
107 | * track_pfn_vma_new is called when a _new_ pfn mapping is being established | ||
108 | * for physical range indicated by pfn and size. | ||
109 | */ | ||
110 | int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot, | ||
111 | unsigned long pfn, unsigned long size) | ||
112 | { | ||
113 | return 0; | ||
114 | } | ||
115 | #endif | ||
116 | |||
117 | #ifndef track_pfn_vma_copy | ||
118 | /* | ||
119 | * Interface that can be used by architecture code to keep track of | ||
120 | * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) | ||
121 | * | ||
122 | * track_pfn_vma_copy is called when vma that is covering the pfnmap gets | ||
123 | * copied through copy_page_range(). | ||
124 | */ | ||
125 | int track_pfn_vma_copy(struct vm_area_struct *vma) | ||
126 | { | ||
127 | return 0; | ||
128 | } | ||
129 | #endif | ||
130 | |||
131 | #ifndef untrack_pfn_vma | ||
132 | /* | ||
133 | * Interface that can be used by architecture code to keep track of | ||
134 | * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) | ||
135 | * | ||
136 | * untrack_pfn_vma is called while unmapping a pfnmap for a region. | ||
137 | * untrack can be called for a specific region indicated by pfn and size or | ||
138 | * can be for the entire vma (in which case size can be zero). | ||
139 | */ | ||
140 | void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, | ||
141 | unsigned long size) | ||
142 | { | ||
143 | } | ||
144 | #endif | ||
145 | |||
102 | static int __init disable_randmaps(char *s) | 146 | static int __init disable_randmaps(char *s) |
103 | { | 147 | { |
104 | randomize_va_space = 0; | 148 | randomize_va_space = 0; |
@@ -669,6 +713,16 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, | |||
669 | if (is_vm_hugetlb_page(vma)) | 713 | if (is_vm_hugetlb_page(vma)) |
670 | return copy_hugetlb_page_range(dst_mm, src_mm, vma); | 714 | return copy_hugetlb_page_range(dst_mm, src_mm, vma); |
671 | 715 | ||
716 | if (is_pfn_mapping(vma)) { | ||
717 | /* | ||
718 | * We do not free on error cases below as remove_vma | ||
719 | * gets called on error from higher level routine | ||
720 | */ | ||
721 | ret = track_pfn_vma_copy(vma); | ||
722 | if (ret) | ||
723 | return ret; | ||
724 | } | ||
725 | |||
672 | /* | 726 | /* |
673 | * We need to invalidate the secondary MMU mappings only when | 727 | * We need to invalidate the secondary MMU mappings only when |
674 | * there could be a permission downgrade on the ptes of the | 728 | * there could be a permission downgrade on the ptes of the |
@@ -915,6 +969,9 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, | |||
915 | if (vma->vm_flags & VM_ACCOUNT) | 969 | if (vma->vm_flags & VM_ACCOUNT) |
916 | *nr_accounted += (end - start) >> PAGE_SHIFT; | 970 | *nr_accounted += (end - start) >> PAGE_SHIFT; |
917 | 971 | ||
972 | if (is_pfn_mapping(vma)) | ||
973 | untrack_pfn_vma(vma, 0, 0); | ||
974 | |||
918 | while (start != end) { | 975 | while (start != end) { |
919 | if (!tlb_start_valid) { | 976 | if (!tlb_start_valid) { |
920 | tlb_start = start; | 977 | tlb_start = start; |
@@ -1473,6 +1530,7 @@ out: | |||
1473 | int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, | 1530 | int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, |
1474 | unsigned long pfn) | 1531 | unsigned long pfn) |
1475 | { | 1532 | { |
1533 | int ret; | ||
1476 | /* | 1534 | /* |
1477 | * Technically, architectures with pte_special can avoid all these | 1535 | * Technically, architectures with pte_special can avoid all these |
1478 | * restrictions (same for remap_pfn_range). However we would like | 1536 | * restrictions (same for remap_pfn_range). However we would like |
@@ -1487,7 +1545,15 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, | |||
1487 | 1545 | ||
1488 | if (addr < vma->vm_start || addr >= vma->vm_end) | 1546 | if (addr < vma->vm_start || addr >= vma->vm_end) |
1489 | return -EFAULT; | 1547 | return -EFAULT; |
1490 | return insert_pfn(vma, addr, pfn, vma->vm_page_prot); | 1548 | if (track_pfn_vma_new(vma, vma->vm_page_prot, pfn, PAGE_SIZE)) |
1549 | return -EINVAL; | ||
1550 | |||
1551 | ret = insert_pfn(vma, addr, pfn, vma->vm_page_prot); | ||
1552 | |||
1553 | if (ret) | ||
1554 | untrack_pfn_vma(vma, pfn, PAGE_SIZE); | ||
1555 | |||
1556 | return ret; | ||
1491 | } | 1557 | } |
1492 | EXPORT_SYMBOL(vm_insert_pfn); | 1558 | EXPORT_SYMBOL(vm_insert_pfn); |
1493 | 1559 | ||
@@ -1625,6 +1691,10 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | |||
1625 | 1691 | ||
1626 | vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; | 1692 | vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; |
1627 | 1693 | ||
1694 | err = track_pfn_vma_new(vma, prot, pfn, PAGE_ALIGN(size)); | ||
1695 | if (err) | ||
1696 | return -EINVAL; | ||
1697 | |||
1628 | BUG_ON(addr >= end); | 1698 | BUG_ON(addr >= end); |
1629 | pfn -= addr >> PAGE_SHIFT; | 1699 | pfn -= addr >> PAGE_SHIFT; |
1630 | pgd = pgd_offset(mm, addr); | 1700 | pgd = pgd_offset(mm, addr); |
@@ -1636,6 +1706,10 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | |||
1636 | if (err) | 1706 | if (err) |
1637 | break; | 1707 | break; |
1638 | } while (pgd++, addr = next, addr != end); | 1708 | } while (pgd++, addr = next, addr != end); |
1709 | |||
1710 | if (err) | ||
1711 | untrack_pfn_vma(vma, pfn, PAGE_ALIGN(size)); | ||
1712 | |||
1639 | return err; | 1713 | return err; |
1640 | } | 1714 | } |
1641 | EXPORT_SYMBOL(remap_pfn_range); | 1715 | EXPORT_SYMBOL(remap_pfn_range); |