diff options
Diffstat (limited to 'fs/nfs/direct.c')
-rw-r--r-- | fs/nfs/direct.c | 142 |
1 files changed, 99 insertions, 43 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index afcab007a22b..5e8d82f6666b 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c | |||
@@ -263,17 +263,19 @@ static const struct rpc_call_ops nfs_read_direct_ops = { | |||
263 | * handled automatically by nfs_direct_read_result(). Otherwise, if | 263 | * handled automatically by nfs_direct_read_result(). Otherwise, if |
264 | * no requests have been sent, just return an error. | 264 | * no requests have been sent, just return an error. |
265 | */ | 265 | */ |
266 | static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) | 266 | static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq, |
267 | const struct iovec *iov, | ||
268 | loff_t pos) | ||
267 | { | 269 | { |
268 | struct nfs_open_context *ctx = dreq->ctx; | 270 | struct nfs_open_context *ctx = dreq->ctx; |
269 | struct inode *inode = ctx->path.dentry->d_inode; | 271 | struct inode *inode = ctx->path.dentry->d_inode; |
272 | unsigned long user_addr = (unsigned long)iov->iov_base; | ||
273 | size_t count = iov->iov_len; | ||
270 | size_t rsize = NFS_SERVER(inode)->rsize; | 274 | size_t rsize = NFS_SERVER(inode)->rsize; |
271 | unsigned int pgbase; | 275 | unsigned int pgbase; |
272 | int result; | 276 | int result; |
273 | ssize_t started = 0; | 277 | ssize_t started = 0; |
274 | 278 | ||
275 | get_dreq(dreq); | ||
276 | |||
277 | do { | 279 | do { |
278 | struct nfs_read_data *data; | 280 | struct nfs_read_data *data; |
279 | size_t bytes; | 281 | size_t bytes; |
@@ -347,15 +349,46 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo | |||
347 | count -= bytes; | 349 | count -= bytes; |
348 | } while (count != 0); | 350 | } while (count != 0); |
349 | 351 | ||
352 | if (started) | ||
353 | return started; | ||
354 | return result < 0 ? (ssize_t) result : -EFAULT; | ||
355 | } | ||
356 | |||
357 | static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, | ||
358 | const struct iovec *iov, | ||
359 | unsigned long nr_segs, | ||
360 | loff_t pos) | ||
361 | { | ||
362 | ssize_t result = -EINVAL; | ||
363 | size_t requested_bytes = 0; | ||
364 | unsigned long seg; | ||
365 | |||
366 | get_dreq(dreq); | ||
367 | |||
368 | for (seg = 0; seg < nr_segs; seg++) { | ||
369 | const struct iovec *vec = &iov[seg]; | ||
370 | result = nfs_direct_read_schedule_segment(dreq, vec, pos); | ||
371 | if (result < 0) | ||
372 | break; | ||
373 | requested_bytes += result; | ||
374 | if ((size_t)result < vec->iov_len) | ||
375 | break; | ||
376 | pos += vec->iov_len; | ||
377 | } | ||
378 | |||
350 | if (put_dreq(dreq)) | 379 | if (put_dreq(dreq)) |
351 | nfs_direct_complete(dreq); | 380 | nfs_direct_complete(dreq); |
352 | 381 | ||
353 | if (started) | 382 | if (requested_bytes != 0) |
354 | return 0; | 383 | return 0; |
355 | return result < 0 ? (ssize_t) result : -EFAULT; | 384 | |
385 | if (result < 0) | ||
386 | return result; | ||
387 | return -EIO; | ||
356 | } | 388 | } |
357 | 389 | ||
358 | static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos) | 390 | static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, |
391 | unsigned long nr_segs, loff_t pos) | ||
359 | { | 392 | { |
360 | ssize_t result = 0; | 393 | ssize_t result = 0; |
361 | sigset_t oldset; | 394 | sigset_t oldset; |
@@ -372,9 +405,8 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size | |||
372 | if (!is_sync_kiocb(iocb)) | 405 | if (!is_sync_kiocb(iocb)) |
373 | dreq->iocb = iocb; | 406 | dreq->iocb = iocb; |
374 | 407 | ||
375 | nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count); | ||
376 | rpc_clnt_sigmask(clnt, &oldset); | 408 | rpc_clnt_sigmask(clnt, &oldset); |
377 | result = nfs_direct_read_schedule(dreq, user_addr, count, pos); | 409 | result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); |
378 | if (!result) | 410 | if (!result) |
379 | result = nfs_direct_wait(dreq); | 411 | result = nfs_direct_wait(dreq); |
380 | rpc_clnt_sigunmask(clnt, &oldset); | 412 | rpc_clnt_sigunmask(clnt, &oldset); |
@@ -601,17 +633,19 @@ static const struct rpc_call_ops nfs_write_direct_ops = { | |||
601 | * handled automatically by nfs_direct_write_result(). Otherwise, if | 633 | * handled automatically by nfs_direct_write_result(). Otherwise, if |
602 | * no requests have been sent, just return an error. | 634 | * no requests have been sent, just return an error. |
603 | */ | 635 | */ |
604 | static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync) | 636 | static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, |
637 | const struct iovec *iov, | ||
638 | loff_t pos, int sync) | ||
605 | { | 639 | { |
606 | struct nfs_open_context *ctx = dreq->ctx; | 640 | struct nfs_open_context *ctx = dreq->ctx; |
607 | struct inode *inode = ctx->path.dentry->d_inode; | 641 | struct inode *inode = ctx->path.dentry->d_inode; |
642 | unsigned long user_addr = (unsigned long)iov->iov_base; | ||
643 | size_t count = iov->iov_len; | ||
608 | size_t wsize = NFS_SERVER(inode)->wsize; | 644 | size_t wsize = NFS_SERVER(inode)->wsize; |
609 | unsigned int pgbase; | 645 | unsigned int pgbase; |
610 | int result; | 646 | int result; |
611 | ssize_t started = 0; | 647 | ssize_t started = 0; |
612 | 648 | ||
613 | get_dreq(dreq); | ||
614 | |||
615 | do { | 649 | do { |
616 | struct nfs_write_data *data; | 650 | struct nfs_write_data *data; |
617 | size_t bytes; | 651 | size_t bytes; |
@@ -689,15 +723,48 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l | |||
689 | count -= bytes; | 723 | count -= bytes; |
690 | } while (count != 0); | 724 | } while (count != 0); |
691 | 725 | ||
726 | if (started) | ||
727 | return started; | ||
728 | return result < 0 ? (ssize_t) result : -EFAULT; | ||
729 | } | ||
730 | |||
731 | static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, | ||
732 | const struct iovec *iov, | ||
733 | unsigned long nr_segs, | ||
734 | loff_t pos, int sync) | ||
735 | { | ||
736 | ssize_t result = 0; | ||
737 | size_t requested_bytes = 0; | ||
738 | unsigned long seg; | ||
739 | |||
740 | get_dreq(dreq); | ||
741 | |||
742 | for (seg = 0; seg < nr_segs; seg++) { | ||
743 | const struct iovec *vec = &iov[seg]; | ||
744 | result = nfs_direct_write_schedule_segment(dreq, vec, | ||
745 | pos, sync); | ||
746 | if (result < 0) | ||
747 | break; | ||
748 | requested_bytes += result; | ||
749 | if ((size_t)result < vec->iov_len) | ||
750 | break; | ||
751 | pos += vec->iov_len; | ||
752 | } | ||
753 | |||
692 | if (put_dreq(dreq)) | 754 | if (put_dreq(dreq)) |
693 | nfs_direct_write_complete(dreq, inode); | 755 | nfs_direct_write_complete(dreq, dreq->inode); |
694 | 756 | ||
695 | if (started) | 757 | if (requested_bytes != 0) |
696 | return 0; | 758 | return 0; |
697 | return result < 0 ? (ssize_t) result : -EFAULT; | 759 | |
760 | if (result < 0) | ||
761 | return result; | ||
762 | return -EIO; | ||
698 | } | 763 | } |
699 | 764 | ||
700 | static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos) | 765 | static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, |
766 | unsigned long nr_segs, loff_t pos, | ||
767 | size_t count) | ||
701 | { | 768 | { |
702 | ssize_t result = 0; | 769 | ssize_t result = 0; |
703 | sigset_t oldset; | 770 | sigset_t oldset; |
@@ -720,10 +787,8 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz | |||
720 | if (!is_sync_kiocb(iocb)) | 787 | if (!is_sync_kiocb(iocb)) |
721 | dreq->iocb = iocb; | 788 | dreq->iocb = iocb; |
722 | 789 | ||
723 | nfs_add_stats(inode, NFSIOS_DIRECTWRITTENBYTES, count); | ||
724 | |||
725 | rpc_clnt_sigmask(clnt, &oldset); | 790 | rpc_clnt_sigmask(clnt, &oldset); |
726 | result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync); | 791 | result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync); |
727 | if (!result) | 792 | if (!result) |
728 | result = nfs_direct_wait(dreq); | 793 | result = nfs_direct_wait(dreq); |
729 | rpc_clnt_sigunmask(clnt, &oldset); | 794 | rpc_clnt_sigunmask(clnt, &oldset); |
@@ -759,21 +824,16 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, | |||
759 | ssize_t retval = -EINVAL; | 824 | ssize_t retval = -EINVAL; |
760 | struct file *file = iocb->ki_filp; | 825 | struct file *file = iocb->ki_filp; |
761 | struct address_space *mapping = file->f_mapping; | 826 | struct address_space *mapping = file->f_mapping; |
762 | /* XXX: temporary */ | 827 | size_t count; |
763 | const char __user *buf = iov[0].iov_base; | 828 | |
764 | size_t count = iov[0].iov_len; | 829 | count = iov_length(iov, nr_segs); |
830 | nfs_add_stats(mapping->host, NFSIOS_DIRECTREADBYTES, count); | ||
765 | 831 | ||
766 | dprintk("nfs: direct read(%s/%s, %lu@%Ld)\n", | 832 | dprintk("nfs: direct read(%s/%s, %zd@%Ld)\n", |
767 | file->f_path.dentry->d_parent->d_name.name, | 833 | file->f_path.dentry->d_parent->d_name.name, |
768 | file->f_path.dentry->d_name.name, | 834 | file->f_path.dentry->d_name.name, |
769 | (unsigned long) count, (long long) pos); | 835 | count, (long long) pos); |
770 | |||
771 | if (nr_segs != 1) | ||
772 | goto out; | ||
773 | 836 | ||
774 | retval = -EFAULT; | ||
775 | if (!access_ok(VERIFY_WRITE, buf, count)) | ||
776 | goto out; | ||
777 | retval = 0; | 837 | retval = 0; |
778 | if (!count) | 838 | if (!count) |
779 | goto out; | 839 | goto out; |
@@ -782,7 +842,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov, | |||
782 | if (retval) | 842 | if (retval) |
783 | goto out; | 843 | goto out; |
784 | 844 | ||
785 | retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos); | 845 | retval = nfs_direct_read(iocb, iov, nr_segs, pos); |
786 | if (retval > 0) | 846 | if (retval > 0) |
787 | iocb->ki_pos = pos + retval; | 847 | iocb->ki_pos = pos + retval; |
788 | 848 | ||
@@ -821,21 +881,21 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, | |||
821 | ssize_t retval = -EINVAL; | 881 | ssize_t retval = -EINVAL; |
822 | struct file *file = iocb->ki_filp; | 882 | struct file *file = iocb->ki_filp; |
823 | struct address_space *mapping = file->f_mapping; | 883 | struct address_space *mapping = file->f_mapping; |
824 | /* XXX: temporary */ | 884 | size_t count; |
825 | const char __user *buf = iov[0].iov_base; | 885 | |
826 | size_t count = iov[0].iov_len; | 886 | count = iov_length(iov, nr_segs); |
887 | nfs_add_stats(mapping->host, NFSIOS_DIRECTWRITTENBYTES, count); | ||
827 | 888 | ||
828 | dprintk("nfs: direct write(%s/%s, %lu@%Ld)\n", | 889 | dfprintk(VFS, "nfs: direct write(%s/%s, %zd@%Ld)\n", |
829 | file->f_path.dentry->d_parent->d_name.name, | 890 | file->f_path.dentry->d_parent->d_name.name, |
830 | file->f_path.dentry->d_name.name, | 891 | file->f_path.dentry->d_name.name, |
831 | (unsigned long) count, (long long) pos); | 892 | count, (long long) pos); |
832 | |||
833 | if (nr_segs != 1) | ||
834 | goto out; | ||
835 | 893 | ||
836 | retval = generic_write_checks(file, &pos, &count, 0); | 894 | retval = generic_write_checks(file, &pos, &count, 0); |
837 | if (retval) | 895 | if (retval) |
838 | goto out; | 896 | goto out; |
897 | if (!count) | ||
898 | goto out; /* return 0 */ | ||
839 | 899 | ||
840 | retval = -EINVAL; | 900 | retval = -EINVAL; |
841 | if ((ssize_t) count < 0) | 901 | if ((ssize_t) count < 0) |
@@ -844,15 +904,11 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov, | |||
844 | if (!count) | 904 | if (!count) |
845 | goto out; | 905 | goto out; |
846 | 906 | ||
847 | retval = -EFAULT; | ||
848 | if (!access_ok(VERIFY_READ, buf, count)) | ||
849 | goto out; | ||
850 | |||
851 | retval = nfs_sync_mapping(mapping); | 907 | retval = nfs_sync_mapping(mapping); |
852 | if (retval) | 908 | if (retval) |
853 | goto out; | 909 | goto out; |
854 | 910 | ||
855 | retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos); | 911 | retval = nfs_direct_write(iocb, iov, nr_segs, pos, count); |
856 | 912 | ||
857 | if (retval > 0) | 913 | if (retval > 0) |
858 | iocb->ki_pos = pos + retval; | 914 | iocb->ki_pos = pos + retval; |