Blame SOURCES/kvm-QIOChannel-Add-flags-on-io_writev-and-introduce-io_f.patch

0727d3
From 7eeec7c008e947bc3e1fed682791092b408852c6 Mon Sep 17 00:00:00 2001
0727d3
From: Leonardo Bras <leobras@redhat.com>
0727d3
Date: Wed, 18 May 2022 02:52:24 -0300
0727d3
Subject: [PATCH 17/37] QIOChannel: Add flags on io_writev and introduce
0727d3
 io_flush callback
0727d3
MIME-Version: 1.0
0727d3
Content-Type: text/plain; charset=UTF-8
0727d3
Content-Transfer-Encoding: 8bit
0727d3
0727d3
RH-Author: Leonardo Brás <leobras@redhat.com>
0727d3
RH-MergeRequest: 191: MSG_ZEROCOPY + Multifd @ rhel8.7
0727d3
RH-Commit: [17/26] 7bde4e79fd3f76a6cc84d9cacf50420584ddd35c
0727d3
RH-Bugzilla: 2072049
0727d3
RH-Acked-by: Peter Xu <peterx@redhat.com>
0727d3
RH-Acked-by: Daniel P. Berrangé <berrange@redhat.com>
0727d3
RH-Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
0727d3
0727d3
Add flags to io_writev and introduce io_flush as optional callback to
0727d3
QIOChannelClass, allowing the implementation of zero copy writes by
0727d3
subclasses.
0727d3
0727d3
How to use them:
0727d3
- Write data using qio_channel_writev*(...,QIO_CHANNEL_WRITE_FLAG_ZERO_COPY),
0727d3
- Wait write completion with qio_channel_flush().
0727d3
0727d3
Notes:
0727d3
As some zero copy write implementations work asynchronously, it's
0727d3
recommended to keep the write buffer untouched until the return of
0727d3
qio_channel_flush(), to avoid the risk of sending an updated buffer
0727d3
instead of the buffer state during write.
0727d3
0727d3
As io_flush callback is optional, if a subclass does not implement it, then:
0727d3
- io_flush will return 0 without changing anything.
0727d3
0727d3
Also, some functions like qio_channel_writev_full_all() were adapted to
0727d3
receive a flag parameter. That allows shared code between zero copy and
0727d3
non-zero copy writev, and also an easier implementation on new flags.
0727d3
0727d3
Signed-off-by: Leonardo Bras <leobras@redhat.com>
0727d3
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
0727d3
Reviewed-by: Peter Xu <peterx@redhat.com>
0727d3
Reviewed-by: Juan Quintela <quintela@redhat.com>
0727d3
Message-Id: <20220513062836.965425-3-leobras@redhat.com>
0727d3
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
0727d3
(cherry picked from commit b88651cb4d4fa416fdbb6afaf5b26ec8c035eaad)
0727d3
Signed-off-by: Leonardo Bras <leobras@redhat.com>
0727d3
---
0727d3
 chardev/char-io.c                   |  2 +-
0727d3
 hw/remote/mpqemu-link.c             |  2 +-
0727d3
 include/io/channel.h                | 38 +++++++++++++++++++++-
0727d3
 io/channel-buffer.c                 |  1 +
0727d3
 io/channel-command.c                |  1 +
0727d3
 io/channel-file.c                   |  1 +
0727d3
 io/channel-socket.c                 |  2 ++
0727d3
 io/channel-tls.c                    |  1 +
0727d3
 io/channel-websock.c                |  1 +
0727d3
 io/channel.c                        | 49 +++++++++++++++++++++++------
0727d3
 migration/rdma.c                    |  1 +
0727d3
 scsi/pr-manager-helper.c            |  2 +-
0727d3
 tests/unit/test-io-channel-socket.c |  1 +
0727d3
 13 files changed, 88 insertions(+), 14 deletions(-)
