ocfs2: move nonsparse hole-filling into ocfs2_write_begin()
authorMark Fasheh <mark.fasheh@oracle.com>
Wed, 29 Aug 2007 00:13:23 +0000 (17:13 -0700)
committerMark Fasheh <mark.fasheh@oracle.com>
Fri, 12 Oct 2007 18:54:35 +0000 (11:54 -0700)
By doing this, we can remove any higher level logic which has to have
knowledge of btree functionality - any callers of ocfs2_write_begin() can
now expect it to do anything necessary to prepare the inode for new data.

Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
Reviewed-by: Joel Becker <joel.becker@oracle.com>
fs/ocfs2/aops.c
fs/ocfs2/file.c
fs/ocfs2/file.h

index f37f25c..fae0767 100644 (file)
@@ -301,12 +301,8 @@ int ocfs2_prepare_write_nolock(struct inode *inode, struct page *page,
 {
        int ret;
 
-       down_read(&OCFS2_I(inode)->ip_alloc_sem);
-
        ret = block_prepare_write(page, from, to, ocfs2_get_block);
 
-       up_read(&OCFS2_I(inode)->ip_alloc_sem);
-
        return ret;
 }
 
@@ -1360,6 +1356,36 @@ out:
        return ret;
 }
 
+/*
+ * This function only does anything for file systems which can't
+ * handle sparse files.
+ *
+ * What we want to do here is fill in any hole between the current end
+ * of allocation and the end of our write. That way the rest of the
+ * write path can treat it as an non-allocating write, which has no
+ * special case code for sparse/nonsparse files.
+ */
+static int ocfs2_expand_nonsparse_inode(struct inode *inode, loff_t pos,
+                                       unsigned len,
+                                       struct ocfs2_write_ctxt *wc)
+{
+       int ret;
+       struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+       loff_t newsize = pos + len;
+
+       if (ocfs2_sparse_alloc(osb))
+               return 0;
+
+       if (newsize <= i_size_read(inode))
+               return 0;
+
+       ret = ocfs2_extend_no_holes(inode, newsize, newsize - len);
+       if (ret)
+               mlog_errno(ret);
+
+       return ret;
+}
+
 int ocfs2_write_begin_nolock(struct address_space *mapping,
                             loff_t pos, unsigned len, unsigned flags,
                             struct page **pagep, void **fsdata,
@@ -1381,6 +1407,12 @@ int ocfs2_write_begin_nolock(struct address_space *mapping,
                return ret;
        }
 
+       ret = ocfs2_expand_nonsparse_inode(inode, pos, len, wc);
+       if (ret) {
+               mlog_errno(ret);
+               goto out;
+       }
+
        ret = ocfs2_populate_write_desc(inode, wc, &clusters_to_alloc,
                                        &extents_to_split);
        if (ret) {
index f3bc365..781ba6c 100644 (file)
@@ -779,25 +779,6 @@ leave:
        return status;
 }
 
-static int ocfs2_extend_allocation(struct inode *inode, u32 logical_start,
-                                  u32 clusters_to_add, int mark_unwritten)
-{
-       int ret;
-
-       /*
-        * The alloc sem blocks peope in read/write from reading our
-        * allocation until we're done changing it. We depend on
-        * i_mutex to block other extend/truncate calls while we're
-        * here.
-        */
-       down_write(&OCFS2_I(inode)->ip_alloc_sem);
-       ret = __ocfs2_extend_allocation(inode, logical_start, clusters_to_add,
-                                       mark_unwritten);
-       up_write(&OCFS2_I(inode)->ip_alloc_sem);
-
-       return ret;
-}
-
 /* Some parts of this taken from generic_cont_expand, which turned out
  * to be too fragile to do exactly what we need without us having to
  * worry about recursive locking in ->prepare_write() and
@@ -889,25 +870,47 @@ out:
        return ret;
 }
 
-/* 
- * A tail_to_skip value > 0 indicates that we're being called from
- * ocfs2_file_aio_write(). This has the following implications:
- *
- * - we don't want to update i_size
- * - di_bh will be NULL, which is fine because it's only used in the
- *   case where we want to update i_size.
- * - ocfs2_zero_extend() will then only be filling the hole created
- *   between i_size and the start of the write.
- */
+int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, u64 zero_to)
+{
+       int ret;
+       u32 clusters_to_add;
+       struct ocfs2_inode_info *oi = OCFS2_I(inode);
+
+       clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size);
+       if (clusters_to_add < oi->ip_clusters)
+               clusters_to_add = 0;
+       else
+               clusters_to_add -= oi->ip_clusters;
+
+       if (clusters_to_add) {
+               ret = __ocfs2_extend_allocation(inode, oi->ip_clusters,
+                                               clusters_to_add, 0);
+               if (ret) {
+                       mlog_errno(ret);
+                       goto out;
+               }
+       }
+
+       /*
+        * Call this even if we don't add any clusters to the tree. We
+        * still need to zero the area between the old i_size and the
+        * new i_size.
+        */
+       ret = ocfs2_zero_extend(inode, zero_to);
+       if (ret < 0)
+               mlog_errno(ret);
+
+out:
+       return ret;
+}
+
 static int ocfs2_extend_file(struct inode *inode,
                             struct buffer_head *di_bh,
-                            u64 new_i_size,
-                            size_t tail_to_skip)
+                            u64 new_i_size)
 {
        int ret = 0;
-       u32 clusters_to_add = 0;
 
-       BUG_ON(!tail_to_skip && !di_bh);
+       BUG_ON(!di_bh);
 
        /* setattr sometimes calls us like this. */
        if (new_i_size == 0)
@@ -917,13 +920,8 @@ static int ocfs2_extend_file(struct inode *inode,
                goto out;
        BUG_ON(new_i_size < i_size_read(inode));
 
-       if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
-               BUG_ON(tail_to_skip != 0);
+       if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
                goto out_update_size;
-       }
-
-       clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size) - 
-               OCFS2_I(inode)->ip_clusters;
 
        /* 
         * protect the pages that ocfs2_zero_extend is going to be
@@ -938,35 +936,25 @@ static int ocfs2_extend_file(struct inode *inode,
                goto out;
        }
 
-       if (clusters_to_add) {
-               ret = ocfs2_extend_allocation(inode,
-                                             OCFS2_I(inode)->ip_clusters,
-                                             clusters_to_add, 0);
-               if (ret < 0) {
-                       mlog_errno(ret);
-                       goto out_unlock;
-               }
-       }
-
        /*
-        * Call this even if we don't add any clusters to the tree. We
-        * still need to zero the area between the old i_size and the
-        * new i_size.
+        * The alloc sem blocks people in read/write from reading our
+        * allocation until we're done changing it. We depend on
+        * i_mutex to block other extend/truncate calls while we're
+        * here.
         */
