Blame view
mm/fremap.c
6.73 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 |
/* * linux/mm/fremap.c * * Explicit pagetable population and nonlinear (random) mappings support. * * started by Ingo Molnar, Copyright (C) 2002, 2003 */ |
4af3c9cc4 Drop some headers... |
8 |
#include <linux/backing-dev.h> |
1da177e4c Linux-2.6.12-rc2 |
9 10 11 12 13 14 15 16 17 |
#include <linux/mm.h> #include <linux/swap.h> #include <linux/file.h> #include <linux/mman.h> #include <linux/pagemap.h> #include <linux/swapops.h> #include <linux/rmap.h> #include <linux/module.h> #include <linux/syscalls.h> |
cddb8a5c1 mmu-notifiers: core |
18 |
#include <linux/mmu_notifier.h> |
1da177e4c Linux-2.6.12-rc2 |
19 20 21 22 |
#include <asm/mmu_context.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> |
ba470de43 mmap: handle mloc... |
23 |
#include "internal.h" |
d0217ac04 mm: fault feedbac... |
24 |
static void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma, |
1da177e4c Linux-2.6.12-rc2 |
25 26 27 |
unsigned long addr, pte_t *ptep) { pte_t pte = *ptep; |
1da177e4c Linux-2.6.12-rc2 |
28 |
if (pte_present(pte)) { |
d0217ac04 mm: fault feedbac... |
29 |
struct page *page; |
6aab341e0 mm: re-architect ... |
30 |
flush_cache_page(vma, addr, pte_pfn(pte)); |
1da177e4c Linux-2.6.12-rc2 |
31 |
pte = ptep_clear_flush(vma, addr, ptep); |
6aab341e0 mm: re-architect ... |
32 33 34 35 |
page = vm_normal_page(vma, addr, pte); if (page) { if (pte_dirty(pte)) set_page_dirty(page); |
edc315fd2 badpage: remove v... |
36 |
page_remove_rmap(page); |
6aab341e0 mm: re-architect ... |
37 |
page_cache_release(page); |
d0217ac04 mm: fault feedbac... |
38 |
update_hiwater_rss(mm); |
d559db086 mm: clean up mm_c... |
39 |
dec_mm_counter(mm, MM_FILEPAGES); |
1da177e4c Linux-2.6.12-rc2 |
40 41 42 43 |
} } else { if (!pte_file(pte)) free_swap_and_cache(pte_to_swp_entry(pte)); |
9888a1cae [PATCH] paravirt:... |
44 |
pte_clear_not_present_full(mm, addr, ptep, 0); |
1da177e4c Linux-2.6.12-rc2 |
45 46 47 48 |
} } /* |
1da177e4c Linux-2.6.12-rc2 |
49 50 51 |
* Install a file pte to a given virtual memory address, release any * previously existing mapping. */ |
d0217ac04 mm: fault feedbac... |
52 |
static int install_file_pte(struct mm_struct *mm, struct vm_area_struct *vma, |
1da177e4c Linux-2.6.12-rc2 |
53 54 55 56 |
unsigned long addr, unsigned long pgoff, pgprot_t prot) { int err = -ENOMEM; pte_t *pte; |
c74df32c7 [PATCH] mm: ptd_a... |
57 |
spinlock_t *ptl; |
1da177e4c Linux-2.6.12-rc2 |
58 |
|
c9cfcddfd VM: add common he... |
59 |
pte = get_locked_pte(mm, addr, &ptl); |
1da177e4c Linux-2.6.12-rc2 |
60 |
if (!pte) |
c74df32c7 [PATCH] mm: ptd_a... |
61 |
goto out; |
1da177e4c Linux-2.6.12-rc2 |
62 |
|
d0217ac04 mm: fault feedbac... |
63 64 |
if (!pte_none(*pte)) zap_pte(mm, vma, addr, pte); |
1da177e4c Linux-2.6.12-rc2 |
65 66 |
set_pte_at(mm, addr, pte, pgoff_to_pte(pgoff)); |
668e0d8f1 [PATCH] fix updat... |
67 68 69 70 71 72 73 |
/* * We don't need to run update_mmu_cache() here because the "file pte" * being installed by install_file_pte() is not a real pte - it's a * non-present entry (like a swap entry), noting what file offset should * be mapped there when there's a fault (in a non-linear vma where * that's not obvious). */ |
c74df32c7 [PATCH] mm: ptd_a... |
74 75 76 |
pte_unmap_unlock(pte, ptl); err = 0; out: |
1da177e4c Linux-2.6.12-rc2 |
77 78 |
return err; } |
54cb8821d mm: merge populat... |
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
static int populate_range(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long addr, unsigned long size, pgoff_t pgoff) { int err; do { err = install_file_pte(mm, vma, addr, pgoff, vma->vm_page_prot); if (err) return err; size -= PAGE_SIZE; addr += PAGE_SIZE; pgoff++; } while (size); return 0; } |
8d63494f7 remap_file_pages:... |
97 98 |
/** * sys_remap_file_pages - remap arbitrary pages of an existing VM_SHARED vma |
1da177e4c Linux-2.6.12-rc2 |
99 100 |
* @start: start of the remapped virtual memory range * @size: size of the remapped virtual memory range |
8d63494f7 remap_file_pages:... |
101 102 |
* @prot: new protection bits of the range (see NOTE) * @pgoff: to-be-mapped page of the backing store file |
1da177e4c Linux-2.6.12-rc2 |
103 104 |
* @flags: 0 or MAP_NONBLOCKED - the later will cause no IO. * |
8d63494f7 remap_file_pages:... |
105 106 107 108 |
* sys_remap_file_pages remaps arbitrary pages of an existing VM_SHARED vma * (shared backing store file). * * This syscall works purely via pagetables, so it's the most efficient |
1da177e4c Linux-2.6.12-rc2 |
109 110 111 112 |
* way to map the same (large) file into a given virtual window. Unlike * mmap()/mremap() it does not create any new vmas. The new mappings are * also safe across swapout. * |
7682486b3 mm: fix various k... |
113 |
* NOTE: the @prot parameter right now is ignored (but must be zero), |
8d63494f7 remap_file_pages:... |
114 115 |
* and the vma's default protection is used. Arbitrary protections * might be implemented in the future. |
1da177e4c Linux-2.6.12-rc2 |
116 |
*/ |
6a6160a7b [CVE-2009-0029] S... |
117 118 |
SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, unsigned long, prot, unsigned long, pgoff, unsigned long, flags) |
1da177e4c Linux-2.6.12-rc2 |
119 120 121 |
{ struct mm_struct *mm = current->mm; struct address_space *mapping; |
1da177e4c Linux-2.6.12-rc2 |
122 123 124 |
struct vm_area_struct *vma; int err = -EINVAL; int has_write_lock = 0; |
8d63494f7 remap_file_pages:... |
125 |
if (prot) |
1da177e4c Linux-2.6.12-rc2 |
126 127 128 129 130 131 132 133 134 135 |
return err; /* * Sanitize the syscall parameters: */ start = start & PAGE_MASK; size = size & PAGE_MASK; /* Does the address range wrap, or is the span zero-sized? */ if (start + size <= start) return err; |
5ec1055aa Avoid pgoff overf... |
136 137 138 |
/* Does pgoff wrap? */ if (pgoff + (size >> PAGE_SHIFT) < pgoff) return err; |
1da177e4c Linux-2.6.12-rc2 |
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
/* Can we represent this offset inside this architecture's pte's? */ #if PTE_FILE_MAX_BITS < BITS_PER_LONG if (pgoff + (size >> PAGE_SHIFT) >= (1UL << PTE_FILE_MAX_BITS)) return err; #endif /* We need down_write() to change vma->vm_flags. */ down_read(&mm->mmap_sem); retry: vma = find_vma(mm, start); /* * Make sure the vma is shared, that it supports prefaulting, * and that the remapped range is valid and fully within * the single existing vma. vm_private_data is used as a |
101d2be76 [PATCH] unpaged: ... |
154 |
* swapout cursor in a VM_NONLINEAR vma. |
1da177e4c Linux-2.6.12-rc2 |
155 |
*/ |
54cb8821d mm: merge populat... |
156 157 158 159 160 |
if (!vma || !(vma->vm_flags & VM_SHARED)) goto out; if (vma->vm_private_data && !(vma->vm_flags & VM_NONLINEAR)) goto out; |
dd204d63c fix VM_CAN_NONLIN... |
161 |
if (!(vma->vm_flags & VM_CAN_NONLINEAR)) |
54cb8821d mm: merge populat... |
162 |
goto out; |
e92b05dec fremap: get rid o... |
163 |
if (start < vma->vm_start || start + size > vma->vm_end) |
54cb8821d mm: merge populat... |
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
goto out; /* Must set VM_NONLINEAR before any pages are populated. */ if (!(vma->vm_flags & VM_NONLINEAR)) { /* Don't need a nonlinear mapping, exit success */ if (pgoff == linear_page_index(vma, start)) { err = 0; goto out; } if (!has_write_lock) { up_read(&mm->mmap_sem); down_write(&mm->mmap_sem); has_write_lock = 1; goto retry; } mapping = vma->vm_file->f_mapping; |
3ee6dafc6 only allow nonlin... |
181 182 183 184 185 186 187 |
/* * page_mkclean doesn't work on nonlinear vmas, so if * dirty pages need to be accounted, emulate with linear * vmas. */ if (mapping_cap_account_dirty(mapping)) { unsigned long addr; |
8a459e44a sys_remap_file_pa... |
188 |
struct file *file = vma->vm_file; |
3ee6dafc6 only allow nonlin... |
189 190 |
flags &= MAP_NONBLOCK; |
8a459e44a sys_remap_file_pa... |
191 192 |
get_file(file); addr = mmap_region(file, start, size, |
5a6fe1259 Do not account fo... |
193 |
flags, vma->vm_flags, pgoff); |
8a459e44a sys_remap_file_pa... |
194 |
fput(file); |
3ee6dafc6 only allow nonlin... |
195 196 197 198 199 200 201 202 |
if (IS_ERR_VALUE(addr)) { err = addr; } else { BUG_ON(addr != start); err = 0; } goto out; } |
54cb8821d mm: merge populat... |
203 204 205 206 207 208 209 210 |
spin_lock(&mapping->i_mmap_lock); flush_dcache_mmap_lock(mapping); vma->vm_flags |= VM_NONLINEAR; vma_prio_tree_remove(vma, &mapping->i_mmap); vma_nonlinear_insert(vma, &mapping->i_mmap_nonlinear); flush_dcache_mmap_unlock(mapping); spin_unlock(&mapping->i_mmap_lock); } |
ba470de43 mmap: handle mloc... |
211 212 213 214 215 216 217 218 |
if (vma->vm_flags & VM_LOCKED) { /* * drop PG_Mlocked flag for over-mapped range */ unsigned int saved_flags = vma->vm_flags; munlock_vma_pages_range(vma, start, start + size); vma->vm_flags = saved_flags; } |
cddb8a5c1 mmu-notifiers: core |
219 |
mmu_notifier_invalidate_range_start(mm, start, start + size); |
d0217ac04 mm: fault feedbac... |
220 |
err = populate_range(mm, vma, start, size, pgoff); |
cddb8a5c1 mmu-notifiers: core |
221 |
mmu_notifier_invalidate_range_end(mm, start, start + size); |
d0217ac04 mm: fault feedbac... |
222 |
if (!err && !(flags & MAP_NONBLOCK)) { |
ba470de43 mmap: handle mloc... |
223 224 225 226 227 228 229 230 231 232 233 |
if (vma->vm_flags & VM_LOCKED) { /* * might be mapping previously unmapped range of file */ mlock_vma_pages_range(vma, start, start + size); } else { if (unlikely(has_write_lock)) { downgrade_write(&mm->mmap_sem); has_write_lock = 0; } make_pages_present(start, start+size); |
1da177e4c Linux-2.6.12-rc2 |
234 |
} |
d0217ac04 mm: fault feedbac... |
235 |
} |
1da177e4c Linux-2.6.12-rc2 |
236 |
|
54cb8821d mm: merge populat... |
237 238 239 240 241 |
/* * We can't clear VM_NONLINEAR because we'd have to do * it after ->populate completes, and that would prevent * downgrading the lock. (Locks can't be upgraded). */ |
1da177e4c Linux-2.6.12-rc2 |
242 |
|
54cb8821d mm: merge populat... |
243 |
out: |
1da177e4c Linux-2.6.12-rc2 |
244 245 246 247 248 249 250 |
if (likely(!has_write_lock)) up_read(&mm->mmap_sem); else up_write(&mm->mmap_sem); return err; } |