0727d3
0727d3
diff --git a/chardev/char-io.c b/chardev/char-io.c
0727d3
index 8ced184160..4451128cba 100644
0727d3
--- a/chardev/char-io.c
0727d3
+++ b/chardev/char-io.c
0727d3
@@ -122,7 +122,7 @@ int io_channel_send_full(QIOChannel *ioc,
0727d3
 
0727d3
         ret = qio_channel_writev_full(
0727d3
             ioc, &iov, 1,
0727d3
-            fds, nfds, NULL);
0727d3
+            fds, nfds, 0, NULL);
0727d3
         if (ret == QIO_CHANNEL_ERR_BLOCK) {
0727d3
             if (offset) {
0727d3
                 return offset;
0727d3
diff --git a/hw/remote/mpqemu-link.c b/hw/remote/mpqemu-link.c
0727d3
index 7e841820e5..e8f556bd27 100644
0727d3
--- a/hw/remote/mpqemu-link.c
0727d3
+++ b/hw/remote/mpqemu-link.c
0727d3
@@ -69,7 +69,7 @@ bool mpqemu_msg_send(MPQemuMsg *msg, QIOChannel *ioc, Error **errp)
0727d3
     }
0727d3
 
0727d3
     if (!qio_channel_writev_full_all(ioc, send, G_N_ELEMENTS(send),
0727d3
-                                    fds, nfds, errp)) {
0727d3
+                                    fds, nfds, 0, errp)) {
0727d3
         ret = true;
0727d3
     } else {
0727d3
         trace_mpqemu_send_io_error(msg->cmd, msg->size, nfds);
0727d3
diff --git a/include/io/channel.h b/include/io/channel.h
0727d3
index 88988979f8..c680ee7480 100644
0727d3
--- a/include/io/channel.h
0727d3
+++ b/include/io/channel.h
0727d3
@@ -32,12 +32,15 @@ OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass,
0727d3
 
0727d3
 #define QIO_CHANNEL_ERR_BLOCK -2
0727d3
 
0727d3
+#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1
0727d3
+
0727d3
 typedef enum QIOChannelFeature QIOChannelFeature;
0727d3
 
0727d3
 enum QIOChannelFeature {
0727d3
     QIO_CHANNEL_FEATURE_FD_PASS,
0727d3
     QIO_CHANNEL_FEATURE_SHUTDOWN,
0727d3
     QIO_CHANNEL_FEATURE_LISTEN,
0727d3
+    QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY,
0727d3
 };
0727d3
 
0727d3
 
0727d3
@@ -104,6 +107,7 @@ struct QIOChannelClass {
0727d3
                          size_t niov,
0727d3
                          int *fds,
0727d3
                          size_t nfds,
0727d3
+                         int flags,
0727d3
                          Error **errp);
0727d3
     ssize_t (*io_readv)(QIOChannel *ioc,
0727d3
                         const struct iovec *iov,
0727d3
@@ -136,6 +140,8 @@ struct QIOChannelClass {
0727d3
                                   IOHandler *io_read,
0727d3
                                   IOHandler *io_write,
0727d3
                                   void *opaque);
0727d3
+    int (*io_flush)(QIOChannel *ioc,
0727d3
+                    Error **errp);
0727d3
 };
0727d3
 
0727d3
 /* General I/O handling functions */
0727d3
@@ -228,6 +234,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc,
0727d3
  * @niov: the length of the @iov array
0727d3
  * @fds: an array of file handles to send
0727d3
  * @nfds: number of file handles in @fds
0727d3
+ * @flags: write flags (QIO_CHANNEL_WRITE_FLAG_*)
0727d3
  * @errp: pointer to a NULL-initialized error object
0727d3
  *
0727d3
  * Write data to the IO channel, reading it from the
0727d3
@@ -260,6 +267,7 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc,
0727d3
                                 size_t niov,
0727d3
                                 int *fds,
0727d3
                                 size_t nfds,
0727d3
+                                int flags,
0727d3
                                 Error **errp);
0727d3
 
0727d3
 /**
0727d3
@@ -837,6 +845,7 @@ int qio_channel_readv_full_all(QIOChannel *ioc,
0727d3
  * @niov: the length of the @iov array
0727d3
  * @fds: an array of file handles to send
0727d3
  * @nfds: number of file handles in @fds
0727d3
+ * @flags: write flags (QIO_CHANNEL_WRITE_FLAG_*)
0727d3
  * @errp: pointer to a NULL-initialized error object
0727d3
  *
0727d3
  *
0727d3
@@ -846,6 +855,14 @@ int qio_channel_readv_full_all(QIOChannel *ioc,
0727d3
  * to be written, yielding from the current coroutine
0727d3
  * if required.
0727d3
  *
0727d3
+ * If QIO_CHANNEL_WRITE_FLAG_ZERO_COPY is passed in flags,
0727d3
+ * instead of waiting for all requested data to be written,
0727d3
+ * this function will wait until it's all queued for writing.
0727d3
+ * In this case, if the buffer gets changed between queueing and
0727d3
+ * sending, the updated buffer will be sent. If this is not a
0727d3
+ * desired behavior, it's suggested to call qio_channel_flush()
0727d3
+ * before reusing the buffer.
0727d3
+ *
0727d3
  * Returns: 0 if all bytes were written, or -1 on error
0727d3
  */
