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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.