diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2022-09-10 22:14:02 +0100 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2022-09-12 22:38:36 -0400 |
commit | bfbfb6182ad1d7d184b16f25165faad879147f79 (patch) | |
tree | 0ef3f13917b04b9b11ff60f9706b068f4c024a03 | |
parent | 568035b01cfb107af8d2e4bd2fb9aea22cf5b868 (diff) | |
download | linux-stable-bfbfb6182ad1d7d184b16f25165faad879147f79.tar.gz linux-stable-bfbfb6182ad1d7d184b16f25165faad879147f79.tar.bz2 linux-stable-bfbfb6182ad1d7d184b16f25165faad879147f79.zip |
nfsd_splice_actor(): handle compound pages
pipe_buffer might refer to a compound page (and contain more than a PAGE_SIZE
worth of data). Theoretically it had been possible since way back, but
nfsd_splice_actor() hadn't run into that until copy_page_to_iter() change.
Fortunately, the only thing that changes for compound pages is that we
need to stuff each relevant subpage in and convert the offset into offset
in the first subpage.
Acked-by: Chuck Lever <chuck.lever@oracle.com>
Tested-by: Benjamin Coddington <bcodding@redhat.com>
Fixes: f0f6b614f83d "copy_page_to_iter(): don't split high-order page in case of ITER_PIPE"
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/nfsd/vfs.c | 12 |
1 files changed, 8 insertions, 4 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 9f486b788ed0..b16aed158ba6 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -846,10 +846,14 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { struct svc_rqst *rqstp = sd->u.data; - - svc_rqst_replace_page(rqstp, buf->page); - if (rqstp->rq_res.page_len == 0) - rqstp->rq_res.page_base = buf->offset; + struct page *page = buf->page; // may be a compound one + unsigned offset = buf->offset; + + page += offset / PAGE_SIZE; + for (int i = sd->len; i > 0; i -= PAGE_SIZE) + svc_rqst_replace_page(rqstp, page++); + if (rqstp->rq_res.page_len == 0) // first call + rqstp->rq_res.page_base = offset % PAGE_SIZE; rqstp->rq_res.page_len += sd->len; return sd->len; } |