From nobody Sat Jul 25 21:59:23 2026 Received: from va-2-37.ptr.blmpb.com (va-2-37.ptr.blmpb.com [209.127.231.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ADB3036E467 for ; Mon, 13 Jul 2026 07:12:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.127.231.37 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783926730; cv=none; b=nyGstcr5d/WJZ+TkP0nSPsbg2k+9BFfX5vaHp7xoyEiwXIZoZxGrBRh6X6kICyD+82Eo16mbZqNRf6O1sWNzvk2pm0m6dlEEz7aFzTtbAlBYNxrgzX3/8Pc2hDkvSTpCwjG8YvfWCpV3Cn/u4GSDcp7FDoz/cn0t5YF6XaXA+3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783926730; c=relaxed/simple; bh=lcT477c2XzOESg10n6ywFtHr8k7NG3Bt2tdCLso1iK4=; h=To:Cc:Subject:Date:From:Content-Type:Message-Id:Mime-Version: References:In-Reply-To; b=RjHtMnRn9f5yF6oExkg6c9WUSUsL4l+R1jaGK81JVpFkBdDVQRaWwRYzW+ctUSEIR1Cz+tfj3Bg9WTnLoBkMnFFyKsci0LLF+WA/3ZLfFDTLyW16wT16KUdHT2BSZDHx/OMnItC0rHpWjAzneGfSGXfzG0omcFxNmg6ocsroPWY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fnnas.com; spf=none smtp.mailfrom=fnnas.com; dkim=pass (2048-bit key) header.d=fnnas-com.20200927.dkim.feishu.cn header.i=@fnnas-com.20200927.dkim.feishu.cn header.b=LFRqK08+; arc=none smtp.client-ip=209.127.231.37 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fnnas.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=fnnas.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fnnas-com.20200927.dkim.feishu.cn header.i=@fnnas-com.20200927.dkim.feishu.cn header.b="LFRqK08+" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=s1; d=fnnas-com.20200927.dkim.feishu.cn; t=1783926722; h=from:subject:mime-version:from:date:message-id:subject:to:cc: reply-to:content-type:mime-version:in-reply-to:message-id; bh=a59FzSWI0PTjOIY9Ww6ZlQcf4iDu4ztOfrph6pTbz6g=; b=LFRqK08+IfTg/HUkP18NL/Rl6qQoyBV/3D502MPQmN2BeDI/nuhlzihkzMuHTiechJ+ocV imT5/r8o84ALehc54Ej/mjw6q3t5QUQvKg9LHyfeAFJmE/2H6h977ofMzuzDpIPM7Nkj8+ +0ECTTohsHHtWGXYpLoX7+BQrhDbs4uRPa/wO86x6rxU2FrRB4Kh/XwRvWcDXK8dkA7fgx mOHDr9obA55CY5lOXmrKlsC7gcvggnS56vK4KypDm0Csdj3F189bzFCn2ilf5jv+6t6O8X KBi1pfc9KlCjGrwEtQw/Z933yeQp2FoCPUqMdAJ8nSn+mOG1AY8Sf6aRNr6X8w== To: Cc: , , , , , "Wang Zhaolong" Subject: [RFC 1/3] ksmbd: add read payload infrastructure Date: Mon, 13 Jul 2026 15:11:38 +0800 X-Lms-Return-Path: From: "wang zhaolong" Content-Transfer-Encoding: quoted-printable Received: from localhost.localdomain ([183.34.174.151]) by smtp.feishu.cn with ESMTPS; Mon, 13 Jul 2026 15:11:59 +0800 Message-Id: <20260713071141.3909087-2-wangzhaolong@fnnas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Mailer: git-send-email 2.47.3 References: <20260713071141.3909087-1-wangzhaolong@fnnas.com> In-Reply-To: <20260713071141.3909087-1-wangzhaolong@fnnas.com> X-Original-From: wang zhaolong Content-Type: text/plain; charset="utf-8" From: Wang Zhaolong ksmbd currently copies SMB READ data into an allocated auxiliary buffer before sending the response. A splice-based path needs to keep page-backed file data separate from the SMB header while still accounting for it in the RFC1002 length. Add a work-owned READ payload made of bio_vecs and an ownership-transfer helper for response pinning. Add a transport operation and a TCP implementation that sends the header followed by the payload with MSG_SPLICE_PAGES while the connection lock prevents interleaving. Handle positive short sends and shut down the socket if a partially emitted PDU fails. The existing READ path remains unchanged until the payload collector is wired up. Signed-off-by: Wang Zhaolong --- fs/smb/server/connection.c | 25 ++++++++++++--- fs/smb/server/connection.h | 4 +++ fs/smb/server/ksmbd_work.c | 45 +++++++++++++++++++++++++++ fs/smb/server/ksmbd_work.h | 12 ++++++++ fs/smb/server/transport_tcp.c | 58 +++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 5 deletions(-) diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 9e8fdb39e5a2..97ad736bf87a 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -351,10 +351,12 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *c= urr_conn, u64 sess_id) } =20 int ksmbd_conn_write(struct ksmbd_work *work) { struct ksmbd_conn *conn =3D work->conn; + unsigned int head_len; + unsigned int pdu_len; int sent; =20 if (!work->response_buf) { pr_err("NULL response header\n"); return -EINVAL; @@ -364,16 +366,29 @@ int ksmbd_conn_write(struct ksmbd_work *work) return 0; =20 if (!work->iov_idx) return -EINVAL; =20 + pdu_len =3D get_rfc1002_len(work->iov[0].iov_base) + 4; ksmbd_conn_lock(conn); - sent =3D conn->transport->ops->writev(conn->transport, work->iov, - work->iov_cnt, - get_rfc1002_len(work->iov[0].iov_base) + 4, - work->need_invalidate_rkey, - work->remote_key); + if (work->read_payload) { + if (WARN_ON_ONCE(work->read_payload->len > pdu_len)) { + sent =3D -EINVAL; + } else if (WARN_ON_ONCE(!conn->transport->ops->write_read_payload)) { + sent =3D -EOPNOTSUPP; + } else { + head_len =3D pdu_len - work->read_payload->len; + sent =3D conn->transport->ops->write_read_payload(conn->transport, + work->iov, work->iov_cnt, head_len, + work->read_payload); + } + } else { + sent =3D conn->transport->ops->writev(conn->transport, work->iov, + work->iov_cnt, pdu_len, + work->need_invalidate_rkey, + work->remote_key); + } ksmbd_conn_unlock(conn); =20 if (sent < 0) { pr_err("Failed to send message: %d\n", sent); return sent; diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index ec75633b7da0..a44e382138a7 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -138,10 +138,14 @@ struct ksmbd_transport_ops { int (*read)(struct ksmbd_transport *t, char *buf, unsigned int size, int max_retries); int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov, int size, bool need_invalidate_rkey, unsigned int remote_key); + int (*write_read_payload)(struct ksmbd_transport *t, + struct kvec *iovs, int niov, + unsigned int head_len, + struct ksmbd_read_payload *payload); int (*rdma_read)(struct ksmbd_transport *t, void *buf, unsigned int len, struct smbdirect_buffer_descriptor_v1 *desc, unsigned int desc_len); int (*rdma_write)(struct ksmbd_transport *t, diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c index e2c2f45264be..05361f6aa8fb 100644 --- a/fs/smb/server/ksmbd_work.c +++ b/fs/smb/server/ksmbd_work.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2019 Samsung Electronics Co., Ltd. */ =20 +#include #include #include #include #include =20 @@ -14,10 +15,23 @@ #include "mgmt/ksmbd_ida.h" =20 static struct kmem_cache *work_cache; static struct workqueue_struct *ksmbd_wq; =20 +void ksmbd_read_payload_release(struct ksmbd_read_payload *payload) +{ + unsigned int i; + + if (!payload) + return; + + for (i =3D 0; i < payload->nr_bvecs; i++) + put_page(payload->bvec[i].bv_page); + kvfree(payload->bvec); + kfree(payload); +} + static int ksmbd_reserve_iov(struct ksmbd_work *work, int need_iov_cnt) { struct kvec *new; int new_alloc_cnt =3D work->iov_alloc_cnt; =20 @@ -74,10 +88,11 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) { kvfree(ar->buf); list_del(&ar->entry); kfree(ar); } + ksmbd_read_payload_release(work->read_payload); =20 kfree(work->tr_buf); kvfree(work->compress_buf); kvfree(work->request_buf); if (work->iov !=3D work->iov_inline) @@ -179,10 +194,40 @@ int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, v= oid *ib, int len, void *aux_buf, unsigned int aux_size) { return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size); } =20 +/** + * ksmbd_iov_pin_rsp_read_payload() - attach a page-backed READ payload + * @work: smb work that owns the response + * @ib: response header + * @len: response header length + * @payload: payload whose ownership transfers to @work only on success + * + * Return: 0 on success, otherwise a negative error with ownership retaine= d by + * the caller. + */ +int ksmbd_iov_pin_rsp_read_payload(struct ksmbd_work *work, void *ib, int = len, + struct ksmbd_read_payload *payload) +{ + int ret; + + if (!payload || !payload->bvec || !payload->nr_bvecs || !payload->len) + return -EINVAL; + if (WARN_ON_ONCE(work->read_payload) || + WARN_ON_ONCE(payload->len > INT_MAX)) + return -EINVAL; + + ret =3D __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0); + if (ret) + return ret; + + inc_rfc1001_len(work->iov[0].iov_base, payload->len); + work->read_payload =3D payload; + return 0; +} + int allocate_interim_rsp_buf(struct ksmbd_work *work) { work->response_buf =3D kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, KSMBD_DEFAULT_= GFP); if (!work->response_buf) return -ENOMEM; diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index 88104f0cf363..c0e1370e2599 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -10,10 +10,11 @@ #include =20 struct ksmbd_conn; struct ksmbd_session; struct ksmbd_tree_connect; +struct bio_vec; =20 #define KSMBD_WORK_INLINE_IOVS 4 =20 enum { KSMBD_WORK_ACTIVE =3D 0, @@ -24,10 +25,17 @@ enum { struct aux_read { void *buf; struct list_head entry; }; =20 +struct ksmbd_read_payload { + struct bio_vec *bvec; + unsigned int nr_bvecs; + unsigned int nr_alloc; + size_t len; +}; + /* one of these for every pending CIFS request at the connection */ struct ksmbd_work { /* Server corresponding to this mid */ struct ksmbd_conn *conn; struct ksmbd_session *sess; @@ -37,10 +45,11 @@ struct ksmbd_work { void *request_buf; /* Response buffer */ void *response_buf; =20 struct list_head aux_read_list; + struct ksmbd_read_payload *read_payload; =20 struct kvec *iov; int iov_alloc_cnt; int iov_cnt; int iov_idx; @@ -142,8 +151,11 @@ int ksmbd_work_pool_init(void); int ksmbd_workqueue_init(void); void ksmbd_workqueue_destroy(void); bool ksmbd_queue_work(struct ksmbd_work *work); int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len, void *aux_buf, unsigned int aux_size); +int ksmbd_iov_pin_rsp_read_payload(struct ksmbd_work *work, void *ib, int = len, + struct ksmbd_read_payload *payload); +void ksmbd_read_payload_release(struct ksmbd_read_payload *payload); int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len); int allocate_interim_rsp_buf(struct ksmbd_work *work); #endif /* __KSMBD_WORK_H__ */ diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index 13b711ea575d..3095898d74d1 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -425,10 +425,67 @@ static int ksmbd_tcp_writev(struct ksmbd_transport *t= , struct kvec *iov, struct msghdr smb_msg =3D {.msg_flags =3D MSG_NOSIGNAL}; =20 return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size); } =20 +static int ksmbd_tcp_send_iter(struct socket *sock, struct iov_iter *iter, + unsigned int flags, bool *sent_any) +{ + struct msghdr msg =3D { .msg_flags =3D MSG_NOSIGNAL | flags }; + int sent =3D 0; + + while (iov_iter_count(iter)) { + int ret; + + msg.msg_iter =3D *iter; + ret =3D sock_sendmsg(sock, &msg); + if (ret <=3D 0) + return ret ?: -EPIPE; + *sent_any =3D true; + iov_iter_advance(iter, ret); + sent +=3D ret; + } + return sent; +} + +static int ksmbd_tcp_write_read_payload(struct ksmbd_transport *t, + struct kvec *iov, int nvecs, + unsigned int head_len, + struct ksmbd_read_payload *payload) +{ + struct tcp_transport *tcp =3D TCP_TRANS(t); + struct iov_iter iter; + bool sent_any =3D false; + unsigned int i; + int ret; + + for (i =3D 0; i < payload->nr_bvecs; i++) { + if (!sendpages_ok(payload->bvec[i].bv_page, + payload->bvec[i].bv_len, + payload->bvec[i].bv_offset)) + return -EOPNOTSUPP; + } + + iov_iter_kvec(&iter, ITER_SOURCE, iov, nvecs, head_len); + ret =3D ksmbd_tcp_send_iter(tcp->sock, &iter, MSG_MORE, &sent_any); + if (ret < 0) + goto out; + iov_iter_bvec(&iter, ITER_SOURCE, payload->bvec, payload->nr_bvecs, + payload->len); + ret =3D ksmbd_tcp_send_iter(tcp->sock, &iter, MSG_SPLICE_PAGES, + &sent_any); + if (ret >=3D 0) + return head_len + ret; +out: + /* A partially emitted RFC1002 PDU makes this TCP stream unusable. */ + if (sent_any) { + ksmbd_conn_set_exiting(t->conn); + kernel_sock_shutdown(tcp->sock, SHUT_RDWR); + } + return ret; +} + static void ksmbd_tcp_disconnect(struct ksmbd_transport *t) { free_transport(TCP_TRANS(t)); if (server_conf.max_connections) atomic_dec(&active_num_conn); @@ -677,8 +734,9 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_li= st_sz) } =20 static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops =3D { .read =3D ksmbd_tcp_read, .writev =3D ksmbd_tcp_writev, + .write_read_payload =3D ksmbd_tcp_write_read_payload, .disconnect =3D ksmbd_tcp_disconnect, .free_transport =3D ksmbd_tcp_free_transport, }; --=20 2.47.3 From nobody Sat Jul 25 21:59:23 2026 Received: from va-2-28.ptr.blmpb.com (va-2-28.ptr.blmpb.com [209.127.231.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97E8C36EA8D for ; Mon, 13 Jul 2026 07:12:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.127.231.28 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783926734; cv=none; b=RGXRg+q08+AGEI/ODVdKuiziS8E8CU4j0pVv7AHqkQsd4QbGiT8p/vyVWa2DqhI2JqyjC0eNiHok3dd4JscjE3VdIAviZoI5Qya6kvKl61ZOwCAPZymxxtfHx3i3TVcEy19uYBw/6eNdVDoOk/huUjymE90PPFXHn9q3MLS/mos= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783926734; c=relaxed/simple; bh=db3JFSG7OjLdGI3YCvK0PZnnUeX5Sn2l6rYLl0OdV7k=; h=Message-Id:Mime-Version:To:Cc:Date:Subject:In-Reply-To: Content-Type:From:References; b=C0L+h3rC4i3w6+W/1z/z5OC6Kc9wr+Vl3pM/bJtxPDxeEkb29Pdy7iNiN7Ojx07QGZsrq4o44SQ7wZRENoEROAowqLqhseFRaFJar7lpEaNojUGyPCEJsE+ws4ctelxaVopw/lsFDqazlyM3ezNjYyq1TUb+DHwzvoTH9EV48Vs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fnnas.com; spf=none smtp.mailfrom=fnnas.com; dkim=pass (2048-bit key) header.d=fnnas-com.20200927.dkim.feishu.cn header.i=@fnnas-com.20200927.dkim.feishu.cn header.b=XCSyEf+v; arc=none smtp.client-ip=209.127.231.28 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fnnas.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=fnnas.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fnnas-com.20200927.dkim.feishu.cn header.i=@fnnas-com.20200927.dkim.feishu.cn header.b="XCSyEf+v" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=s1; d=fnnas-com.20200927.dkim.feishu.cn; t=1783926726; h=from:subject:mime-version:from:date:message-id:subject:to:cc: reply-to:content-type:mime-version:in-reply-to:message-id; bh=0rEphVK5/6ke2PIYY6Uc03BOGm7dYzNeTT3fi75D3ak=; b=XCSyEf+vb5+a/09RQc5I48Odg0xTaWBKMNTm95meYIjnS1mdg2PXDPeVhOnum9PxYUrOAa QA1SpWNhNlMyz8U1X1iRupXU/zyG1Ma9NbqXryoEfKFevIXYQOPcxDMjtTW0UGaot+pKWr zMg/jFklEUgT+hVWoPo/vCIcJ8GOmLECmTIelwfOcIrx5jUStq33SVUFUbseHtFanK+aFk 91GtI0ekuYkc/welm3acoikwMXUu8x6pt5auQMNL5Rw2CntxEirzQHB0Wt7gugaUr45xQ+ gFkgEMB1RwXMYrn6bkQwXOWy0Eb5ASgOvLYnBRwPar4/Jx57LHAAc1nN+ZXlGw== Message-Id: <20260713071141.3909087-3-wangzhaolong@fnnas.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Lms-Return-Path: X-Mailer: git-send-email 2.47.3 To: Cc: , , , , , "Wang Zhaolong" Date: Mon, 13 Jul 2026 15:11:39 +0800 X-Original-From: wang zhaolong Received: from localhost.localdomain ([183.34.174.151]) by smtp.feishu.cn with ESMTPS; Mon, 13 Jul 2026 15:12:03 +0800 Subject: [RFC 2/3] ksmbd: add splice-based read payload helper Content-Transfer-Encoding: quoted-printable In-Reply-To: <20260713071141.3909087-1-wangzhaolong@fnnas.com> From: "wang zhaolong" References: <20260713071141.3909087-1-wangzhaolong@fnnas.com> Content-Type: text/plain; charset="utf-8" From: Wang Zhaolong Add ksmbd_vfs_read_payload() to collect page-cache backed splice buffers into a bio_vec payload. The actor takes a reference to every page so the payload remains valid after the internal splice pipe is drained. Preserve the buffered READ path's directory, zero-length, access and byte-range lock semantics. Streams, non-regular files, O_DIRECT, DAX and files without splice_read support return -EOPNOTSUPP with the offset unchanged, allowing the caller to retry through kernel_read(). A short read before EOF is handled the same way to preserve the existing READ data path. Signed-off-by: Wang Zhaolong --- fs/smb/server/vfs.c | 195 ++++++++++++++++++++++++++++++++++++++------ fs/smb/server/vfs.h | 4 + 2 files changed, 176 insertions(+), 23 deletions(-) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index d0a0ad15d803..3a3f1b8e2d33 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -17,10 +17,13 @@ #include #include #include #include #include +#include +#include +#include #include #include =20 #include "glob.h" #include "oplock.h" @@ -326,10 +329,33 @@ static int check_lock_range(struct file *filp, loff_t= start, loff_t end, out: spin_unlock(&ctx->flc_lock); return error; } =20 +static int ksmbd_vfs_check_read_access(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + if (work->conn->connection_type && + !(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { + pr_err("no right to read(%pD)\n", fp->filp); + return -EACCES; + } + return 0; +} + +static int ksmbd_vfs_check_read_range(struct ksmbd_work *work, + struct ksmbd_file *fp, loff_t pos, + size_t count) +{ + if (!work->tcon->posix_extensions && + check_lock_range(fp->filp, pos, pos + count - 1, READ)) { + pr_err("unable to read due to lock\n"); + return -EAGAIN; + } + return 0; +} + /** * ksmbd_vfs_read() - vfs helper for smb file read * @work: smb work * @fp: ksmbd file pointer * @count: read byte count @@ -340,47 +366,170 @@ static int check_lock_range(struct file *filp, loff_= t start, loff_t end, */ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t = count, loff_t *pos, char *rbuf) { struct file *filp =3D fp->filp; - ssize_t nbytes =3D 0; - struct inode *inode =3D file_inode(filp); + ssize_t nbytes; + int ret; =20 - if (S_ISDIR(inode->i_mode)) + if (S_ISDIR(file_inode(filp)->i_mode)) return -EISDIR; - if (unlikely(count =3D=3D 0)) return 0; - - if (work->conn->connection_type) { - if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { - pr_err("no right to read(%pD)\n", fp->filp); - return -EACCES; - } - } - + ret =3D ksmbd_vfs_check_read_access(work, fp); + if (ret) + return ret; if (ksmbd_stream_fd(fp)) return ksmbd_vfs_stream_read(fp, rbuf, pos, count); - - if (!work->tcon->posix_extensions) { - int ret; - - ret =3D check_lock_range(filp, *pos, *pos + count - 1, READ); - if (ret) { - pr_err("unable to read due to lock\n"); - return -EAGAIN; - } - } + ret =3D ksmbd_vfs_check_read_range(work, fp, *pos, count); + if (ret) + return ret; =20 nbytes =3D kernel_read(filp, rbuf, count, pos); if (nbytes < 0) { pr_err("smb read failed, err =3D %zd\n", nbytes); return nbytes; } + filp->f_pos =3D *pos; + ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, nbytes); + return nbytes; +} + +static int ksmbd_read_payload_reserve(struct ksmbd_read_payload *payload, + unsigned int nr_bvecs) +{ + struct bio_vec *bvec; + unsigned int needed, nr_alloc; =20 + if (check_add_overflow(payload->nr_bvecs, nr_bvecs, &needed)) + return -EOVERFLOW; + if (payload->nr_alloc >=3D needed) + return 0; + nr_alloc =3D payload->nr_alloc ?: 16; + while (nr_alloc < needed) { + if (nr_alloc > UINT_MAX / 2) { + nr_alloc =3D needed; + break; + } + nr_alloc *=3D 2; + } + bvec =3D kvrealloc(payload->bvec, + array_size(nr_alloc, sizeof(*payload->bvec)), + KSMBD_DEFAULT_GFP | __GFP_ZERO); + if (!bvec) + return -ENOMEM; + payload->bvec =3D bvec; + payload->nr_alloc =3D nr_alloc; + return 0; +} + +static int ksmbd_read_payload_actor(struct pipe_inode_info *pipe, + struct pipe_buffer *buf, + struct splice_desc *sd) +{ + struct ksmbd_read_payload *payload =3D sd->u.data; + unsigned int offset =3D buf->offset, len =3D sd->len; + size_t payload_len; + unsigned int nr_bvecs; + int ret; + + nr_bvecs =3D DIV_ROUND_UP(offset_in_page(offset) + len, PAGE_SIZE); + ret =3D ksmbd_read_payload_reserve(payload, nr_bvecs); + if (ret) + return ret; + if (check_add_overflow(payload->len, (size_t)len, &payload_len)) + return -EOVERFLOW; + while (len) { + struct page *page =3D buf->page + offset / PAGE_SIZE; + unsigned int page_offset =3D offset_in_page(offset); + unsigned int bytes =3D min_t(unsigned int, len, + PAGE_SIZE - page_offset); + + if (!sendpage_ok(page)) + return -EOPNOTSUPP; + get_page(page); + bvec_set_page(&payload->bvec[payload->nr_bvecs++], page, + bytes, page_offset); + offset +=3D bytes; + len -=3D bytes; + } + payload->len =3D payload_len; + return sd->len; +} + +static int ksmbd_direct_splice_actor(struct pipe_inode_info *pipe, + struct splice_desc *sd) +{ + return __splice_from_pipe(pipe, sd, ksmbd_read_payload_actor); +} + +/** + * ksmbd_vfs_read_payload() - collect file pages for an SMB READ response + * @work: smb work + * @fp: ksmbd file pointer + * @count: read byte count + * @pos: file position, advanced only on success + * @payload: payload returned on success, otherwise set to NULL + * + * Return: number of bytes read, or -EOPNOTSUPP with @pos unchanged when t= he + * caller may safely retry through ksmbd_vfs_read(). + */ +int ksmbd_vfs_read_payload(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos, + struct ksmbd_read_payload **payload) +{ + struct file *filp =3D fp->filp; + struct ksmbd_read_payload *read_payload; + loff_t start =3D *pos; + struct splice_desc sd; + ssize_t nbytes; + int ret; + + *payload =3D NULL; + if (S_ISDIR(file_inode(filp)->i_mode)) + return -EISDIR; + if (unlikely(count =3D=3D 0)) + return 0; + ret =3D ksmbd_vfs_check_read_access(work, fp); + if (ret) + return ret; + if (ksmbd_stream_fd(fp) || !S_ISREG(file_inode(filp)->i_mode) || + (filp->f_flags & O_DIRECT) || IS_DAX(file_inode(filp)) || + !(filp->f_mode & FMODE_LSEEK) || !filp->f_op->splice_read) + return -EOPNOTSUPP; + ret =3D ksmbd_vfs_check_read_range(work, fp, start, count); + if (ret) + return ret; + + read_payload =3D kzalloc_obj(struct ksmbd_read_payload, + KSMBD_DEFAULT_GFP); + if (!read_payload) + return -ENOMEM; + nbytes =3D rw_verify_area(READ, filp, pos, count); + if (nbytes) + goto out_free; + sd =3D (struct splice_desc) { + .total_len =3D count, + .pos =3D start, + .u.data =3D read_payload, + }; + nbytes =3D splice_direct_to_actor(filp, &sd, ksmbd_direct_splice_actor); + if (nbytes <=3D 0) + goto out_free; + if (nbytes < count && start + nbytes < i_size_read(file_inode(filp))) { + nbytes =3D -EOPNOTSUPP; + goto out_free; + } + *pos =3D start + nbytes; filp->f_pos =3D *pos; - ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, (s64)nbytes); + ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, nbytes); + *payload =3D read_payload; + return nbytes; + +out_free: + *pos =3D start; + ksmbd_read_payload_release(read_payload); return nbytes; } =20 static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t= *pos, size_t count) diff --git a/fs/smb/server/vfs.h b/fs/smb/server/vfs.h index 7b3d2f4fd985..5afee75c4a3b 100644 --- a/fs/smb/server/vfs.h +++ b/fs/smb/server/vfs.h @@ -34,10 +34,11 @@ enum { #define CREATE_OPTION_SPECIAL 0x20000000 =20 struct ksmbd_work; struct ksmbd_file; struct ksmbd_conn; +struct ksmbd_read_payload; =20 struct ksmbd_dir_info { const char *name; char *wptr; char *rptr; @@ -77,10 +78,13 @@ void ksmbd_vfs_query_maximal_access(struct mnt_idmap *i= dmap, struct dentry *dentry, __le32 *daccess); int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mo= de); int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mod= e); int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t = count, loff_t *pos, char *rbuf); +int ksmbd_vfs_read_payload(struct ksmbd_work *work, struct ksmbd_file *fp, + size_t count, loff_t *pos, + struct ksmbd_read_payload **payload); int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, char *buf, size_t count, loff_t *pos, bool sync, ssize_t *written); int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id); int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path= ); --=20 2.47.3 From nobody Sat Jul 25 21:59:23 2026 Received: from va-2-36.ptr.blmpb.com (va-2-36.ptr.blmpb.com [209.127.231.36]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 65968390C8C for ; Mon, 13 Jul 2026 07:12:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.127.231.36 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783926743; cv=none; b=OyUwHUVnpzMLjQm2QPmjAVRbdu7XkLMp5UgD+ev0WTHCAgR5kUdAcHz3+XsUOeF0NDCytw0YE5ABKz6V2oLS0KF8cEtv89teD1VZaEijululpBoXMOyBP6+/OpKPHiCWlQuX8yV5dexLtNYjn2gZNy5oQHkLiJPTf/MkzRys6v4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783926743; c=relaxed/simple; bh=2nFA8ksd9tCNvD3AMegQgqMCJDUorA1CPovrmmOLUsU=; h=From:Date:Message-Id:To:Cc:Mime-Version:In-Reply-To:References: Content-Type:Subject; b=nVrbX16Un2L8gtl7umobLXiIe9h1sCuMxgorraLBZPXD5ggnb4aS4Tg3UMFX2/DdJcyfuQS1ZkbsyrTPcNUiR95W1g1zMSpJ8RtXk6zTaZg1cEe5TOLiB/zGFzqEUbn9XUprvvc7naDBXTHEjcseFweQJDVu94JQhpcL3O3/hP0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fnnas.com; spf=none smtp.mailfrom=fnnas.com; dkim=pass (2048-bit key) header.d=fnnas-com.20200927.dkim.feishu.cn header.i=@fnnas-com.20200927.dkim.feishu.cn header.b=3OidMsm4; arc=none smtp.client-ip=209.127.231.36 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=fnnas.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=fnnas.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=fnnas-com.20200927.dkim.feishu.cn header.i=@fnnas-com.20200927.dkim.feishu.cn header.b="3OidMsm4" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=s1; d=fnnas-com.20200927.dkim.feishu.cn; t=1783926732; h=from:subject:mime-version:from:date:message-id:subject:to:cc: reply-to:content-type:mime-version:in-reply-to:message-id; bh=vTs7L++cZmkN0GWVBWEMa8mxMyPGIfkDisZ9cd21MDs=; b=3OidMsm4CN0dsnf3y766rv1MDKBtm2raaPZ1/6ZG2W/U6DMHLFsxKwvqN7LH9OLbM0aCnM HCisENl/rEUB9x5G0PqmG97IsOgRpe9Tx4M5v5FKpzfQziiu2yApmaGjsKe5UeSls9CEcE +3M0gpJPuaHLAjVAeXNwPGP1gOcXjHzAblr7dK/XOK1iPUQKG4iiYMgiXHcVZwgYD7ruzs R1vBa+9hi+NUdbdarG6oLKJgfN8AWC8niyd/WocmMY/t+R3yF9vXQj9wgCQyqXnrZIWNzC 2YJb0UE7jL/yQJPSWGYa5NM77Xj32Ef8O0m2DcoHfzodgEtlKV5bAEixVLRbSw== From: "wang zhaolong" Date: Mon, 13 Jul 2026 15:11:40 +0800 Message-Id: <20260713071141.3909087-4-wangzhaolong@fnnas.com> To: Received: from localhost.localdomain ([183.34.174.151]) by smtp.feishu.cn with ESMTPS; Mon, 13 Jul 2026 15:12:09 +0800 X-Lms-Return-Path: X-Mailer: git-send-email 2.47.3 Cc: , , , , , "Wang Zhaolong" Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable In-Reply-To: <20260713071141.3909087-1-wangzhaolong@fnnas.com> X-Original-From: wang zhaolong References: <20260713071141.3909087-1-wangzhaolong@fnnas.com> Subject: [RFC 3/3] ksmbd: use splice payloads for simple SMB2 READ Content-Type: text/plain; charset="utf-8" From: Wang Zhaolong 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 --- 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; =20 return length; } =20 +#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 =3D work->conn; + struct file *filp =3D 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 =3D work->conn; struct smb2_read_req *req; struct smb2_read_rsp *rsp; struct ksmbd_file *fp =3D NULL; + struct ksmbd_read_payload *payload =3D NULL; loff_t offset; size_t length, mincount; ssize_t nbytes =3D 0, remain_bytes =3D 0; int err =3D 0; bool is_rdma_channel =3D false, async_interim =3D false; @@ -7418,10 +7447,48 @@ int smb2_read(struct ksmbd_work *work) } =20 ksmbd_debug(SMB, "filename %pD, offset %lld, len %zu\n", fp->filp, offset, length); =20 + if (smb2_read_payload_allowed(work, req, fp, length, + is_rdma_channel)) { + nbytes =3D ksmbd_vfs_read_payload(work, fp, length, &offset, + &payload); + if (nbytes =3D=3D -EOPNOTSUPP) + goto read_fallback; + if (nbytes < 0) { + err =3D nbytes; + goto out; + } + if ((nbytes =3D=3D 0 && length !=3D 0) || nbytes < mincount) { + ksmbd_read_payload_release(payload); + rsp->hdr.Status =3D STATUS_END_OF_FILE; + smb2_set_err_rsp(work); + ksmbd_fd_put(work, fp); + return -ENODATA; + } + + rsp->StructureSize =3D cpu_to_le16(17); + rsp->DataOffset =3D 80; + rsp->Reserved =3D 0; + rsp->DataLength =3D cpu_to_le32(nbytes); + rsp->DataRemaining =3D 0; + rsp->Flags =3D 0; + err =3D 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 =3D NULL; + aux_payload_buf =3D kvmalloc(ALIGN(length, 8), KSMBD_DEFAULT_GFP); if (!aux_payload_buf) { err =3D -ENOMEM; goto out; } --=20 2.47.3