-       ret = ocfs2_zero_extend(inode, (u64)new_i_size - tail_to_skip);
+       down_write(&OCFS2_I(inode)->ip_alloc_sem);
+       ret = ocfs2_extend_no_holes(inode, new_i_size, new_i_size);
+       up_write(&OCFS2_I(inode)->ip_alloc_sem);
+
        if (ret < 0) {
                mlog_errno(ret);
                goto out_unlock;
        }
 
 out_update_size:
-       if (!tail_to_skip) {
-               /* We're being called from ocfs2_setattr() which wants
-                * us to update i_size */
-               ret = ocfs2_simple_size_update(inode, di_bh, new_i_size);
-               if (ret < 0)
-                       mlog_errno(ret);
-       }
+       ret = ocfs2_simple_size_update(inode, di_bh, new_i_size);
+       if (ret < 0)
+               mlog_errno(ret);
 
 out_unlock:
        if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb)))
@@ -1035,7 +1023,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
                if (i_size_read(inode) > attr->ia_size)
                        status = ocfs2_truncate_file(inode, bh, attr->ia_size);
                else
-                       status = ocfs2_extend_file(inode, bh, attr->ia_size, 0);
+                       status = ocfs2_extend_file(inode, bh, attr->ia_size);
                if (status < 0) {
                        if (status != -ENOSPC)
                                mlog_errno(status);
@@ -1713,15 +1701,13 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
                                         int appending,
                                         int *direct_io)
 {
-       int ret = 0, meta_level = appending;
+       int ret = 0, meta_level = 0;
        struct inode *inode = dentry->d_inode;
-       u32 clusters;
-       loff_t newsize, saved_pos;
+       loff_t saved_pos, end;
 
        /* 
-        * We sample i_size under a read level meta lock to see if our write
-        * is extending the file, if it is we back off and get a write level
-        * meta lock.
+        * We start with a read level meta lock and only jump to an ex
+        * if we need to make modifications here.
         */
        for(;;) {
                ret = ocfs2_meta_lock(inode, NULL, meta_level);
@@ -1763,87 +1749,38 @@ static int ocfs2_prepare_inode_for_write(struct dentry *dentry,
                        saved_pos = *ppos;
                }
 
-               if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) {
-                       loff_t end = saved_pos + count;
-
-                       /*
-                        * Skip the O_DIRECT checks if we don't need
-                        * them.
-                        */
-                       if (!direct_io || !(*direct_io))
-                               break;
-
-                       /*
-                        * Allowing concurrent direct writes means
-                        * i_size changes wouldn't be synchronized, so
-                        * one node could wind up truncating another
-                        * nodes writes.
-                        */
-                       if (end > i_size_read(inode)) {
-                               *direct_io = 0;
-                               break;
-                       }
+               end = saved_pos + count;
 
-                       /*
-                        * We don't fill holes during direct io, so
-                        * check for them here. If any are found, the
-                        * caller will have to retake some cluster
-                        * locks and initiate the io as buffered.
-                        */
-                       ret = ocfs2_check_range_for_holes(inode, saved_pos,
-                                                         count);
-                       if (ret == 1) {
-                               *direct_io = 0;
-                               ret = 0;
-                       } else if (ret < 0)
-                               mlog_errno(ret);
+               /*
+                * Skip the O_DIRECT checks if we don't need
+                * them.
+                */
+               if (!direct_io || !(*direct_io))
                        break;
-               }
 
                /*
-                * The rest of this loop is concerned with legacy file
-                * systems which don't support sparse files.
+                * Allowing concurrent direct writes means
+                * i_size changes wouldn't be synchronized, so
+                * one node could wind up truncating another
+                * nodes writes.
                 */
-
-               newsize = count + saved_pos;
-
-               mlog(0, "pos=%lld newsize=%lld cursize=%lld\n",
-                    (long long) saved_pos, (long long) newsize,
-                    (long long) i_size_read(inode));
-
-               /* No need for a higher level metadata lock if we're
-                * never going past i_size. */
-               if (newsize <= i_size_read(inode))
+               if (end > i_size_read(inode)) {
+                       *direct_io = 0;
                        break;
-
-               if (meta_level == 0) {
-                       ocfs2_meta_unlock(inode, meta_level);
-                       meta_level = 1;
-                       continue;
                }
 
-               spin_lock(&OCFS2_I(inode)->ip_lock);
-               clusters = ocfs2_clusters_for_bytes(inode->i_sb, newsize) -
-                       OCFS2_I(inode)->ip_clusters;
-               spin_unlock(&OCFS2_I(inode)->ip_lock);
-
-               mlog(0, "Writing at EOF, may need more allocation: "
-                    "i_size = %lld, newsize = %lld, need %u clusters\n",
-                    (long long) i_size_read(inode), (long long) newsize,
-                    clusters);
-
-               /* We only want to continue the rest of this loop if
-                * our extend will actually require more
-                * allocation. */
-               if (!clusters)
-                       break;
-
-               ret = ocfs2_extend_file(inode, NULL, newsize, count);
-               if (ret < 0) {
-                       if (ret != -ENOSPC)
-                               mlog_errno(ret);
-                       goto out_unlock;
-               }
+               /*
+                * We don't fill holes during direct io, so
+                * check for them here. If any are found, the
+                * caller will have to retake some cluster
+                * locks and initiate the io as buffered.
+                */
+               ret = ocfs2_check_range_for_holes(inode, saved_pos, count);
+               if (ret == 1) {
+                       *direct_io = 0;
+                       ret = 0;
+               } else if (ret < 0)
+                       mlog_errno(ret);
                break;
        }
 
index 36fe27f..066f14a 100644 (file)
@@ -47,6 +47,8 @@ int ocfs2_do_extend_allocation(struct ocfs2_super *osb,
                               struct ocfs2_alloc_context *data_ac,
                               struct ocfs2_alloc_context *meta_ac,
                               enum ocfs2_alloc_restarted *reason_ret);
+int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size,
+                         u64 zero_to);
 int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di,
                          u32 clusters_to_add, u32 extents_to_split,
                          struct ocfs2_alloc_context **data_ac,