aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2008-07-11 19:27:31 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-07-11 19:27:31 -0400
commit2e9ee850355593e311d9a26542290fe51e152f74 (patch)
treee860a33119edf36e367ad354428dfc0d6201273c
parent93e3270c87549dc531a0b0e5d06362d998d810cb (diff)
ext4: Use page_mkwrite vma_operations to get mmap write notification.
We would like to get notified when we are doing a write on mmap section. This is needed with respect to preallocated area. We split the preallocated area into initialzed extent and uninitialzed extent in the call back. This let us handle ENOSPC better. Otherwise we get ENOSPC in the writepage and that would result in data loss. The changes are also needed to handle ENOSPC when writing to an mmap section of files with holes. Acked-by: Jan Kara <jack@suse.cz> Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Mingming Cao <cmm@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/ext4.h1
-rw-r--r--fs/ext4/file.c19
-rw-r--r--fs/ext4/inode.c61
3 files changed, 80 insertions, 1 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 64edb09c481e..98760d14e2cd 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1068,6 +1068,7 @@ extern void ext4_set_aops(struct inode *inode);
1068extern int ext4_writepage_trans_blocks(struct inode *); 1068extern int ext4_writepage_trans_blocks(struct inode *);
1069extern int ext4_block_truncate_page(handle_t *handle, struct page *page, 1069extern int ext4_block_truncate_page(handle_t *handle, struct page *page,
1070 struct address_space *mapping, loff_t from); 1070 struct address_space *mapping, loff_t from);
1071extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page);
1071 1072
1072/* ioctl.c */ 1073/* ioctl.c */
1073extern long ext4_ioctl(struct file *, unsigned int, unsigned long); 1074extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 4159be6366ab..b9510ba66a2d 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -123,6 +123,23 @@ force_commit:
123 return ret; 123 return ret;
124} 124}
125 125
126static struct vm_operations_struct ext4_file_vm_ops = {
127 .fault = filemap_fault,
128 .page_mkwrite = ext4_page_mkwrite,
129};
130
131static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
132{
133 struct address_space *mapping = file->f_mapping;
134
135 if (!mapping->a_ops->readpage)
136 return -ENOEXEC;
137 file_accessed(file);
138 vma->vm_ops = &ext4_file_vm_ops;
139 vma->vm_flags |= VM_CAN_NONLINEAR;
140 return 0;
141}
142
126const struct file_operations ext4_file_operations = { 143const struct file_operations ext4_file_operations = {
127 .llseek = generic_file_llseek, 144 .llseek = generic_file_llseek,
128 .read = do_sync_read, 145 .read = do_sync_read,
@@ -133,7 +150,7 @@ const struct file_operations ext4_file_operations = {
133#ifdef CONFIG_COMPAT 150#ifdef CONFIG_COMPAT
134 .compat_ioctl = ext4_compat_ioctl, 151 .compat_ioctl = ext4_compat_ioctl,
135#endif 152#endif
136 .mmap = generic_file_mmap, 153 .mmap = ext4_file_mmap,
137 .open = generic_file_open, 154 .open = generic_file_open,
138 .release = ext4_release_file, 155 .release = ext4_release_file,
139 .fsync = ext4_sync_file, 156 .fsync = ext4_sync_file,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c5d506dce8c5..034fc544aa66 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3564,3 +3564,64 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
3564 3564
3565 return err; 3565 return err;
3566} 3566}
3567
3568static int ext4_bh_unmapped(handle_t *handle, struct buffer_head *bh)
3569{
3570 return !buffer_mapped(bh);
3571}
3572
3573int ext4_page_mkwrite(struct vm_area_struct *vma, struct page *page)
3574{
3575 loff_t size;
3576 unsigned long len;
3577 int ret = -EINVAL;
3578 struct file *file = vma->vm_file;
3579 struct inode *inode = file->f_path.dentry->d_inode;
3580 struct address_space *mapping = inode->i_mapping;
3581
3582 /*
3583 * Get i_alloc_sem to stop truncates messing with the inode. We cannot
3584 * get i_mutex because we are already holding mmap_sem.
3585 */
3586 down_read(&inode->i_alloc_sem);
3587 size = i_size_read(inode);
3588 if (page->mapping != mapping || size <= page_offset(page)
3589 || !PageUptodate(page)) {
3590 /* page got truncated from under us? */
3591 goto out_unlock;
3592 }
3593 ret = 0;
3594 if (PageMappedToDisk(page))
3595 goto out_unlock;
3596
3597 if (page->index == size >> PAGE_CACHE_SHIFT)
3598 len = size & ~PAGE_CACHE_MASK;
3599 else
3600 len = PAGE_CACHE_SIZE;
3601
3602 if (page_has_buffers(page)) {
3603 /* return if we have all the buffers mapped */
3604 if (!walk_page_buffers(NULL, page_buffers(page), 0, len, NULL,
3605 ext4_bh_unmapped))
3606 goto out_unlock;
3607 }
3608 /*
3609 * OK, we need to fill the hole... Do write_begin write_end
3610 * to do block allocation/reservation.We are not holding
3611 * inode.i__mutex here. That allow * parallel write_begin,
3612 * write_end call. lock_page prevent this from happening
3613 * on the same page though
3614 */
3615 ret = mapping->a_ops->write_begin(file, mapping, page_offset(page),
3616 len, AOP_FLAG_UNINTERRUPTIBLE, &page, NULL);
3617 if (ret < 0)
3618 goto out_unlock;
3619 ret = mapping->a_ops->write_end(file, mapping, page_offset(page),
3620 len, len, page, NULL);
3621 if (ret < 0)
3622 goto out_unlock;
3623 ret = 0;
3624out_unlock:
3625 up_read(&inode->i_alloc_sem);
3626 return ret;
3627}