ocfs2: add ip_alloc_sem in direct IO to protect allocation changes
[pandora-kernel.git] / fs / ocfs2 / aops.c
index 0f5fd9d..a7ab145 100644 (file)
@@ -533,10 +533,14 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock,
 
        inode_blocks = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode));
 
+       down_read(&OCFS2_I(inode)->ip_alloc_sem);
+
        /* This figures out the size of the next contiguous block, and
         * our logical offset */
        ret = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno,
                                          &contig_blocks, &ext_flags);
+       up_read(&OCFS2_I(inode)->ip_alloc_sem);
+
        if (ret) {
                mlog(ML_ERROR, "get_blocks() failed iblock=%llu\n",
                     (unsigned long long)iblock);
@@ -557,6 +561,8 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock,
 
                alloc_locked = 1;
 
+               down_write(&OCFS2_I(inode)->ip_alloc_sem);
+
                /* fill hole, allocate blocks can't be larger than the size
                 * of the hole */
                clusters_to_alloc = ocfs2_clusters_for_bytes(inode->i_sb, len);
@@ -569,6 +575,7 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock,
                ret = ocfs2_extend_allocation(inode, cpos,
                                clusters_to_alloc, 0);
                if (ret < 0) {
+                       up_write(&OCFS2_I(inode)->ip_alloc_sem);
                        mlog_errno(ret);
                        goto bail;
                }
@@ -576,11 +583,13 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock,
                ret = ocfs2_extent_map_get_blocks(inode, iblock, &p_blkno,
                                &contig_blocks, &ext_flags);
                if (ret < 0) {
+                       up_write(&OCFS2_I(inode)->ip_alloc_sem);
                        mlog(ML_ERROR, "get_blocks() failed iblock=%llu\n",
                                        (unsigned long long)iblock);
                        ret = -EIO;
                        goto bail;
                }
+               up_write(&OCFS2_I(inode)->ip_alloc_sem);
        }
 
        /*
@@ -627,10 +636,13 @@ static void ocfs2_dio_end_io(struct kiocb *iocb,
                mutex_unlock(&OCFS2_I(inode)->ip_unaligned_aio);
        }
 
-       ocfs2_iocb_clear_rw_locked(iocb);
+       /* Let rw unlock to be done later to protect append direct io write */
+       if (offset + bytes <= i_size_read(inode)) {
+               ocfs2_iocb_clear_rw_locked(iocb);
 
-       level = ocfs2_iocb_rw_locked_level(iocb);
-       ocfs2_rw_unlock(inode, level);
+               level = ocfs2_iocb_rw_locked_level(iocb);
+               ocfs2_rw_unlock(inode, level);
+       }
 }
 
 static int ocfs2_releasepage(struct page *page, gfp_t wait)
@@ -832,12 +844,17 @@ static ssize_t ocfs2_direct_IO_write(struct kiocb *iocb,
 
                /* zeroing out the previously allocated cluster tail
                 * that but not zeroed */
-               if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
+               if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
+                       down_read(&OCFS2_I(inode)->ip_alloc_sem);
                        ret = ocfs2_direct_IO_zero_extend(osb, inode, offset,
                                        zero_len_tail, cluster_align_tail);
-               else
+                       up_read(&OCFS2_I(inode)->ip_alloc_sem);
+               } else {
+                       down_write(&OCFS2_I(inode)->ip_alloc_sem);
                        ret = ocfs2_direct_IO_extend_no_holes(osb, inode,
                                        offset);
+                       up_write(&OCFS2_I(inode)->ip_alloc_sem);
+               }
                if (ret < 0) {
                        mlog_errno(ret);
                        ocfs2_inode_unlock(inode, 1);
@@ -857,7 +874,8 @@ static ssize_t ocfs2_direct_IO_write(struct kiocb *iocb,
        written = __blockdev_direct_IO(iocb, inode, inode->i_sb->s_bdev, iter,
                                       offset, ocfs2_direct_IO_get_blocks,
                                       ocfs2_dio_end_io, NULL, 0);
-       if (unlikely(written < 0)) {
+       /* overwrite aio may return -EIOCBQUEUED, and it is not an error */
+       if ((written < 0) && (written != -EIOCBQUEUED)) {
                loff_t i_size = i_size_read(inode);
 
                if (offset + count > i_size) {
@@ -876,12 +894,14 @@ static ssize_t ocfs2_direct_IO_write(struct kiocb *iocb,
 
                                        ocfs2_inode_unlock(inode, 1);
                                        brelse(di_bh);
+                                       di_bh = NULL;
                                        goto clean_orphan;
                                }
                        }
 
                        ocfs2_inode_unlock(inode, 1);
                        brelse(di_bh);
+                       di_bh = NULL;
 
                        ret = jbd2_journal_force_commit(journal);
                        if (ret < 0)
@@ -936,10 +956,12 @@ clean_orphan:
                if (tmp_ret < 0) {
                        ret = tmp_ret;
                        mlog_errno(ret);
+                       brelse(di_bh);
                        goto out;
                }
 
                ocfs2_inode_unlock(inode, 1);
+               brelse(di_bh);
 
                tmp_ret = jbd2_journal_force_commit(journal);
                if (tmp_ret < 0) {