|
@@ -263,17 +263,19 @@ static const struct rpc_call_ops nfs_read_direct_ops = {
|
|
* handled automatically by nfs_direct_read_result(). Otherwise, if
|
|
* handled automatically by nfs_direct_read_result(). Otherwise, if
|
|
* no requests have been sent, just return an error.
|
|
* 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 nfs_open_context *ctx = dreq->ctx;
|
|
struct inode *inode = ctx->path.dentry->d_inode;
|
|
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;
|
|
size_t rsize = NFS_SERVER(inode)->rsize;
|
|
unsigned int pgbase;
|
|
unsigned int pgbase;
|
|
int result;
|
|
int result;
|
|
ssize_t started = 0;
|
|
ssize_t started = 0;
|
|
|
|
|
|
- get_dreq(dreq);
|
|
|
|
-
|
|
|
|
do {
|
|
do {
|
|
struct nfs_read_data *data;
|
|
struct nfs_read_data *data;
|
|
size_t bytes;
|
|
size_t bytes;
|
|
@@ -347,15 +349,46 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
|
|
count -= bytes;
|
|
count -= bytes;
|
|
} while (count != 0);
|
|
} 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))
|
|
if (put_dreq(dreq))
|
|
nfs_direct_complete(dreq);
|
|
nfs_direct_complete(dreq);
|
|
|
|
|
|
- if (started)
|
|
|
|
|
|
+ if (requested_bytes != 0)
|
|
return 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;
|
|
ssize_t result = 0;
|
|
sigset_t oldset;
|
|
sigset_t oldset;
|
|
@@ -372,9 +405,8 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size
|
|
if (!is_sync_kiocb(iocb))
|
|
if (!is_sync_kiocb(iocb))
|
|
dreq->iocb = iocb;
|
|
dreq->iocb = iocb;
|
|
|
|
|
|
- nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count);
|
|
|
|
rpc_clnt_sigmask(clnt, &oldset);
|
|
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)
|
|
if (!result)
|
|
result = nfs_direct_wait(dreq);
|
|
result = nfs_direct_wait(dreq);
|
|
rpc_clnt_sigunmask(clnt, &oldset);
|
|
rpc_clnt_sigunmask(clnt, &oldset);
|
|
@@ -601,17 +633,19 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
|
|
* handled automatically by nfs_direct_write_result(). Otherwise, if
|
|
* handled automatically by nfs_direct_write_result(). Otherwise, if
|
|
* no requests have been sent, just return an error.
|
|
* 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 nfs_open_context *ctx = dreq->ctx;
|
|
struct inode *inode = ctx->path.dentry->d_inode;
|
|
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;
|
|
size_t wsize = NFS_SERVER(inode)->wsize;
|
|
unsigned int pgbase;
|
|
unsigned int pgbase;
|
|
int result;
|
|
int result;
|
|
ssize_t started = 0;
|
|
ssize_t started = 0;
|
|
|
|
|
|
- get_dreq(dreq);
|
|
|
|
-
|
|
|
|
do {
|
|
do {
|
|
struct nfs_write_data *data;
|
|
struct nfs_write_data *data;
|
|
size_t bytes;
|
|
size_t bytes;
|
|
@@ -689,15 +723,48 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
|
|
count -= bytes;
|
|
count -= bytes;
|
|
} while (count != 0);
|
|
} 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))
|
|
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 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;
|
|
ssize_t result = 0;
|
|
sigset_t oldset;
|
|
sigset_t oldset;
|
|
@@ -720,10 +787,8 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz
|
|
if (!is_sync_kiocb(iocb))
|
|
if (!is_sync_kiocb(iocb))
|
|
dreq->iocb = iocb;
|
|
dreq->iocb = iocb;
|
|
|
|
|
|
- nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, count);
|
|
|
|
-
|
|
|
|
rpc_clnt_sigmask(clnt, &oldset);
|
|
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)
|
|
if (!result)
|
|
result = nfs_direct_wait(dreq);
|
|
result = nfs_direct_wait(dreq);
|
|
rpc_clnt_sigunmask(clnt, &oldset);
|
|
rpc_clnt_sigunmask(clnt, &oldset);
|
|
@@ -759,21 +824,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
|
|
ssize_t retval = -EINVAL;
|
|
ssize_t retval = -EINVAL;
|
|
struct file *file = iocb->ki_filp;
|
|
struct file *file = iocb->ki_filp;
|
|
struct address_space *mapping = file->f_mapping;
|
|
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_DIRECTREADBYTES, count);
|
|
|
|
|
|
- dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n",
|
|
|
|
|
|
+ dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n",
|
|
file->f_path.dentry->d_parent->d_name.name,
|
|
file->f_path.dentry->d_parent->d_name.name,
|
|
file->f_path.dentry->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;
|
|
retval = 0;
|
|
if (!count)
|
|
if (!count)
|
|
goto out;
|
|
goto out;
|
|
@@ -782,7 +842,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
|
|
if (retval)
|
|
if (retval)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos);
|
|
|
|
|
|
+ retval = nfs_direct_read(iocb, iov, nr_segs, pos);
|
|
if (retval > 0)
|
|
if (retval > 0)
|
|
iocb->ki_pos = pos + retval;
|
|
iocb->ki_pos = pos + retval;
|
|
|
|
|
|
@@ -821,21 +881,21 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
|
ssize_t retval = -EINVAL;
|
|
ssize_t retval = -EINVAL;
|
|
struct file *file = iocb->ki_filp;
|
|
struct file *file = iocb->ki_filp;
|
|
struct address_space *mapping = file->f_mapping;
|
|
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_parent->d_name.name,
|
|
file->f_path.dentry->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);
|
|
retval = generic_write_checks(file, &pos, &count, 0);
|
|
if (retval)
|
|
if (retval)
|
|
goto out;
|
|
goto out;
|
|
|
|
+ if (!count)
|
|
|
|
+ goto out; /* return 0 */
|
|
|
|
|
|
retval = -EINVAL;
|
|
retval = -EINVAL;
|
|
if ((ssize_t) count < 0)
|
|
if ((ssize_t) count < 0)
|
|
@@ -844,15 +904,11 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
|
|
if (!count)
|
|
if (!count)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- retval = -EFAULT;
|
|
|
|
- if (!access_ok(VERIFY_READ, buf, count))
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
retval = nfs_sync_mapping(mapping);
|
|
retval = nfs_sync_mapping(mapping);
|
|
if (retval)
|
|
if (retval)
|
|
goto out;
|
|
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)
|
|
if (retval > 0)
|
|
iocb->ki_pos = pos + retval;
|
|
iocb->ki_pos = pos + retval;
|