+++ /dev/null
-From 56fca566d91b2a1e56ab124811765dddbbddaf71 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Thu, 8 Aug 2024 14:29:38 +0100
-Subject: 9p: Fix DIO read through netfs
-
-From: Dominique Martinet <asmadeus@codewreck.org>
-
-[ Upstream commit e3786b29c54cdae3490b07180a54e2461f42144c ]
-
-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: 80105ed2fd27 ("9p: Use netfslib read/write_iter")
-Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218916
-Signed-off-by: David Howells <dhowells@redhat.com>
-Link: https://lore.kernel.org/r/1229195.1723211769@warthog.procyon.org.uk
-cc: Eric Van Hensbergen <ericvh@kernel.org>
-cc: Latchesar Ionkov <lucho@ionkov.net>
-cc: Christian Schoenebeck <linux_oss@crudebyte.com>
-cc: Marc Dionne <marc.dionne@auristor.com>
-cc: Ilya Dryomov <idryomov@gmail.com>
-cc: Steve French <sfrench@samba.org>
-cc: Paulo Alcantara <pc@manguebit.com>
-cc: Trond Myklebust <trond.myklebust@hammerspace.com>
-cc: v9fs@lists.linux.dev
-cc: linux-afs@lists.infradead.org
-cc: ceph-devel@vger.kernel.org
-cc: linux-cifs@vger.kernel.org
-cc: linux-nfs@vger.kernel.org
-cc: netfs@lists.linux.dev
-cc: linux-fsdevel@vger.kernel.org
-Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
-Signed-off-by: Christian Brauner <brauner@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- fs/9p/vfs_addr.c | 3 ++-
- fs/afs/file.c | 3 ++-
- fs/ceph/addr.c | 6 ++++--
- fs/netfs/io.c | 17 +++++++++++------
- fs/nfs/fscache.c | 3 ++-
- fs/smb/client/file.c | 3 ++-
- 6 files changed, 23 insertions(+), 12 deletions(-)
-
-diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c
-index a97ceb105cd8d..24fdc74caeba4 100644
---- a/fs/9p/vfs_addr.c
-+++ b/fs/9p/vfs_addr.c
-@@ -75,7 +75,8 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq)
-
- /* if we just extended the file size, any portion not in
- * cache won't be on server and is zeroes */
-- __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-+ if (subreq->rreq->origin != NETFS_DIO_READ)
-+ __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-
- netfs_subreq_terminated(subreq, err ?: total, false);
- }
-diff --git a/fs/afs/file.c b/fs/afs/file.c
-index c3f0c45ae9a9b..ec1be0091fdb5 100644
---- a/fs/afs/file.c
-+++ b/fs/afs/file.c
-@@ -242,7 +242,8 @@ static void afs_fetch_data_notify(struct afs_operation *op)
-
- req->error = error;
- if (subreq) {
-- __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-+ if (subreq->rreq->origin != NETFS_DIO_READ)
-+ __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
- netfs_subreq_terminated(subreq, error ?: req->actual_len, false);
- req->subreq = NULL;
- } else if (req->done) {
-diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
-index 73b5a07bf94de..d2194022132ec 100644
---- a/fs/ceph/addr.c
-+++ b/fs/ceph/addr.c
-@@ -246,7 +246,8 @@ static void finish_netfs_read(struct ceph_osd_request *req)
- if (err >= 0) {
- if (sparse && err > 0)
- err = ceph_sparse_ext_map_end(op);
-- if (err < subreq->len)
-+ if (err < subreq->len &&
-+ subreq->rreq->origin != NETFS_DIO_READ)
- __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
- if (IS_ENCRYPTED(inode) && err > 0) {
- err = ceph_fscrypt_decrypt_extents(inode,
-@@ -282,7 +283,8 @@ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
- size_t len;
- int mode;
-
-- __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-+ if (rreq->origin != NETFS_DIO_READ)
-+ __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
- __clear_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
-
- if (subreq->start >= inode->i_size)
-diff --git a/fs/netfs/io.c b/fs/netfs/io.c
-index f3abc5dfdbc0c..19ec6990dc91e 100644
---- a/fs/netfs/io.c
-+++ b/fs/netfs/io.c
-@@ -530,7 +530,8 @@ void netfs_subreq_terminated(struct netfs_io_subrequest *subreq,
-
- if (transferred_or_error == 0) {
- if (__test_and_set_bit(NETFS_SREQ_NO_PROGRESS, &subreq->flags)) {
-- subreq->error = -ENODATA;
-+ if (rreq->origin != NETFS_DIO_READ)
-+ subreq->error = -ENODATA;
- goto failed;
- }
- } else {
-@@ -601,9 +602,14 @@ netfs_rreq_prepare_read(struct netfs_io_request *rreq,
- }
- if (subreq->len > ictx->zero_point - subreq->start)
- subreq->len = ictx->zero_point - subreq->start;
-+
-+ /* We limit buffered reads to the EOF, but let the
-+ * server deal with larger-than-EOF DIO/unbuffered
-+ * reads.
-+ */
-+ if (subreq->len > rreq->i_size - subreq->start)
-+ subreq->len = rreq->i_size - subreq->start;
- }
-- if (subreq->len > rreq->i_size - subreq->start)
-- subreq->len = rreq->i_size - subreq->start;
- if (rreq->rsize && subreq->len > rreq->rsize)
- subreq->len = rreq->rsize;
-
-@@ -739,11 +745,10 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync)
- do {
- kdebug("submit %llx + %llx >= %llx",
- rreq->start, rreq->submitted, rreq->i_size);
-- if (rreq->origin == NETFS_DIO_READ &&
-- rreq->start + rreq->submitted >= rreq->i_size)
-- break;
- if (!netfs_rreq_submit_slice(rreq, &io_iter))
- break;
-+ if (test_bit(NETFS_SREQ_NO_PROGRESS, &rreq->flags))
-+ break;
- if (test_bit(NETFS_RREQ_BLOCKED, &rreq->flags) &&
- test_bit(NETFS_RREQ_NONBLOCK, &rreq->flags))
- break;
-diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
-index ddc1ee0319554..bc20ba50283c8 100644
---- a/fs/nfs/fscache.c
-+++ b/fs/nfs/fscache.c
-@@ -361,7 +361,8 @@ void nfs_netfs_read_completion(struct nfs_pgio_header *hdr)
- return;
-
- sreq = netfs->sreq;
-- if (test_bit(NFS_IOHDR_EOF, &hdr->flags))
-+ if (test_bit(NFS_IOHDR_EOF, &hdr->flags) &&
-+ sreq->rreq->origin != NETFS_DIO_READ)
- __set_bit(NETFS_SREQ_CLEAR_TAIL, &sreq->flags);
-
- if (hdr->error)
-diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
-index 2e3c4d0277dbb..9e4f4e67768b9 100644
---- a/fs/smb/client/file.c
-+++ b/fs/smb/client/file.c
-@@ -196,7 +196,8 @@ static void cifs_req_issue_read(struct netfs_io_subrequest *subreq)
- goto out;
- }
-
-- __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-+ if (subreq->rreq->origin != NETFS_DIO_READ)
-+ __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
-
- rc = rdata->server->ops->async_readv(rdata);
- out:
---
-2.43.0
-
Stable-dep-of: 74c2ab6d653b ("smb/client: avoid possible NULL dereference in cifs_free_subrequest()")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
- fs/smb/client/cifsglob.h | 17 +++++++-----
- fs/smb/client/file.c | 32 ++++++++++++++++++++++-
- fs/smb/client/smb1ops.c | 2 +-
- fs/smb/client/smb2ops.c | 42 ++++++++++++++++++++++++++----
- fs/smb/client/smb2pdu.c | 40 +++++++++++++++++++++++++---
- fs/smb/client/trace.h | 55 ++++++++++++++++++++++++++++++++++++++-
- fs/smb/client/transport.c | 8 +++---
+ fs/smb/client/cifsglob.h | 17 ++++++++------
+ fs/smb/client/file.c | 32 +++++++++++++++++++++++++-
+ fs/smb/client/smb1ops.c | 2 -
+ fs/smb/client/smb2ops.c | 42 ++++++++++++++++++++++++++++++-----
+ fs/smb/client/smb2pdu.c | 40 ++++++++++++++++++++++++++++++---
+ fs/smb/client/trace.h | 55 +++++++++++++++++++++++++++++++++++++++++++++-
+ fs/smb/client/transport.c | 8 +++---
7 files changed, 173 insertions(+), 23 deletions(-)
-diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
-index d4bcc7da700c6..0a271b9fbc622 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -290,7 +290,7 @@ struct smb_version_operations {
/* check if we need to issue closedir */
bool (*dir_needs_close)(struct cifsFileInfo *);
long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
-@@ -848,6 +848,9 @@ static inline void cifs_server_unlock(struct TCP_Server_Info *server)
+@@ -848,6 +848,9 @@ static inline void cifs_server_unlock(st
struct cifs_credits {
unsigned int value;
unsigned int instance;
};
static inline unsigned int
-@@ -873,7 +876,7 @@ has_credits(struct TCP_Server_Info *server, int *credits, int num_credits)
+@@ -873,7 +876,7 @@ has_credits(struct TCP_Server_Info *serv
}
static inline void
const int optype)
{
server->ops->add_credits(server, credits, optype);
-@@ -897,11 +900,11 @@ set_credits(struct TCP_Server_Info *server, const int val)
+@@ -897,11 +900,11 @@ set_credits(struct TCP_Server_Info *serv
}
static inline int
}
static inline __le64
-diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
-index 9e4f4e67768b9..b413cfef05422 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
-@@ -80,6 +80,16 @@ static void cifs_prepare_write(struct netfs_io_subrequest *subreq)
+@@ -80,6 +80,16 @@ retry:
return netfs_prepare_write_failed(subreq);
}
#ifdef CONFIG_CIFS_SMB_DIRECT
if (server->smbd_conn)
subreq->max_nr_segs = server->smbd_conn->max_frmr_depth;
-@@ -101,7 +111,7 @@ static void cifs_issue_write(struct netfs_io_subrequest *subreq)
+@@ -101,7 +111,7 @@ static void cifs_issue_write(struct netf
goto fail;
}
if (rc)
goto fail;
-@@ -163,7 +173,18 @@ static bool cifs_clamp_length(struct netfs_io_subrequest *subreq)
+@@ -163,7 +173,18 @@ static bool cifs_clamp_length(struct net
return false;
}
#ifdef CONFIG_CIFS_SMB_DIRECT
if (server->smbd_conn)
subreq->max_nr_segs = server->smbd_conn->max_frmr_depth;
-@@ -295,6 +316,15 @@ static void cifs_free_subrequest(struct netfs_io_subrequest *subreq)
+@@ -294,6 +315,15 @@ static void cifs_free_subrequest(struct
#endif
}
add_credits_and_wake_if(rdata->server, &rdata->credits, 0);
if (rdata->have_xid)
free_xid(rdata->xid);
-diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
-index 212ec6f66ec65..e1f2feb56f45f 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
-@@ -108,7 +108,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
+@@ -108,7 +108,7 @@ cifs_find_mid(struct TCP_Server_Info *se
static void
cifs_add_credits(struct TCP_Server_Info *server,
{
spin_lock(&server->req_lock);
server->credits += credits->value;
-diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
-index c8e536540895a..7fe59235f0901 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
-@@ -66,7 +66,7 @@ change_conf(struct TCP_Server_Info *server)
+@@ -66,7 +66,7 @@ change_conf(struct TCP_Server_Info *serv
static void
smb2_add_credits(struct TCP_Server_Info *server,
{
int *val, rc = -1;
int scredits, in_flight;
-@@ -94,7 +94,21 @@ smb2_add_credits(struct TCP_Server_Info *server,
+@@ -94,7 +94,21 @@ smb2_add_credits(struct TCP_Server_Info
server->conn_id, server->hostname, *val,
add, server->in_flight);
}
server->in_flight--;
if (server->in_flight == 0 &&
((optype & CIFS_OP_MASK) != CIFS_NEG_OP) &&
-@@ -283,16 +297,23 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,
+@@ -283,16 +297,23 @@ smb2_wait_mtu_credits(struct TCP_Server_
static int
smb2_adjust_credits(struct TCP_Server_Info *server,
trace_smb3_too_many_credits(server->CurrentMid,
server->conn_id, server->hostname, 0, credits->value - new_val, 0);
cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)",
-@@ -308,6 +329,12 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
+@@ -308,6 +329,12 @@ smb2_adjust_credits(struct TCP_Server_In
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_reconnect_detected(server->CurrentMid,
server->conn_id, server->hostname, scredits,
credits->value - new_val, in_flight);
-@@ -316,6 +343,11 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
+@@ -316,6 +343,11 @@ smb2_adjust_credits(struct TCP_Server_In
return -EAGAIN;
}
server->credits += credits->value - new_val;
scredits = server->credits;
in_flight = server->in_flight;
-diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
-index 896147ba6660e..4cd5c33be2a1a 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
-@@ -4505,8 +4505,15 @@ smb2_readv_callback(struct mid_q_entry *mid)
+@@ -4505,8 +4505,15 @@ smb2_readv_callback(struct mid_q_entry *
struct TCP_Server_Info *server = rdata->server;
struct smb2_hdr *shdr =
(struct smb2_hdr *)rdata->iov[0].iov_base;
if (rdata->got_bytes) {
rqst.rq_iter = rdata->subreq.io_iter;
-@@ -4590,10 +4597,16 @@ smb2_readv_callback(struct mid_q_entry *mid)
+@@ -4590,10 +4597,16 @@ smb2_readv_callback(struct mid_q_entry *
if (rdata->subreq.start < rdata->subreq.rreq->i_size)
rdata->result = 0;
}
add_credits(server, &credits, 0);
}
-@@ -4650,7 +4663,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
+@@ -4650,7 +4663,7 @@ smb2_async_readv(struct cifs_io_subreque
min_t(int, server->max_credits -
server->credits, credit_request));
if (rc)
goto async_readv_out;
-@@ -4769,7 +4782,14 @@ smb2_writev_callback(struct mid_q_entry *mid)
+@@ -4769,7 +4782,14 @@ smb2_writev_callback(struct mid_q_entry
struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink);
struct TCP_Server_Info *server = wdata->server;
struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
ssize_t result = 0;
size_t written;
-@@ -4840,9 +4860,15 @@ smb2_writev_callback(struct mid_q_entry *mid)
+@@ -4840,9 +4860,15 @@ smb2_writev_callback(struct mid_q_entry
tcon->tid, tcon->ses->Suid,
wdata->subreq.start, wdata->subreq.len);
add_credits(server, &credits, 0);
}
-@@ -4972,7 +4998,7 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
+@@ -4972,7 +4998,7 @@ smb2_async_writev(struct cifs_io_subrequ
min_t(int, server->max_credits -
server->credits, credit_request));
if (rc)
goto async_writev_out;
-@@ -4997,6 +5023,12 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
+@@ -4997,6 +5023,12 @@ async_writev_out:
cifs_small_buf_release(req);
out:
if (rc) {
add_credits_and_wake_if(wdata->server, &wdata->credits, 0);
cifs_write_subrequest_terminated(wdata, rc, true);
}
-diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h
-index 36d47ce596317..36d5295c2a6f9 100644
--- a/fs/smb/client/trace.h
+++ b/fs/smb/client/trace.h
@@ -20,6 +20,22 @@
#undef EM
#undef E_
-@@ -71,6 +88,7 @@ enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte);
+@@ -71,6 +88,7 @@ enum smb3_tcon_ref_trace { smb3_tcon_ref
#define EM(a, b) TRACE_DEFINE_ENUM(a);
#define E_(a, b) TRACE_DEFINE_ENUM(a);
#undef EM
#undef E_
-diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
-index 012b9bd069952..adfe0d0587010 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -988,10 +988,10 @@ static void
add_credits(server, &credits, mid->optype);
---
-2.43.0
-