Merge branch 'for-linus' of git://git.kernel.org/pub/scm/fs/xfs/xfs
[safe/jmp/linux-2.6] / fs / ocfs2 / file.c
index 4921b4e..89fc8ee 100644 (file)
@@ -59,6 +59,7 @@
 #include "xattr.h"
 #include "acl.h"
 #include "quota.h"
+#include "refcounttree.h"
 
 #include "buffer_head_io.h"
 
@@ -334,6 +335,39 @@ out:
        return ret;
 }
 
+static int ocfs2_cow_file_pos(struct inode *inode,
+                             struct buffer_head *fe_bh,
+                             u64 offset)
+{
+       int status;
+       u32 phys, cpos = offset >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
+       unsigned int num_clusters = 0;
+       unsigned int ext_flags = 0;
+
+       /*
+        * If the new offset is aligned to the range of the cluster, there is
+        * no space for ocfs2_zero_range_for_truncate to fill, so no need to
+        * CoW either.
+        */
+       if ((offset & (OCFS2_SB(inode->i_sb)->s_clustersize - 1)) == 0)
+               return 0;
+
+       status = ocfs2_get_clusters(inode, cpos, &phys,
+                                   &num_clusters, &ext_flags);
+       if (status) {
+               mlog_errno(status);
+               goto out;
+       }
+
+       if (!(ext_flags & OCFS2_EXT_REFCOUNTED))
+               goto out;
+
+       return ocfs2_refcount_cow(inode, fe_bh, cpos, 1, cpos+1);
+
+out:
+       return status;
+}
+
 static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
                                     struct inode *inode,
                                     struct buffer_head *fe_bh,
@@ -346,6 +380,17 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb,
 
        mlog_entry_void();
 
+       /*
+        * We need to CoW the cluster contains the offset if it is reflinked
+        * since we will call ocfs2_zero_range_for_truncate later which will
+        * write "0" from offset to the end of the cluster.
+        */
+       status = ocfs2_cow_file_pos(inode, fe_bh, new_i_size);
+       if (status) {
+               mlog_errno(status);
+               return status;
+       }
+
        /* TODO: This needs to actually orphan the inode in this
         * transaction. */
 
@@ -486,6 +531,8 @@ bail_unlock_sem:
        up_write(&OCFS2_I(inode)->ip_alloc_sem);
 
 bail:
+       if (!status && OCFS2_I(inode)->ip_clusters == 0)
+               status = ocfs2_try_remove_refcount_tree(inode, di_bh);
 
        mlog_exit(status);
        return status;
@@ -1656,6 +1703,70 @@ static long ocfs2_fallocate(struct inode *inode, int mode, loff_t offset,
                                         OCFS2_IOC_RESVSP64, &sr, change_size);
 }
 
+int ocfs2_check_range_for_refcount(struct inode *inode, loff_t pos,
+                                  size_t count)
+{
+       int ret = 0;
+       unsigned int extent_flags;
+       u32 cpos, clusters, extent_len, phys_cpos;
+       struct super_block *sb = inode->i_sb;
+
+       if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb)) ||
+           !(OCFS2_I(inode)->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL))
+               return 0;
+
+       cpos = pos >> OCFS2_SB(sb)->s_clustersize_bits;
+       clusters = ocfs2_clusters_for_bytes(sb, pos + count) - cpos;
+
+       while (clusters) {
+               ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, &extent_len,
+                                        &extent_flags);
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+
+               if (phys_cpos && (extent_flags & OCFS2_EXT_REFCOUNTED)) {
+                       ret = 1;
+                       break;
+               }
+
+               if (extent_len > clusters)
+                       extent_len = clusters;
+
+               clusters -= extent_len;
+               cpos += extent_len;
+       }
+out:
+       return ret;
+}
+
+static int ocfs2_prepare_inode_for_refcount(struct inode *inode,
+                                           loff_t pos, size_t count,
+                                           int *meta_level)
+{
+       int ret;
+       struct buffer_head *di_bh = NULL;
+       u32 cpos = pos >> OCFS2_SB(inode->i_sb)->s_clustersize_bits;
+       u32 clusters =
+               ocfs2_clusters_for_bytes(inode->i_sb, pos + count) - cpos;
+
+       ret = ocfs2_inode_lock(inode, &di_bh, 1);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
+       *meta_level = 1;
+
+       ret = ocfs2_refcount_cow(inode, di_bh, cpos, clusters, UINT_MAX);
+       if (ret)
+               mlog_errno(ret);
+out:
+       brelse(di_bh);
+       return ret;
+}
+
 static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
                                         loff_t *ppos,
                                         size_t count,
