[RFC PATCH 18/36] cifs: Use page frag allocator for Tx buffers
From: David Howells
Date: Tue May 19 2026 - 06:32:21 EST
Use a per-TCP connection page fragment allocator for transmission buffers
rather than using whole pages for messages less than half a page in size.
Currently, the data is copied into there, but we should allocate these in
future up front instead of kmalloc'd bits and avoid that copy too.
Note that these page fragments can be used with MSG_SPLICE_PAGES and thus
avoid a copy inside the TCP stack.
Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
cc: Steve French <sfrench@xxxxxxxxx>
cc: Paulo Alcantara <pc@xxxxxxxxxxxxx>
cc: Shyam Prasad N <sprasad@xxxxxxxxxxxxx>
cc: Tom Talpey <tom@xxxxxxxxxx>
cc: linux-cifs@xxxxxxxxxxxxxxx
cc: linux-fsdevel@xxxxxxxxxxxxxxx
---
fs/smb/client/cifsglob.h | 2 ++
fs/smb/client/connect.c | 3 +++
fs/smb/client/transport.c | 38 ++++++++++++++++++++++++++++++++------
3 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index f7c12d24e2ea..cd9bf550a144 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -662,6 +662,8 @@ struct TCP_Server_Info {
/* updates to tcpStatus protected by cifs_tcp_ses_lock */
enum statusEnum tcpStatus; /* what we think the status is */
char *hostname; /* hostname portion of UNC string */
+ struct page_frag_cache tx_alloc; /* Transmission buffer allocator */
+ struct mutex tx_alloc_lock; /* Lock for ->tx_alloc */
struct socket *ssocket;
struct sockaddr_storage dstaddr;
struct sockaddr_storage srcaddr; /* locally bind to this IP */
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index 4a3ebbd6d71b..1d6a18390a25 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1054,6 +1054,8 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
*/
}
+ page_frag_cache_drain(&server->tx_alloc);
+
put_net(cifs_net_ns(server));
kfree(server->leaf_fullpath);
kfree(server->hostname);
@@ -1694,6 +1696,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
mutex_init(&tcp_ses->reconnect_mutex);
+ mutex_init(&tcp_ses->tx_alloc_lock);
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index e43fce1099a9..98dca2524376 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -445,7 +445,8 @@ static size_t smb3_copy_data_iter(void *iter_from, size_t progress, size_t len,
*/
static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
int num_rqst, struct smb_rqst *rqst,
- struct iov_iter *iter, struct bvecq **_bq)
+ struct iov_iter *iter, struct bvecq **_bq,
+ unsigned int flags)
{
struct bvecq *bq;
size_t total_len = 0, offset = 0;
@@ -460,9 +461,31 @@ static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
total_len += size;
}
- bq = bvecq_alloc_buffer(total_len, GFP_NOFS);
- if (!bq)
- return -ENOMEM;
+ if (total_len <= PAGE_SIZE / 2) {
+ /* TODO: Choose algo-based alignment. */
+ unsigned int align = (flags & CIFS_TRANSFORM_REQ) ? 32 : 1;
+ size_t alen = (flags & CIFS_TRANSFORM_REQ) ?
+ round_up(total_len, align) : total_len;
+
+ bq = bvecq_alloc_chain(2, GFP_NOFS);
+ if (!bq)
+ return -ENOMEM;
+ mutex_lock(&server->tx_alloc_lock);
+ void *p = page_frag_alloc_align(&server->tx_alloc, alen,
+ GFP_NOFS, align);
+ mutex_unlock(&server->tx_alloc_lock);
+ if (!p) {
+ bvecq_put(bq);
+ return -ENOMEM;
+ }
+ bvec_set_virt(&bq->bv[1], p, total_len);
+ bq->nr_slots = 2;
+ bq->mem_type = BVECQ_MEM_PAGECACHE;
+ } else {
+ bq = bvecq_alloc_buffer2(total_len, 1, GFP_NOFS);
+ if (!bq)
+ return -ENOMEM;
+ }
iov_iter_bvec_queue(iter, ITER_DEST, bq, 1, 0, total_len);
@@ -495,6 +518,7 @@ static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
"offset=%zx total_len=%zx\n", offset, total_len)) {
goto error;
}
+
iov_iter_bvec_queue(iter, ITER_DEST, bq, 1, 0, total_len);
*_bq = bq;
return 0;
@@ -519,7 +543,7 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
return smb_EIO(smb_eio_trace_tx_need_transform);
}
- rc = smb_copy_data_into_buffer(server, num_rqst, rqst, &iter, &bq);
+ rc = smb_copy_data_into_buffer(server, num_rqst, rqst, &iter, &bq, flags);
if (rc)
return rc;
content_len = iov_iter_count(&iter);
@@ -537,7 +561,9 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
/* TODO: Allocate netmem here */
rc = -ENOMEM;
- hdr_blob = (void *)__get_free_page(GFP_NOFS);
+ mutex_lock(&server->tx_alloc_lock);
+ hdr_blob = page_frag_alloc(&server->tx_alloc, hdr_len, GFP_NOFS);
+ mutex_unlock(&server->tx_alloc_lock);
if (!hdr_blob)
goto error;
bvec_set_virt(&bq->bv[0], hdr_blob, hdr_len);