Skip to content

Commit e3786b2

Browse files
martinetdbrauner
authored andcommitted
9p: Fix DIO read through netfs
If a program is watching a file on a 9p mount, it won't see any change in size if the file being exported by the server is changed directly in the source filesystem, presumably because 9p doesn't have change notifications, and because netfs skips the reads if the file is empty. Fix this by attempting to read the full size specified when a DIO read is requested (such as when 9p is operating in unbuffered mode) and dealing with a short read if the EOF was less than the expected read. To make this work, filesystems using netfslib must not set NETFS_SREQ_CLEAR_TAIL if performing a DIO read where that read hit the EOF. I don't want to mandatorily clear this flag in netfslib for DIO because, say, ceph might make a read from an object that is not completely filled, but does not reside at the end of file - and so we need to clear the excess. This can be tested by watching an empty file over 9p within a VM (such as in the ktest framework): while true; do read content; if [ -n "$content" ]; then echo $content; break; fi; done < /host/tmp/foo then writing something into the empty file. The watcher should immediately display the file content and break out of the loop. Without this fix, it remains in the loop indefinitely. Fixes: 80105ed ("9p: Use netfslib read/write_iter") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218916 Signed-off-by: David Howells <[email protected]> Link: https://lore.kernel.org/r/[email protected] cc: Eric Van Hensbergen <[email protected]> cc: Latchesar Ionkov <[email protected]> cc: Christian Schoenebeck <[email protected]> cc: Marc Dionne <[email protected]> cc: Ilya Dryomov <[email protected]> cc: Steve French <[email protected]> cc: Paulo Alcantara <[email protected]> cc: Trond Myklebust <[email protected]> cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] cc: [email protected] Signed-off-by: Dominique Martinet <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 2a06298 commit e3786b2

File tree

6 files changed

+23
-12
lines changed

6 files changed

+23
-12
lines changed

fs/9p/vfs_addr.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq)
7575

7676
/* if we just extended the file size, any portion not in
7777
* cache won't be on server and is zeroes */
78-
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
78+
if (subreq->rreq->origin != NETFS_DIO_READ)
79+
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
7980

8081
netfs_subreq_terminated(subreq, err ?: total, false);
8182
}

fs/afs/file.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ static void afs_fetch_data_notify(struct afs_operation *op)
242242

243243
req->error = error;
244244
if (subreq) {
245-
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
245+
if (subreq->rreq->origin != NETFS_DIO_READ)
246+
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
246247
netfs_subreq_terminated(subreq, error ?: req->actual_len, false);
247248
req->subreq = NULL;
248249
} else if (req->done) {

fs/ceph/addr.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ static void finish_netfs_read(struct ceph_osd_request *req)
246246
if (err >= 0) {
247247
if (sparse && err > 0)
248248
err = ceph_sparse_ext_map_end(op);
249-
if (err < subreq->len)
249+
if (err < subreq->len &&
250+
subreq->rreq->origin != NETFS_DIO_READ)
250251
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
251252
if (IS_ENCRYPTED(inode) && err > 0) {
252253
err = ceph_fscrypt_decrypt_extents(inode,
@@ -282,7 +283,8 @@ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
282283
size_t len;
283284
int mode;
284285

285-
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
286+
if (rreq->origin != NETFS_DIO_READ)
287+
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
286288
__clear_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
287289

288290
if (subreq->start >= inode->i_size)

fs/netfs/io.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,8 @@ void netfs_subreq_terminated(struct netfs_io_subrequest *subreq,
530530

531531
if (transferred_or_error == 0) {
532532
if (__test_and_set_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags)) {
533-
subreq->error = -ENODATA;
533+
if (rreq->origin != NETFS_DIO_READ)
534+
subreq->error = -ENODATA;
534535
goto failed;
535536
}
536537
} else {
@@ -601,9 +602,14 @@ netfs_rreq_prepare_read(struct netfs_io_request *rreq,
601602
}
602603
if (subreq->len > ictx->zero_point - subreq->start)
603604
subreq->len = ictx->zero_point - subreq->start;
605+
606+
/* We limit buffered reads to the EOF, but let the
607+
* server deal with larger-than-EOF DIO/unbuffered
608+
* reads.
609+
*/
610+
if (subreq->len > rreq->i_size - subreq->start)
611+
subreq->len = rreq->i_size - subreq->start;
604612
}
605-
if (subreq->len > rreq->i_size - subreq->start)
606-
subreq->len = rreq->i_size - subreq->start;
607613
if (rreq->rsize && subreq->len > rreq->rsize)
608614
subreq->len = rreq->rsize;
609615

@@ -739,11 +745,10 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync)
739745
do {
740746
_debug("submit %llx + %llx >= %llx",
741747
rreq->start, rreq->submitted, rreq->i_size);
742-
if (rreq->origin == NETFS_DIO_READ &&
743-
rreq->start + rreq->submitted >= rreq->i_size)
744-
break;
745748
if (!netfs_rreq_submit_slice(rreq, &io_iter))
746749
break;
750+
if (test_bit(NETFS_SREQ_NO_PROGRESS, &rreq->flags))
751+
break;
747752
if (test_bit(NETFS_RREQ_BLOCKED, &rreq->flags) &&
748753
test_bit(NETFS_RREQ_NONBLOCK, &rreq->flags))
749754
break;

fs/nfs/fscache.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ void nfs_netfs_read_completion(struct nfs_pgio_header *hdr)
363363
return;
364364

365365
sreq = netfs->sreq;
366-
if (test_bit(NFS_IOHDR_EOF, &hdr->flags))
366+
if (test_bit(NFS_IOHDR_EOF, &hdr->flags) &&
367+
sreq->rreq->origin != NETFS_DIO_READ)
367368
__set_bit(NETFS_SREQ_CLEAR_TAIL, &sreq->flags);
368369

369370
if (hdr->error)

fs/smb/client/file.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@ static void cifs_req_issue_read(struct netfs_io_subrequest *subreq)
217217
goto out;
218218
}
219219

220-
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
220+
if (subreq->rreq->origin != NETFS_DIO_READ)
221+
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
221222

222223
rc = rdata->server->ops->async_readv(rdata);
223224
out:

0 commit comments

Comments
 (0)