@@ -1712,6 +1823,22 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
 
                end = saved_pos + count;
 
+               ret = ocfs2_check_range_for_refcount(inode, saved_pos, count);
+               if (ret == 1) {
+                       ocfs2_inode_unlock(inode, meta_level);
+                       meta_level = -1;
+
+                       ret = ocfs2_prepare_inode_for_refcount(inode,
+                                                              saved_pos,
+                                                              count,
+                                                              &meta_level);
+               }
+
+               if (ret < 0) {
+                       mlog_errno(ret);
+                       goto out_unlock;
+               }
+
                /*
                 * Skip the O_DIRECT checks if we don't need
                 * them.
@@ -1758,7 +1885,8 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
                *ppos = saved_pos;
 
 out_unlock:
-       ocfs2_inode_unlock(inode, meta_level);
+       if (meta_level >= 0)
+               ocfs2_inode_unlock(inode, meta_level);
 
 out:
        return ret;
@@ -1870,8 +1998,7 @@ relock:
                        goto out_dio;
                }
        } else {
-               written = generic_file_aio_write_nolock(iocb, iov, nr_segs,
-                                                       *ppos);
+               written = __generic_file_aio_write(iocb, iov, nr_segs, ppos);
        }
 
 out_dio:
@@ -1879,18 +2006,21 @@ out_dio:
        BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT));
 
        if ((file->f_flags & O_SYNC && !direct_io) || IS_SYNC(inode)) {
-               /*
-                * The generic write paths have handled getting data
-                * to disk, but since we don't make use of the dirty
-                * inode list, a manual journal commit is necessary
-                * here.
-                */
-               if (old_size != i_size_read(inode) ||
-                   old_clusters != OCFS2_I(inode)->ip_clusters) {
+               ret = filemap_fdatawrite_range(file->f_mapping, pos,
+                                              pos + count - 1);
+               if (ret < 0)
+                       written = ret;
+
+               if (!ret && (old_size != i_size_read(inode) ||
+                   old_clusters != OCFS2_I(inode)->ip_clusters)) {
                        ret = jbd2_journal_force_commit(osb->journal->j_journal);
                        if (ret < 0)
                                written = ret;
                }
+
+               if (!ret)
+                       ret = filemap_fdatawait_range(file->f_mapping, pos,
+                                                     pos + count - 1);
        }
 
        /* 
@@ -1990,31 +2120,16 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
 
        if (ret > 0) {
                unsigned long nr_pages;
+               int err;
 
-               *ppos += ret;
                nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 
-               /*
-                * If file or inode is SYNC and we actually wrote some data,
-                * sync it.
-                */
-               if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
-                       int err;
-
-                       mutex_lock(&inode->i_mutex);
-                       err = ocfs2_rw_lock(inode, 1);
-                       if (err < 0) {
-                               mlog_errno(err);
-                       } else {
-                               err = generic_osync_inode(inode, mapping,
-                                                 OSYNC_METADATA|OSYNC_DATA);
-                               ocfs2_rw_unlock(inode, 1);
-                       }
-                       mutex_unlock(&inode->i_mutex);
+               err = generic_write_sync(out, *ppos, ret);
+               if (err)
+                       ret = err;
+               else
+                       *ppos += ret;
 
-                       if (err)
-                               ret = err;
-               }
                balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
        }