From: Liu Kui <kui.liu@virtuozzo.com>
To: Konstantin Khorenko <khorenko@virtuozzo.com>
Cc: azaitsev@virtuozzo.com, devel@openvz.org,
Liu Kui <kui.liu@virtuozzo.com>
Subject: [Devel] [PATCH hci-8.1 2/3] fs/fuse kio: refactor transmit path in pcs_sockio_send()
Date: Wed, 30 Sep 2026 15:34:32 +0800 [thread overview]
Message-ID: <20260930-sockio-refactor-8-1-v1-2-806348b9eb26@virtuozzo.com> (raw)
In-Reply-To: <20260930-sockio-refactor-8-1-v1-0-806348b9eb26@virtuozzo.com>
The transmit path sends one iov_iter segment per sock_sendmsg() call,
and the bvec builder produces one bio_vec per 4k page, so a 1M data
chunk costs up to 256 sendmsg calls where the socket could take the
whole iov_iter at once.
Replace do_send_one_seg() with do_sockio_sendmsg(), which submits the
entire iov_iter in a single sock_sendmsg() call (with MSG_SPLICE_PAGES
when all pages qualify), reducing pcs_sockio_send() to pure queue
management.
Also clear PCS_MSG_BUSY in the transport whenever IO on the msg ends,
like the rdma transport does, instead of relying on krpc to mop it up.
https://virtuozzo.atlassian.net/browse/VSTOR-142167
Signed-off-by: Liu Kui <kui.liu@virtuozzo.com>
---
fs/fuse/kio/pcs/pcs_sock_io.c | 207 ++++++++++++++++++++++--------------------
1 file changed, 109 insertions(+), 98 deletions(-)
diff --git a/fs/fuse/kio/pcs/pcs_sock_io.c b/fs/fuse/kio/pcs/pcs_sock_io.c
index fa26105b2185..828c8a98cabf 100644
--- a/fs/fuse/kio/pcs/pcs_sock_io.c
+++ b/fs/fuse/kio/pcs/pcs_sock_io.c
@@ -139,54 +139,6 @@ static bool pcs_should_fail_sock_io(void)
}
#endif
-static int do_send_one_seg(struct socket *sock, struct iov_iter *it, size_t left, int has_more)
-{
- int ret = -EIO;
- size_t size = iov_iter_single_seg_count(it);
- bool more = (size < left);
- int flags = (MSG_DONTWAIT | MSG_NOSIGNAL) | ((more || has_more) ? MSG_MORE : 0);
- struct msghdr msg = { .msg_flags = flags };
-
- if (unlikely(pcs_use_eor) && !more)
- flags |= MSG_EOR;
-
- DTRACE("sock(%px) len:%ld, more:%d\n", sock, iov_iter_count(it), more);
-
- if (unlikely(!size))
- return 0;
-
- if (pcs_should_fail_sock_io())
- goto out;
-
- if (iov_iter_is_kvec(it)) {
- struct kvec kv;
-
- iov_iter_get_kvec(it, &kv);
- BUG_ON(kv.iov_len != size);
-
- iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &kv, 1, size);
- } else if (iov_iter_is_bvec(it)) {
- struct bio_vec bv;
-
- /* iov_iter_iovec() for ITER_BVEC */
- bvec_set_page(&bv, it->bvec->bv_page,
- min(size, it->bvec->bv_len - it->iov_offset),
- it->bvec->bv_offset + it->iov_offset);
-
- if (sendpage_ok(bv.bv_page))
- msg.msg_flags |= MSG_SPLICE_PAGES;
- else
- msg.msg_flags &= ~MSG_SPLICE_PAGES;
-
- iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bv, 1, bv.bv_len);
- }
-
- ret = sock_sendmsg(sock, &msg);
-out:
- DTRACE("sock(%px) len:%ld, more:%d ret:%d\n", sock, iov_iter_count(it), more, ret);
- return ret;
-}
-
static int do_sock_recv(struct socket *sock, void *buf, size_t len)
{
@@ -352,14 +304,110 @@ static void pcs_sockio_recv(struct pcs_sockio *sio)
}
+static bool iov_iter_sendpages_ok(struct iov_iter *it)
+{
+ const struct bio_vec *bvec = it->bvec;
+
+ if (!iov_iter_is_bvec(it))
+ return false;
+
+ while (bvec < (it->bvec + it->nr_segs)) {
+ if (!sendpages_ok(bvec->bv_page, bvec->bv_len, bvec->bv_offset))
+ return false;
+ bvec++;
+ }
+
+ return true;
+}
+
+static int do_sockio_sendmsg(struct pcs_sockio *sio, struct pcs_msg *msg)
+{
+ struct iov_iter *it = &sio->write_iter;
+ unsigned int flags = MSG_DONTWAIT | MSG_NOSIGNAL;
+
+ if (unlikely(pcs_should_fail_sock_io()))
+ return -PCS_ERR_NET_ABORT;
+
+ set_bit(PCS_MSG_BUSY, &msg->flags);
+
+ /* pair with the smp_mb() between set PCS_MSG_ABORTED and test PCS_MSG_BUSY */
+ smp_mb();
+ /* Shouldn't abort a half sent message */
+ if (unlikely(test_bit(PCS_MSG_ABORTED, &msg->flags)) && !sio->write_offset) {
+ pcs_set_local_error(&msg->error, PCS_ERR_CANCEL_REQUEST);
+ goto out;
+ }
+
+ while (sio->write_offset < msg->size) {
+ size_t left = msg->size - sio->write_offset;
+ struct msghdr sockmsg = {.msg_flags = flags};
+
+ TRACE("sio(%px) offset:%d msg:%px left:%zu, it->len:%zu\n", sio, sio->write_offset, msg, left, iov_iter_count(it));
+
+ if (!iov_iter_count(it)) {
+ /* Current iter is exhausted, init new one */
+ msg->get_iter(msg, sio->write_offset, it, WRITE);
+ }
+
+ BUG_ON(iov_iter_count(it) > left);
+
+ if (iov_iter_count(it) < left ||
+ msg->list.next != &sio->write_queue)
+ sockmsg.msg_flags |= MSG_MORE;
+ if (iov_iter_count(it) == left && unlikely(pcs_use_eor))
+ sockmsg.msg_flags |= MSG_EOR;
+
+ if (iov_iter_sendpages_ok(it))
+ sockmsg.msg_flags |= MSG_SPLICE_PAGES;
+
+ sockmsg.msg_iter = *it;
+
+ while (iov_iter_count(it)) {
+ int ret;
+
+ DTRACE("sock(%px) len:%zu, flags:%08x\n", sio->socket, iov_iter_count(it), sockmsg.msg_flags);
+ ret = sock_sendmsg(sio->socket, &sockmsg);
+ DTRACE("sock(%px) len:%zu, flags:%08x, ret=%d\n", sio->socket, iov_iter_count(it), sockmsg.msg_flags, ret);
+ if (ret <= 0) {
+ if (ret == -EAGAIN) {
+ if (!time_is_before_jiffies((unsigned long)(msg->start_time + sio->send_timeout)))
+ /* Keep PCS_MSG_BUSY: the msg is half
+ * sent and the transfer will resume,
+ * it must not be aborted meanwhile. */
+ return 0;
+
+ ret = -PCS_ERR_WRITE_TIMEOUT;
+ } else {
+ WARN_ON(ret == 0);
+ ret = -PCS_ERR_NET_ABORT;
+ }
+
+ /* The caller aborts the connection: no more IO on
+ * this msg, release it for pcs_msg_abort() before
+ * sio_abort() completes it. */
+ clear_and_wake_up_bit(PCS_MSG_BUSY, &msg->flags);
+ return ret;
+ }
+
+ iov_iter_advance(it, ret);
+ sio->write_offset += ret;
+ }
+ WARN_ON(msg_data_left(&sockmsg));
+ }
+
+out:
+ clear_and_wake_up_bit(PCS_MSG_BUSY, &msg->flags);
+ return 1;
+}
+
static void pcs_sockio_send(struct pcs_sockio *sio)
{
struct pcs_rpc *ep __maybe_unused = sio->netio.parent;
- struct iov_iter *it = &sio->write_iter;
unsigned long loop_timeout = jiffies + PCS_SIO_SLICE;
struct pcs_msg * msg;
- int done = 0;
int count = 0;
+ int ret = 0;
+ bool to_push = false;
while (!list_empty(&sio->write_queue)) {
msg = list_first_entry(&sio->write_queue, struct pcs_msg, list);
@@ -380,54 +428,15 @@ static void pcs_sockio_send(struct pcs_sockio *sio)
return;
}
- set_bit(PCS_MSG_BUSY, &msg->flags);
-
- /* pair with the smp_mb() between set PCS_MSG_ABORTED and test PCS_MSG_BUSY */
- smp_mb();
-
- /* Shouldn't abort a half sent message */
- if (unlikely(test_bit(PCS_MSG_ABORTED, &msg->flags)) && !sio->write_offset) {
- pcs_set_local_error(&msg->error, PCS_ERR_CANCEL_REQUEST);
- goto skip_send;
- }
-
- /* TODO: cond resched here? */
- while (sio->write_offset < msg->size) {
- size_t left = msg->size - sio->write_offset;
- int has_more = (msg->list.next != &sio->write_queue);
- int n;
-
- TRACE(PEER_FMT "offset:%d msg:%px left:%ld, it->len:%ld\n", PEER_ARGS(ep), sio->write_offset, msg,
- left, iov_iter_count(it));
-
- if (!iov_iter_count(it)) {
- /* Current iter is exhausted, init new one */
- msg->get_iter(msg, sio->write_offset, it, WRITE);
- }
- BUG_ON(iov_iter_count(it) > left);
- n = do_send_one_seg(sio->socket, it, left, has_more);
- if (n > 0) {
- sio->write_offset += n;
- iov_iter_advance(it, n);
- done = 1;
- } else {
- if (n == 0)
- WARN_ON(1);
-
- if (n == -EAGAIN) {
- unsigned long timeout = msg->start_time + sio->send_timeout;
- if (time_is_before_jiffies(timeout))
- sio_abort(sio, PCS_ERR_WRITE_TIMEOUT);
- return;
- }
- sio_abort(sio, PCS_ERR_NET_ABORT);
- return;
- }
+ /* Send out this msg */
+ ret = do_sockio_sendmsg(sio, msg);
+ if (ret <= 0) {
+ if (ret < 0)
+ sio_abort(sio, -ret);
+ return;
}
-skip_send:
- clear_and_wake_up_bit(PCS_MSG_BUSY, &msg->flags);
-
+ to_push = true;
list_del_init(&msg->list);
sio->write_queue_len -= msg->size;
@@ -435,8 +444,9 @@ static void pcs_sockio_send(struct pcs_sockio *sio)
if (sio->write_wakeup)
sio->write_wakeup(sio);
}
+
sio->write_offset = 0;
- iov_iter_kvec(it, WRITE, NULL, 0, 0);
+ iov_iter_kvec(&sio->write_iter, WRITE, NULL, 0, 0);
pcs_msg_sent(msg);
msg->done(msg);
if (++count >= PCS_SIO_PREEMPT_LIMIT ||
@@ -445,7 +455,8 @@ static void pcs_sockio_send(struct pcs_sockio *sio)
break;
}
}
- if (done)
+
+ if (to_push)
sio_push(sio);
}
--
Git-155)
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
next prev parent reply other threads:[~2026-09-30 7:36 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 7:34 [Devel] [PATCH hci-8.1 0/3] fs/fuse kio: cut per-4k-page sendmsg/recvmsg calls in the sockio path Liu Kui
2026-09-30 7:34 ` [Devel] [PATCH hci-8.1 1/3] fs/fuse kio: teach pcs_umem about hugepage-backed mappings Liu Kui
2026-09-30 7:41 ` no-reply
2026-09-30 7:34 ` Liu Kui [this message]
2026-09-30 7:42 ` [Devel] [PATCH hci-8.1 2/3] fs/fuse kio: refactor transmit path in pcs_sockio_send() no-reply
2026-09-30 16:29 ` Konstantin Khorenko
2026-09-30 7:34 ` [Devel] [PATCH hci-8.1 3/3] fs/fuse kio: refactor receive path in pcs_sockio_recv() Liu Kui
2026-09-30 7:42 ` no-reply
2026-09-30 16:09 ` [Devel] [PATCH hci-8.1 0/3] fs/fuse kio: cut per-4k-page sendmsg/recvmsg calls in the sockio path Konstantin Khorenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260930-sockio-refactor-8-1-v1-2-806348b9eb26@virtuozzo.com \
--to=kui.liu@virtuozzo.com \
--cc=azaitsev@virtuozzo.com \
--cc=devel@openvz.org \
--cc=khorenko@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox