[RFC 3/3] ksmbd: use splice payloads for simple SMB2 READ

From: wang zhaolong

Date: Mon Jul 13 2026 - 03:15:36 EST


From: Wang Zhaolong <wangzhaolong@xxxxxxxxx>

Use the page-backed payload path for plain TCP SMB2 READ requests of at
least 64 KiB. Unsupported collection results fall back before response
ownership is committed; permission, locking and allocation failures retain
the existing error handling.

Keep signed, encrypted, compressed, compound and RDMA requests on the
buffered path because they require signing, response transforms, compound
response layout, or alternate transport handling. Streams, non-regular
files, O_DIRECT and DAX reads also remain on the buffered path.

Signed-off-by: Wang Zhaolong <wangzhaolong@xxxxxxxxx>
---
fs/smb/server/smb2pdu.c | 67 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)

diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index b73167785e87..a4a80ecc6d2f 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -7304,10 +7304,38 @@ static ssize_t smb2_read_rdma_channel(struct ksmbd_work *work,
return err;

return length;
}

+#define KSMBD_READ_PAYLOAD_MIN_IO_SIZE SZ_64K
+
+static bool smb2_read_payload_allowed(struct ksmbd_work *work,
+ struct smb2_read_req *req,
+ struct ksmbd_file *fp,
+ size_t length, bool is_rdma_channel)
+{
+ struct ksmbd_conn *conn = work->conn;
+ struct file *filp = fp->filp;
+
+ if (is_rdma_channel || length < KSMBD_READ_PAYLOAD_MIN_IO_SIZE ||
+ !conn->transport->ops->write_read_payload)
+ return false;
+ if (req->Flags & SMB2_READFLAG_REQUEST_COMPRESSED)
+ return false;
+ if (work->next_smb2_rcv_hdr_off || le32_to_cpu(req->hdr.NextCommand))
+ return false;
+ if (work->encrypted)
+ return false;
+ if (work->sess &&
+ (work->sess->sign || conn->ops->is_sign_req(work, SMB2_READ_HE)))
+ return false;
+ if (ksmbd_stream_fd(fp) || !S_ISREG(file_inode(filp)->i_mode) ||
+ (filp->f_flags & O_DIRECT) || IS_DAX(file_inode(filp)))
+ return false;
+ return true;
+}
+
/**
* smb2_read() - handler for smb2 read from file
* @work: smb work containing read command buffer
*
* Return: 0 on success, otherwise error
@@ -7316,10 +7344,11 @@ int smb2_read(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
struct smb2_read_req *req;
struct smb2_read_rsp *rsp;
struct ksmbd_file *fp = NULL;
+ struct ksmbd_read_payload *payload = NULL;
loff_t offset;
size_t length, mincount;
ssize_t nbytes = 0, remain_bytes = 0;
int err = 0;
bool is_rdma_channel = false, async_interim = false;
@@ -7418,10 +7447,48 @@ int smb2_read(struct ksmbd_work *work)
}

ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n",
fp->filp, offset, length);

+ if (smb2_read_payload_allowed(work, req, fp, length,
+ is_rdma_channel)) {
+ nbytes = ksmbd_vfs_read_payload(work, fp, length, &offset,
+ &payload);
+ if (nbytes == -EOPNOTSUPP)
+ goto read_fallback;
+ if (nbytes < 0) {
+ err = nbytes;
+ goto out;
+ }
+ if ((nbytes == 0 && length != 0) || nbytes < mincount) {
+ ksmbd_read_payload_release(payload);
+ rsp->hdr.Status = STATUS_END_OF_FILE;
+ smb2_set_err_rsp(work);
+ ksmbd_fd_put(work, fp);
+ return -ENODATA;
+ }
+
+ rsp->StructureSize = cpu_to_le16(17);
+ rsp->DataOffset = 80;
+ rsp->Reserved = 0;
+ rsp->DataLength = cpu_to_le32(nbytes);
+ rsp->DataRemaining = 0;
+ rsp->Flags = 0;
+ err = ksmbd_iov_pin_rsp_read_payload(work, rsp,
+ offsetof(struct smb2_read_rsp, Buffer),
+ payload);
+ if (err) {
+ ksmbd_read_payload_release(payload);
+ goto out;
+ }
+ ksmbd_fd_put(work, fp);
+ return 0;
+ }
+
+read_fallback:
+ payload = NULL;
+
aux_payload_buf = kvmalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP);
if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
}
--
2.47.3