0727d3
 
0727d3
@@ -853,6 +870,25 @@ int qio_channel_writev_full_all(QIOChannel *ioc,
0727d3
                                 const struct iovec *iov,
0727d3
                                 size_t niov,
0727d3
                                 int *fds, size_t nfds,
0727d3
-                                Error **errp);
0727d3
+                                int flags, Error **errp);
0727d3
+
0727d3
+/**
0727d3
+ * qio_channel_flush:
0727d3
+ * @ioc: the channel object
0727d3
+ * @errp: pointer to a NULL-initialized error object
0727d3
+ *
0727d3
+ * Will block until every packet queued with
0727d3
+ * qio_channel_writev_full() + QIO_CHANNEL_WRITE_FLAG_ZERO_COPY
0727d3
+ * is sent, or return in case of any error.
0727d3
+ *
0727d3
+ * If not implemented, acts as a no-op, and returns 0.
0727d3
+ *
0727d3
+ * Returns -1 if any error is found,
0727d3
+ *          1 if every send failed to use zero copy.
0727d3
+ *          0 otherwise.
0727d3
+ */
0727d3
+
0727d3
+int qio_channel_flush(QIOChannel *ioc,
0727d3
+                      Error **errp);
0727d3
 
0727d3
 #endif /* QIO_CHANNEL_H */
0727d3
diff --git a/io/channel-buffer.c b/io/channel-buffer.c
0727d3
index baa4e2b089..bf52011be2 100644
0727d3
--- a/io/channel-buffer.c
0727d3
+++ b/io/channel-buffer.c
0727d3
@@ -81,6 +81,7 @@ static ssize_t qio_channel_buffer_writev(QIOChannel *ioc,
0727d3
                                          size_t niov,
0727d3
                                          int *fds,
0727d3
                                          size_t nfds,
0727d3
+                                         int flags,
0727d3
                                          Error **errp)
