diff options
author | Carsten Otte <cotte@de.ibm.com> | 2007-03-29 04:20:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-03-29 11:22:26 -0400 |
commit | a76c0b976310bbb1b6eaecaaae465af194134477 (patch) | |
tree | 4f23a06244f7cb6689bd7ded80843c481968a283 | |
parent | 90ed52ebe48181d3c5427b3bd1d24f659e7575ad (diff) |
[PATCH] mm: fix xip issue with /dev/zero
Fix the bug, that reading into xip mapping from /dev/zero fills the user
page table with ZERO_PAGE() entries. Later on, xip cannot tell which pages
have been ZERO_PAGE() filled by access to a sparse mapping, and which ones
origin from /dev/zero. It will unmap ZERO_PAGE from all mappings when
filling the sparse hole with data. xip does now use its own zeroed page
for its sparse mappings. Please apply.
Signed-off-by: Carsten Otte <cotte@de.ibm.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | mm/filemap_xip.c | 48 |
1 files changed, 37 insertions, 11 deletions
diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 9dd9fbb75139..cbb335813ec0 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c | |||
@@ -17,6 +17,29 @@ | |||
17 | #include "filemap.h" | 17 | #include "filemap.h" |
18 | 18 | ||
19 | /* | 19 | /* |
20 | * We do use our own empty page to avoid interference with other users | ||
21 | * of ZERO_PAGE(), such as /dev/zero | ||
22 | */ | ||
23 | static struct page *__xip_sparse_page; | ||
24 | |||
25 | static struct page *xip_sparse_page(void) | ||
26 | { | ||
27 | if (!__xip_sparse_page) { | ||
28 | unsigned long zeroes = get_zeroed_page(GFP_HIGHUSER); | ||
29 | if (zeroes) { | ||
30 | static DEFINE_SPINLOCK(xip_alloc_lock); | ||
31 | spin_lock(&xip_alloc_lock); | ||
32 | if (!__xip_sparse_page) | ||
33 | __xip_sparse_page = virt_to_page(zeroes); | ||
34 | else | ||
35 | free_page(zeroes); | ||
36 | spin_unlock(&xip_alloc_lock); | ||
37 | } | ||
38 | } | ||
39 | return __xip_sparse_page; | ||
40 | } | ||
41 | |||
42 | /* | ||
20 | * This is a file read routine for execute in place files, and uses | 43 | * This is a file read routine for execute in place files, and uses |
21 | * the mapping->a_ops->get_xip_page() function for the actual low-level | 44 | * the mapping->a_ops->get_xip_page() function for the actual low-level |
22 | * stuff. | 45 | * stuff. |
@@ -162,7 +185,7 @@ EXPORT_SYMBOL_GPL(xip_file_sendfile); | |||
162 | * xip_write | 185 | * xip_write |
163 | * | 186 | * |
164 | * This function walks all vmas of the address_space and unmaps the | 187 | * This function walks all vmas of the address_space and unmaps the |
165 | * ZERO_PAGE when found at pgoff. Should it go in rmap.c? | 188 | * __xip_sparse_page when found at pgoff. |
166 | */ | 189 | */ |
167 | static void | 190 | static void |
168 | __xip_unmap (struct address_space * mapping, | 191 | __xip_unmap (struct address_space * mapping, |
@@ -177,13 +200,16 @@ __xip_unmap (struct address_space * mapping, | |||
177 | spinlock_t *ptl; | 200 | spinlock_t *ptl; |
178 | struct page *page; | 201 | struct page *page; |
179 | 202 | ||
203 | page = __xip_sparse_page; | ||
204 | if (!page) | ||
205 | return; | ||
206 | |||
180 | spin_lock(&mapping->i_mmap_lock); | 207 | spin_lock(&mapping->i_mmap_lock); |
181 | vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { | 208 | vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { |
182 | mm = vma->vm_mm; | 209 | mm = vma->vm_mm; |
183 | address = vma->vm_start + | 210 | address = vma->vm_start + |
184 | ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); | 211 | ((pgoff - vma->vm_pgoff) << PAGE_SHIFT); |
185 | BUG_ON(address < vma->vm_start || address >= vma->vm_end); | 212 | BUG_ON(address < vma->vm_start || address >= vma->vm_end); |
186 | page = ZERO_PAGE(0); | ||
187 | pte = page_check_address(page, mm, address, &ptl); | 213 | pte = page_check_address(page, mm, address, &ptl); |
188 | if (pte) { | 214 | if (pte) { |
189 | /* Nuke the page table entry. */ | 215 | /* Nuke the page table entry. */ |
@@ -222,16 +248,14 @@ xip_file_nopage(struct vm_area_struct * area, | |||
222 | + area->vm_pgoff; | 248 | + area->vm_pgoff; |
223 | 249 | ||
224 | size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | 250 | size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; |
225 | if (pgoff >= size) { | 251 | if (pgoff >= size) |
226 | return NULL; | 252 | return NOPAGE_SIGBUS; |
227 | } | ||
228 | 253 | ||
229 | page = mapping->a_ops->get_xip_page(mapping, pgoff*(PAGE_SIZE/512), 0); | 254 | page = mapping->a_ops->get_xip_page(mapping, pgoff*(PAGE_SIZE/512), 0); |
230 | if (!IS_ERR(page)) { | 255 | if (!IS_ERR(page)) |
231 | goto out; | 256 | goto out; |
232 | } | ||
233 | if (PTR_ERR(page) != -ENODATA) | 257 | if (PTR_ERR(page) != -ENODATA) |
234 | return NULL; | 258 | return NOPAGE_SIGBUS; |
235 | 259 | ||
236 | /* sparse block */ | 260 | /* sparse block */ |
237 | if ((area->vm_flags & (VM_WRITE | VM_MAYWRITE)) && | 261 | if ((area->vm_flags & (VM_WRITE | VM_MAYWRITE)) && |
@@ -241,12 +265,14 @@ xip_file_nopage(struct vm_area_struct * area, | |||
241 | page = mapping->a_ops->get_xip_page (mapping, | 265 | page = mapping->a_ops->get_xip_page (mapping, |
242 | pgoff*(PAGE_SIZE/512), 1); | 266 | pgoff*(PAGE_SIZE/512), 1); |
243 | if (IS_ERR(page)) | 267 | if (IS_ERR(page)) |
244 | return NULL; | 268 | return NOPAGE_SIGBUS; |
245 | /* unmap page at pgoff from all other vmas */ | 269 | /* unmap page at pgoff from all other vmas */ |
246 | __xip_unmap(mapping, pgoff); | 270 | __xip_unmap(mapping, pgoff); |
247 | } else { | 271 | } else { |
248 | /* not shared and writable, use ZERO_PAGE() */ | 272 | /* not shared and writable, use xip_sparse_page() */ |
249 | page = ZERO_PAGE(0); | 273 | page = xip_sparse_page(); |
274 | if (!page) | ||
275 | return NOPAGE_OOM; | ||
250 | } | 276 | } |
251 | 277 | ||
252 | out: | 278 | out: |