ocfs2: cond_resched() in ocfs2_zero_extend()
[pandora-kernel.git] / fs / ocfs2 / file.c
index 581eb45..1be74c4 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <linux/uio.h>
+#include <linux/sched.h>
 
 #define MLOG_MASK_PREFIX ML_INODE
 #include <cluster/masklog.h>
@@ -44,6 +45,7 @@
 #include "file.h"
 #include "sysfile.h"
 #include "inode.h"
+#include "ioctl.h"
 #include "journal.h"
 #include "mmap.h"
 #include "suballoc.h"
@@ -613,7 +615,8 @@ leave:
 
 /* 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 ->commit_write(). */
+ * worry about recursive locking in ->prepare_write() and
+ * ->commit_write(). */
 static int ocfs2_write_zero_page(struct inode *inode,
                                 u64 size)
 {
@@ -641,7 +644,7 @@ static int ocfs2_write_zero_page(struct inode *inode,
                goto out;
        }
 
-       ret = ocfs2_prepare_write(NULL, page, offset, offset);
+       ret = ocfs2_prepare_write_nolock(inode, page, offset, offset);
        if (ret < 0) {
                mlog_errno(ret);
                goto out_unlock;
@@ -689,19 +692,38 @@ static int ocfs2_zero_extend(struct inode *inode,
                }
 
                start_off += sb->s_blocksize;
+
+               /*
+                * Very large extends have the potential to lock up
+                * the cpu for extended periods of time.
+                */
+               cond_resched();
        }
 
 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.
+ */
 static int ocfs2_extend_file(struct inode *inode,
                             struct buffer_head *di_bh,
-                            u64 new_i_size)
+                            u64 new_i_size,
+                            size_t tail_to_skip)
 {
        int ret = 0;
        u32 clusters_to_add;
 
+       BUG_ON(!tail_to_skip && !di_bh);
+
        /* setattr sometimes calls us like this. */
        if (new_i_size == 0)
                goto out;
@@ -713,28 +735,49 @@ static int ocfs2_extend_file(struct inode *inode,
        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
+        * pulling into the page cache.. we do this before the
+        * metadata extend so that we don't get into the situation
+        * where we've extended the metadata but can't get the data
+        * lock to zero.
+        */
+       ret = ocfs2_data_lock(inode, 1);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto out;
+       }
+
        if (clusters_to_add) {
                ret = ocfs2_extend_allocation(inode, clusters_to_add);
                if (ret < 0) {
                        mlog_errno(ret);
-                       goto out;
-               }
-
-               ret = ocfs2_zero_extend(inode, new_i_size);
-               if (ret < 0) {
-                       mlog_errno(ret);
-                       goto out;
+                       goto out_unlock;
                }
-       } 
+       }
 
-       /* No allocation required, we just use this helper to
-        * do a trivial update of i_size. */
-       ret = ocfs2_simple_size_update(inode, di_bh, new_i_size);
+       /*
+        * 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, (u64)new_i_size - tail_to_skip);
        if (ret < 0) {
                mlog_errno(ret);
-               goto out;
+               goto out_unlock;
+       }
+
+       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);
        }
 
+out_unlock:
+       ocfs2_data_unlock(inode, 1);
+
 out:
        return ret;
 }
@@ -793,7 +836,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);
+                       status = ocfs2_extend_file(inode, bh, attr->ia_size, 0);
                if (status < 0) {
                        if (status != -ENOSPC)
                                mlog_errno(status);
@@ -929,25 +972,23 @@ static inline int ocfs2_write_should_remove_suid(struct inode *inode)
 }
 
 static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
-                                   const char __user *buf,
-                                   size_t count,
+                                   const struct iovec *iov,
+                                   unsigned long nr_segs,
                                    loff_t pos)
 {
-       struct iovec local_iov = { .iov_base = (void __user *)buf,
-                                  .iov_len = count };
        int ret, rw_level = -1, meta_level = -1, have_alloc_sem = 0;
        u32 clusters;
        struct file *filp = iocb->ki_filp;
        struct inode *inode = filp->f_dentry->d_inode;
        loff_t newsize, saved_pos;
 
-       mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", filp, buf,
-                  (unsigned int)count,
+       mlog_entry("(0x%p, %u, '%.*s')\n", filp,
+                  (unsigned int)nr_segs,
                   filp->f_dentry->d_name.len,
                   filp->f_dentry->d_name.name);
 
        /* happy write of zero bytes */
