splice: direct splicing updates ppos twice
authorJens Axboe <jens.axboe@oracle.com>
Mon, 16 Jul 2007 12:41:49 +0000 (14:41 +0200)
committerJens Axboe <jens.axboe@oracle.com>
Mon, 16 Jul 2007 13:02:48 +0000 (15:02 +0200)
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> reported that he's noticed
nfsd read corruption in recent kernels, and did the hard work of
discovering that it's due to splice updating the file position twice.
This means that the next operation would start further ahead than it
should.

nfsd_vfs_read()
    splice_direct_to_actor()
        while(len) {
            do_splice_to()                     [update sd->pos]
                -> generic_file_splice_read()  [read from sd->pos]
            nfsd_direct_splice_actor()
                -> __splice_from_pipe()        [update sd->pos]

There's nothing wrong with the core splice code, but the direct
splicing is an addon that calls both input and output paths.
So it has to take care in locally caching offset so it remains correct.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
fs/splice.c

index 6c9828651e6f5cdec78ce99d5169bb238c61f0ea..53fc2082a4681fdc311adcf0596f11fb6d45a8c5 100644 (file)
@@ -1061,8 +1061,9 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
 
        while (len) {
                size_t read_len;
 
        while (len) {
                size_t read_len;
+               loff_t pos = sd->pos;
 
 
-               ret = do_splice_to(in, &sd->pos, pipe, len, flags);
+               ret = do_splice_to(in, &pos, pipe, len, flags);
                if (unlikely(ret <= 0))
                        goto out_release;
 
                if (unlikely(ret <= 0))
                        goto out_release;
 
@@ -1080,6 +1081,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
 
                bytes += ret;
                len -= ret;
 
                bytes += ret;
                len -= ret;
+               sd->pos = pos;
 
                if (ret < read_len)
                        goto out_release;
 
                if (ret < read_len)
                        goto out_release;