[SPARC]: Constify function pointer tables.
[pandora-kernel.git] / fs / nfs / direct.c
index fcf4d38..3c9d16b 100644 (file)
@@ -263,17 +263,19 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
  * handled automatically by nfs_direct_read_result().  Otherwise, if
  * no requests have been sent, just return an error.
  */
-static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos)
+static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq,
+                                               const struct iovec *iov,
+                                               loff_t pos)
 {
        struct nfs_open_context *ctx = dreq->ctx;
        struct inode *inode = ctx->path.dentry->d_inode;
+       unsigned long user_addr = (unsigned long)iov->iov_base;
+       size_t count = iov->iov_len;
        size_t rsize = NFS_SERVER(inode)->rsize;
        unsigned int pgbase;
        int result;
        ssize_t started = 0;
 
-       get_dreq(dreq);
-
        do {
                struct nfs_read_data *data;
                size_t bytes;
@@ -347,15 +349,46 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
                count -= bytes;
        } while (count != 0);
 
+       if (started)
+               return started;
+       return result < 0 ? (ssize_t) result : -EFAULT;
+}
+
+static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
+                                             const struct iovec *iov,
+                                             unsigned long nr_segs,
+                                             loff_t pos)
+{
+       ssize_t result = -EINVAL;
+       size_t requested_bytes = 0;
+       unsigned long seg;
+
+       get_dreq(dreq);
+
+       for (seg = 0; seg < nr_segs; seg++) {
+               const struct iovec *vec = &iov[seg];
+               result = nfs_direct_read_schedule_segment(dreq, vec, pos);
+               if (result < 0)
+                       break;
+               requested_bytes += result;
+               if ((size_t)result < vec->iov_len)
+                       break;
+               pos += vec->iov_len;
+       }
+
        if (put_dreq(dreq))
                nfs_direct_complete(dreq);
 
-       if (started)
+       if (requested_bytes != 0)
                return 0;
-       return result < 0 ? (ssize_t) result : -EFAULT;
+
+       if (result < 0)
+               return result;
+       return -EIO;
 }
 
-static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
+static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
+                              unsigned long nr_segs, loff_t pos)
 {
        ssize_t result = 0;
        sigset_t oldset;
@@ -368,13 +401,12 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size
                return -ENOMEM;
 
        dreq->inode = inode;
-       dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data);
+       dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
        rpc_clnt_sigmask(clnt, &oldset);
-       result = nfs_direct_read_schedule(dreq, user_addr, count, pos);
+       result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
        if (!result)
                result = nfs_direct_wait(dreq);
        rpc_clnt_sigunmask(clnt, &oldset);
@@ -510,7 +542,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
                        nfs_direct_write_reschedule(dreq);
                        break;
                default:
-                       nfs_end_data_update(inode);
                        if (dreq->commit_data != NULL)
                                nfs_commit_free(dreq->commit_data);
                        nfs_direct_free_writedata(dreq);
@@ -533,7 +564,6 @@ static inline void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
 
 static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
 {
-       nfs_end_data_update(inode);
        nfs_direct_free_writedata(dreq);
        nfs_zap_mapping(inode, inode->i_mapping);
        nfs_direct_complete(dreq);
@@ -551,13 +581,13 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
 
        spin_lock(&dreq->lock);
 
-       if (unlikely(dreq->error != 0))
-               goto out_unlock;
        if (unlikely(status < 0)) {
-               /* An error has occured, so we should not commit */
+               /* An error has occurred, so we should not commit */
                dreq->flags = 0;
                dreq->error = status;
        }
+       if (unlikely(dreq->error != 0))
+               goto out_unlock;
 
        dreq->count += data->res.count;
 
@@ -603,17 +633,19 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
  * handled automatically by nfs_direct_write_result().  Otherwise, if
  * no requests have been sent, just return an error.
  */
-static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync)
+static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
+                                                const struct iovec *iov,
+                                                loff_t pos, int sync)
 {
        struct nfs_open_context *ctx = dreq->ctx;
        struct inode *inode = ctx->path.dentry->d_inode;
+       unsigned long user_addr = (unsigned long)iov->iov_base;
+       size_t count = iov->iov_len;
        size_t wsize = NFS_SERVER(inode)->wsize;
        unsigned int pgbase;
        int result;
        ssize_t started = 0;
 
-       get_dreq(dreq);
-
        do {
                struct nfs_write_data *data;
                size_t bytes;
@@ -691,15 +723,48 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
                count -= bytes;
        } while (count != 0);
 
+       if (started)
+               return started;
+       return result < 0 ? (ssize_t) result : -EFAULT;
+}
+
+static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
+                                              const struct iovec *iov,
+                                              unsigned long nr_segs,
+                                              loff_t pos, int sync)
+{
+       ssize_t result = 0;
+       size_t requested_bytes = 0;
+       unsigned long seg;
+
+       get_dreq(dreq);
+
+       for (seg = 0; seg < nr_segs; seg++) {
+               const struct iovec *vec = &iov[seg];
+               result = nfs_direct_write_schedule_segment(dreq, vec,
+                                                          pos, sync);
+               if (result < 0)
+                       break;
+               requested_bytes += result;
+               if ((size_t)result < vec->iov_len)
+                       break;
+               pos += vec->iov_len;
+       }
+
        if (put_dreq(dreq))
-               nfs_direct_write_complete(dreq, inode);
+               nfs_direct_write_complete(dreq, dreq->inode);
 
-       if (started)
+       if (requested_bytes != 0)
                return 0;
-       return result < 0 ? (ssize_t) result : -EFAULT;
+
+       if (result < 0)
+               return result;
+       return -EIO;
 }
 