-       if (count == 0)
+       if (iocb->ki_left == 0)
                return 0;
 
        if (!inode) {
@@ -1016,7 +1057,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
                } else {
                        saved_pos = iocb->ki_pos;
                }
-               newsize = count + saved_pos;
+               newsize = iocb->ki_left + saved_pos;
 
                mlog(0, "pos=%lld newsize=%lld cursize=%lld\n",
                     (long long) saved_pos, (long long) newsize,
@@ -1049,21 +1090,12 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
                if (!clusters)
                        break;
 
-               ret = ocfs2_extend_allocation(inode, clusters);
+               ret = ocfs2_extend_file(inode, NULL, newsize, iocb->ki_left);
                if (ret < 0) {
                        if (ret != -ENOSPC)
                                mlog_errno(ret);
                        goto out;
                }
-
-               /* Fill any holes which would've been created by this
-                * write. If we're O_APPEND, this will wind up
-                * (correctly) being a noop. */
-               ret = ocfs2_zero_extend(inode, (u64) newsize - count);
-               if (ret < 0) {
-                       mlog_errno(ret);
-                       goto out;
-               }
                break;
        }
 
@@ -1075,7 +1107,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
        /* communicate with ocfs2_dio_end_io */
        ocfs2_iocb_set_rw_locked(iocb);
 
-       ret = generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);
+       ret = generic_file_aio_write_nolock(iocb, iov, nr_segs, iocb->ki_pos);
 
        /* buffered aio wouldn't have proper lock coverage today */
        BUG_ON(ret == -EIOCBQUEUED && !(filp->f_flags & O_DIRECT));
@@ -1109,16 +1141,16 @@ out:
 }
 
 static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
-                                  char __user *buf,
-                                  size_t count,
+                                  const struct iovec *iov,
+                                  unsigned long nr_segs,
                                   loff_t pos)
 {
        int ret = 0, rw_level = -1, have_alloc_sem = 0;
        struct file *filp = iocb->ki_filp;
        struct inode *inode = filp->f_dentry->d_inode;
 
-       mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", filp, buf,
-                  (unsigned int)count,
+       mlog_entry("(0x%p, %u, '%.*s')\n", filp,
+                  (unsigned int)nr_segs,
                   filp->f_dentry->d_name.len,
                   filp->f_dentry->d_name.name);
 
@@ -1146,7 +1178,23 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
                ocfs2_iocb_set_rw_locked(iocb);
        }
 
-       ret = generic_file_aio_read(iocb, buf, count, iocb->ki_pos);
+       /*
+        * We're fine letting folks race truncates and extending
+        * writes with read across the cluster, just like they can
+        * locally. Hence no rw_lock during read.
+        * 
+        * Take and drop the meta data lock to update inode fields
+        * like i_size. This allows the checks down below
+        * generic_file_aio_read() a chance of actually working. 
+        */
+       ret = ocfs2_meta_lock(inode, NULL, NULL, 0);
+       if (ret < 0) {
+               mlog_errno(ret);
+               goto bail;
+       }
+       ocfs2_meta_unlock(inode, 0);
+
+       ret = generic_file_aio_read(iocb, iov, nr_segs, iocb->ki_pos);
        if (ret == -EINVAL)
                mlog(ML_ERROR, "generic_file_aio_read returned -EINVAL\n");
 
@@ -1189,10 +1237,12 @@ const struct file_operations ocfs2_fops = {
        .open           = ocfs2_file_open,
        .aio_read       = ocfs2_file_aio_read,
        .aio_write      = ocfs2_file_aio_write,
+       .ioctl          = ocfs2_ioctl,
 };
 
 const struct file_operations ocfs2_dops = {
        .read           = generic_read_dir,
        .readdir        = ocfs2_readdir,
        .fsync          = ocfs2_sync_file,
+       .ioctl          = ocfs2_ioctl,
 };