0727d3
 {
0727d3
     QIOChannelBuffer *bioc = QIO_CHANNEL_BUFFER(ioc);
0727d3
diff --git a/io/channel-command.c b/io/channel-command.c
0727d3
index b2a9e27138..5ff1691bad 100644
0727d3
--- a/io/channel-command.c
0727d3
+++ b/io/channel-command.c
0727d3
@@ -258,6 +258,7 @@ static ssize_t qio_channel_command_writev(QIOChannel *ioc,
0727d3
                                           size_t niov,
0727d3
                                           int *fds,
0727d3
                                           size_t nfds,
0727d3
+                                          int flags,
0727d3
                                           Error **errp)
0727d3
 {
0727d3
     QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
0727d3
diff --git a/io/channel-file.c b/io/channel-file.c
0727d3
index c4bf799a80..348a48545e 100644
0727d3
--- a/io/channel-file.c
0727d3
+++ b/io/channel-file.c
0727d3
@@ -114,6 +114,7 @@ static ssize_t qio_channel_file_writev(QIOChannel *ioc,
0727d3
                                        size_t niov,
0727d3
                                        int *fds,
0727d3
                                        size_t nfds,
0727d3
+                                       int flags,
0727d3
                                        Error **errp)
0727d3
 {
0727d3
     QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
0727d3
diff --git a/io/channel-socket.c b/io/channel-socket.c
0727d3
index 606ec97cf7..bfbd64787e 100644
0727d3
--- a/io/channel-socket.c
0727d3
+++ b/io/channel-socket.c
0727d3
@@ -525,6 +525,7 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
0727d3
                                          size_t niov,
0727d3
                                          int *fds,
0727d3
                                          size_t nfds,
0727d3
+                                         int flags,
0727d3
                                          Error **errp)
0727d3
 {
0727d3
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
0727d3
@@ -620,6 +621,7 @@ static ssize_t qio_channel_socket_writev(QIOChannel *ioc,
0727d3
                                          size_t niov,
0727d3
                                          int *fds,
0727d3
                                          size_t nfds,
0727d3
+                                         int flags,
0727d3
                                          Error **errp)
0727d3
 {
0727d3
     QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
0727d3
diff --git a/io/channel-tls.c b/io/channel-tls.c
0727d3
index 2ae1b92fc0..4ce890a538 100644
0727d3
--- a/io/channel-tls.c
0727d3
+++ b/io/channel-tls.c
0727d3
@@ -301,6 +301,7 @@ static ssize_t qio_channel_tls_writev(QIOChannel *ioc,
0727d3
                                       size_t niov,
0727d3
                                       int *fds,
0727d3
                                       size_t nfds,
0727d3
+                                      int flags,
0727d3
                                       Error **errp)
0727d3
 {
0727d3
     QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
0727d3
diff --git a/io/channel-websock.c b/io/channel-websock.c
0727d3
index 70889bb54d..035dd6075b 100644
0727d3
--- a/io/channel-websock.c
0727d3
+++ b/io/channel-websock.c
0727d3
@@ -1127,6 +1127,7 @@ static ssize_t qio_channel_websock_writev(QIOChannel *ioc,
0727d3
                                           size_t niov,
0727d3
                                           int *fds,
0727d3
                                           size_t nfds,
0727d3
+                                          int flags,
0727d3
                                           Error **errp)
0727d3
 {
0727d3
     QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
0727d3
diff --git a/io/channel.c b/io/channel.c
0727d3
index e8b019dc36..0640941ac5 100644
0727d3
--- a/io/channel.c
0727d3
+++ b/io/channel.c
0727d3
@@ -72,18 +72,32 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc,
0727d3
                                 size_t niov,
0727d3
                                 int *fds,
0727d3
                                 size_t nfds,
0727d3
+                                int flags,
0727d3
                                 Error **errp)
0727d3
 {
0727d3
     QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
0727d3
 
0727d3
-    if ((fds || nfds) &&
0727d3
-        !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
0727d3
+    if (fds || nfds) {
0727d3
+        if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
0727d3
+            error_setg_errno(errp, EINVAL,
0727d3
+                             "Channel does not support file descriptor passing");
0727d3
+            return -1;
0727d3
+        }
0727d3
+        if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) {
0727d3
+            error_setg_errno(errp, EINVAL,
0727d3
+                             "Zero Copy does not support file descriptor passing");
0727d3
+            return -1;
0727d3
+        }
0727d3
+    }
0727d3
+
0727d3
+    if ((flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) &&
0727d3
+        !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) {
0727d3
         error_setg_errno(errp, EINVAL,
0727d3
-                         "Channel does not support file descriptor passing");
0727d3
+                         "Requested Zero Copy feature is not available");
0727d3
         return -1;
0727d3
     }
0727d3
 
0727d3
-    return klass->io_writev(ioc, iov, niov, fds, nfds, errp);
0727d3
+    return klass->io_writev(ioc, iov, niov, fds, nfds, flags, errp);
0727d3
 }
0727d3
 
0727d3
 
0727d3
@@ -217,14 +231,14 @@ int qio_channel_writev_all(QIOChannel *ioc,
0727d3
                            size_t niov,
0727d3
                            Error **errp)
0727d3
 {
0727d3
-    return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, errp);
0727d3
+    return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, 0, errp);
0727d3
 }
0727d3
 
0727d3
 int qio_channel_writev_full_all(QIOChannel *ioc,
0727d3
                                 const struct iovec *iov,
0727d3
                                 size_t niov,
0727d3
                                 int *fds, size_t nfds,
0727d3
-                                Error **errp)
0727d3
+                                int flags, Error **errp)
0727d3
 {
0727d3
     int ret = -1;
0727d3
     struct iovec *local_iov = g_new(struct iovec, niov);
0727d3
@@ -237,8 +251,10 @@ int qio_channel_writev_full_all(QIOChannel *ioc,
0727d3
 
0727d3
     while (nlocal_iov > 0) {
0727d3
         ssize_t len;
0727d3
-        len = qio_channel_writev_full(ioc, local_iov, nlocal_iov, fds, nfds,
0727d3
-                                      errp);
0727d3
+
0727d3
+        len = qio_channel_writev_full(ioc, local_iov, nlocal_iov, fds,
0727d3
+                                            nfds, flags, errp);
0727d3
+
0727d3
         if (len == QIO_CHANNEL_ERR_BLOCK) {
0727d3
             if (qemu_in_coroutine()) {
0727d3
                 qio_channel_yield(ioc, G_IO_OUT);
0727d3
@@ -277,7 +293,7 @@ ssize_t qio_channel_writev(QIOChannel *ioc,
0727d3
                            size_t niov,
0727d3
                            Error **errp)
0727d3
 {
0727d3
-    return qio_channel_writev_full(ioc, iov, niov, NULL, 0, errp);
0727d3
+    return qio_channel_writev_full(ioc, iov, niov, NULL, 0, 0, errp);
0727d3
 }
0727d3
 
0727d3
 
0727d3
@@ -297,7 +313,7 @@ ssize_t qio_channel_write(QIOChannel *ioc,
0727d3
                           Error **errp)
0727d3
 {
0727d3
     struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen };
0727d3
-    return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, errp);
0727d3
+    return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, 0, errp);
0727d3
 }
0727d3
 
0727d3
 
0727d3
@@ -473,6 +489,19 @@ off_t qio_channel_io_seek(QIOChannel *ioc,
0727d3
     return klass->io_seek(ioc, offset, whence, errp);
0727d3
 }
0727d3
 
0727d3
+int qio_channel_flush(QIOChannel *ioc,
0727d3
+                                Error **errp)
0727d3
+{
0727d3
+    QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
0727d3
+
0727d3
+    if (!klass->io_flush ||
0727d3
+        !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) {
0727d3
+        return 0;
0727d3
+    }
0727d3
+
0727d3
+    return klass->io_flush(ioc, errp);
0727d3
+}
0727d3
+
0727d3
 
0727d3
 static void qio_channel_restart_read(void *opaque)
0727d3
 {
0727d3
diff --git a/migration/rdma.c b/migration/rdma.c
0727d3
index f5d3bbe7e9..54acd2000e 100644
0727d3
--- a/migration/rdma.c
0727d3
+++ b/migration/rdma.c
0727d3
@@ -2833,6 +2833,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc,
0727d3
                                        size_t niov,
0727d3
                                        int *fds,
0727d3
                                        size_t nfds,
0727d3
+                                       int flags,
0727d3
                                        Error **errp)
0727d3
 {
0727d3
     QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
0727d3
diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c
0727d3
index 451c7631b7..3be52a98d5 100644
0727d3
--- a/scsi/pr-manager-helper.c
0727d3
+++ b/scsi/pr-manager-helper.c
0727d3
@@ -77,7 +77,7 @@ static int pr_manager_helper_write(PRManagerHelper *pr_mgr,
0727d3
         iov.iov_base = (void *)buf;
0727d3
         iov.iov_len = sz;
0727d3
         n_written = qio_channel_writev_full(QIO_CHANNEL(pr_mgr->ioc), &iov, 1,
0727d3
-                                            nfds ? &fd : NULL, nfds, errp);
0727d3
+                                            nfds ? &fd : NULL, nfds, 0, errp);
0727d3
 
0727d3
         if (n_written <= 0) {
0727d3
             assert(n_written != QIO_CHANNEL_ERR_BLOCK);
0727d3
diff --git a/tests/unit/test-io-channel-socket.c b/tests/unit/test-io-channel-socket.c
0727d3
index c49eec1f03..6713886d02 100644
0727d3
--- a/tests/unit/test-io-channel-socket.c
0727d3
+++ b/tests/unit/test-io-channel-socket.c
0727d3
@@ -444,6 +444,7 @@ static void test_io_channel_unix_fd_pass(void)
0727d3
                             G_N_ELEMENTS(iosend),
0727d3
                             fdsend,
0727d3
                             G_N_ELEMENTS(fdsend),
0727d3
+                            0,
0727d3
                             &error_abort);
0727d3
 
0727d3
     qio_channel_readv_full(dst,
0727d3
-- 
0727d3
2.35.3
0727d3