-static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos)
+static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
+                               unsigned long nr_segs, loff_t pos,
+                               size_t count)
 {
        ssize_t result = 0;
        sigset_t oldset;
@@ -718,16 +783,12 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
                sync = FLUSH_STABLE;
 
        dreq->inode = inode;
-       dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data);
+       dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp));
        if (!is_sync_kiocb(iocb))
                dreq->iocb = iocb;
 
-       nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, count);
-
-       nfs_begin_data_update(inode);
-
        rpc_clnt_sigmask(clnt, &oldset);
-       result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync);
+       result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync);
        if (!result)
                result = nfs_direct_wait(dreq);
        rpc_clnt_sigunmask(clnt, &oldset);
@@ -763,21 +824,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
        ssize_t retval = -EINVAL;
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
-       /* XXX: temporary */
-       const char __user *buf = iov[0].iov_base;
-       size_t count = iov[0].iov_len;
+       size_t count;
 
-       dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n",
+       count = iov_length(iov, nr_segs);
+       nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count);
+
+       dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n",
                file->f_path.dentry->d_parent->d_name.name,
                file->f_path.dentry->d_name.name,
-               (unsigned long) count, (long long) pos);
-
-       if (nr_segs != 1)
-               goto out;
+               count, (long long) pos);
 
-       retval = -EFAULT;
-       if (!access_ok(VERIFY_WRITE, buf, count))
-               goto out;
        retval = 0;
        if (!count)
                goto out;
@@ -786,7 +842,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
        if (retval)
                goto out;
 
-       retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos);
+       retval = nfs_direct_read(iocb, iov, nr_segs, pos);
        if (retval > 0)
                iocb->ki_pos = pos + retval;
 
@@ -825,17 +881,15 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
        ssize_t retval = -EINVAL;
        struct file *file = iocb->ki_filp;
        struct address_space *mapping = file->f_mapping;
-       /* XXX: temporary */
-       const char __user *buf = iov[0].iov_base;
-       size_t count = iov[0].iov_len;
+       size_t count;
+
+       count = iov_length(iov, nr_segs);
+       nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count);
 
-       dprintk("nfs: direct write(%s/%s, %lu@%Ld)\n",
+       dfprintk(VFS, "nfs: direct write(%s/%s, %zd@%Ld)\n",
                file->f_path.dentry->d_parent->d_name.name,
                file->f_path.dentry->d_name.name,
-               (unsigned long) count, (long long) pos);
-
-       if (nr_segs != 1)
-               goto out;
+               count, (long long) pos);
 
        retval = generic_write_checks(file, &pos, &count, 0);
        if (retval)
@@ -848,15 +902,11 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
        if (!count)
                goto out;
 
-       retval = -EFAULT;
-       if (!access_ok(VERIFY_READ, buf, count))
-               goto out;
-
        retval = nfs_sync_mapping(mapping);
        if (retval)
                goto out;
 
-       retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos);
+       retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);
 
        if (retval > 0)
                iocb->ki_pos = pos + retval;