aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r--fs/ext4/inode.c61
1 files changed, 61 insertions, 0 deletions
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}