aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Chinner <dgc@sgi.com>2007-07-19 03:39:55 -0400
committerTim Shimmin <tes@chook.melbourne.sgi.com>2007-07-19 05:50:50 -0400
commit5417169026c3df151adf5a65eb061278b0a72e69 (patch)
treee4872a4243e12fe931ff6ac789df181b579b884c
parent589f1e81bde732dd0b1bc5d01b6bddd4bcb4527b (diff)
[FS] Implement block_page_mkwrite.
Many filesystems need a ->page-mkwrite callout to correctly set up pages that have been written to by mmap. This is especially important when mmap is writing into holes as it allows filesystems to correctly account for and allocate space before the mmap write is allowed to proceed. Protection against truncate races is provided by locking the page and checking to see whether the page mapping is correct and whether it is beyond EOF so we don't end up allowing allocations beyond the current EOF or changing EOF as a result of a mmap write. SGI-PV: 940392 SGI-Modid: 2.6.x-xfs-melb:linux:29146a Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Christoph Hellwig <hch@infradead.org> Signed-off-by: Tim Shimmin <tes@sgi.com>
-rw-r--r--fs/buffer.c47
-rw-r--r--include/linux/buffer_head.h2
2 files changed, 49 insertions, 0 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 0f9006714230..02ebb1f1d3b0 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2194,6 +2194,52 @@ int generic_commit_write(struct file *file, struct page *page,
2194 return 0; 2194 return 0;
2195} 2195}
2196 2196
2197/*
2198 * block_page_mkwrite() is not allowed to change the file size as it gets
2199 * called from a page fault handler when a page is first dirtied. Hence we must
2200 * be careful to check for EOF conditions here. We set the page up correctly
2201 * for a written page which means we get ENOSPC checking when writing into
2202 * holes and correct delalloc and unwritten extent mapping on filesystems that
2203 * support these features.
2204 *
2205 * We are not allowed to take the i_mutex here so we have to play games to
2206 * protect against truncate races as the page could now be beyond EOF. Because
2207 * vmtruncate() writes the inode size before removing pages, once we have the
2208 * page lock we can determine safely if the page is beyond EOF. If it is not
2209 * beyond EOF, then the page is guaranteed safe against truncation until we
2210 * unlock the page.
2211 */
2212int
2213block_page_mkwrite(struct vm_area_struct *vma, struct page *page,
2214 get_block_t get_block)
2215{
2216 struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
2217 unsigned long end;
2218 loff_t size;
2219 int ret = -EINVAL;
2220
2221 lock_page(page);
2222 size = i_size_read(inode);
2223 if ((page->mapping != inode->i_mapping) ||
2224 ((page->index << PAGE_CACHE_SHIFT) > size)) {
2225 /* page got truncated out from underneath us */
2226 goto out_unlock;
2227 }
2228
2229 /* page is wholly or partially inside EOF */
2230 if (((page->index + 1) << PAGE_CACHE_SHIFT) > size)
2231 end = size & ~PAGE_CACHE_MASK;
2232 else
2233 end = PAGE_CACHE_SIZE;
2234
2235 ret = block_prepare_write(page, 0, end, get_block);
2236 if (!ret)
2237 ret = block_commit_write(page, 0, end);
2238
2239out_unlock:
2240 unlock_page(page);
2241 return ret;
2242}
2197 2243
2198/* 2244/*
2199 * nobh_prepare_write()'s prereads are special: the buffer_heads are freed 2245 * nobh_prepare_write()'s prereads are special: the buffer_heads are freed
@@ -2977,6 +3023,7 @@ EXPORT_SYMBOL(__brelse);
2977EXPORT_SYMBOL(__wait_on_buffer); 3023EXPORT_SYMBOL(__wait_on_buffer);
2978EXPORT_SYMBOL(block_commit_write); 3024EXPORT_SYMBOL(block_commit_write);
2979EXPORT_SYMBOL(block_prepare_write); 3025EXPORT_SYMBOL(block_prepare_write);
3026EXPORT_SYMBOL(block_page_mkwrite);
2980EXPORT_SYMBOL(block_read_full_page); 3027EXPORT_SYMBOL(block_read_full_page);
2981EXPORT_SYMBOL(block_sync_page); 3028EXPORT_SYMBOL(block_sync_page);
2982EXPORT_SYMBOL(block_truncate_page); 3029EXPORT_SYMBOL(block_truncate_page);
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index 5c6e12853a9b..35cadad84b14 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -209,6 +209,8 @@ int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
209int generic_cont_expand(struct inode *inode, loff_t size); 209int generic_cont_expand(struct inode *inode, loff_t size);
210int generic_cont_expand_simple(struct inode *inode, loff_t size); 210int generic_cont_expand_simple(struct inode *inode, loff_t size);
211int block_commit_write(struct page *page, unsigned from, unsigned to); 211int block_commit_write(struct page *page, unsigned from, unsigned to);
212int block_page_mkwrite(struct vm_area_struct *vma, struct page *page,
213 get_block_t get_block);
212void block_sync_page(struct page *); 214void block_sync_page(struct page *);
213sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); 215sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *);
214int generic_commit_write(struct file *, struct page *, unsigned, unsigned); 216int generic_commit_write(struct file *, struct page *, unsigned, unsigned);