From nobody Tue Nov 4 08:12:02 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1504172903047279.3509510476497; Thu, 31 Aug 2017 02:48:23 -0700 (PDT) Received: from localhost ([::1]:54698 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dnM5E-0004eU-PS for importer@patchew.org; Thu, 31 Aug 2017 05:48:20 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:59433) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dnM3w-000490-TU for qemu-devel@nongnu.org; Thu, 31 Aug 2017 05:47:02 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dnM3q-0003IH-64 for qemu-devel@nongnu.org; Thu, 31 Aug 2017 05:47:00 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57954) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dnM3p-0003I8-To for qemu-devel@nongnu.org; Thu, 31 Aug 2017 05:46:54 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D7CEA883AE for ; Thu, 31 Aug 2017 09:46:52 +0000 (UTC) Received: from localhost.localdomain.com (unknown [10.42.22.189]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4684BB32AB; Thu, 31 Aug 2017 09:46:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com D7CEA883AE Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=berrange@redhat.com From: "Daniel P. Berrange" To: qemu-devel@nongnu.org Date: Thu, 31 Aug 2017 10:46:43 +0100 Message-Id: <20170831094643.22567-1-berrange@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Thu, 31 Aug 2017 09:46:53 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v2] io: add new qio_channel_{readv, writev, read, write}_all functions X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , Juan Quintela Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" These functions wait until they are able to read / write the full requested data buffer(s). Signed-off-by: Daniel P. Berrange Reviewed-by: Eric Blake --- Changed in v2: - Remove 'coroutine_fn' annotation (Stefan) - Fix docs typos (Eric) - Remove bogus error overwriting (Eric) include/io/channel.h | 90 +++++++++++++++++++++++++++++++++++++++ io/channel.c | 94 +++++++++++++++++++++++++++++++++++++++++ tests/io-channel-helpers.c | 102 ++++-------------------------------------= ---- 3 files changed, 193 insertions(+), 93 deletions(-) diff --git a/include/io/channel.h b/include/io/channel.h index 54f3dc252f..8f25893c45 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -269,6 +269,58 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, Error **errp); =20 /** + * qio_channel_readv_all: + * @ioc: the channel object + * @iov: the array of memory regions to read data into + * @niov: the length of the @iov array + * @errp: pointer to a NULL-initialized error object + * + * Read data from the IO channel, storing it in the + * memory regions referenced by @iov. Each element + * in the @iov will be fully populated with data + * before the next one is used. The @niov parameter + * specifies the total number of elements in @iov. + * + * The function will wait for all requested data + * to be read, yielding from the current coroutine + * if required. + * + * If end-of-file occurs before all requested data + * has been read, an error will be reported. + * + * Returns: 0 if all bytes were read, or -1 on error + */ +int qio_channel_readv_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp); + + +/** + * qio_channel_writev_all: + * @ioc: the channel object + * @iov: the array of memory regions to write data from + * @niov: the length of the @iov array + * @errp: pointer to a NULL-initialized error object + * + * Write data to the IO channel, reading it from the + * memory regions referenced by @iov. Each element + * in the @iov will be fully sent, before the next + * one is used. The @niov parameter specifies the + * total number of elements in @iov. + * + * The function will wait for all requested data + * to be written, yielding from the current coroutine + * if required. + * + * Returns: 0 if all bytes were written, or -1 on error + */ +int qio_channel_writev_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **erp); + +/** * qio_channel_readv: * @ioc: the channel object * @iov: the array of memory regions to read data into @@ -331,6 +383,44 @@ ssize_t qio_channel_write(QIOChannel *ioc, Error **errp); =20 /** + * qio_channel_read_all: + * @ioc: the channel object + * @buf: the memory region to read data into + * @buflen: the number of bytes to @buf + * @errp: pointer to a NULL-initialized error object + * + * Reads @buflen bytes into @buf, possibly blocking or (if the + * channel is non-blocking) yielding from the current coroutine + * multiple times until the entire content is read. If end-of-file + * occurs it will return an error rather than a short-read. Otherwise + * behaves as qio_channel_read(). + * + * Returns: 0 if all bytes were read, or -1 on error + */ +int qio_channel_read_all(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp); +/** + * qio_channel_write_all: + * @ioc: the channel object + * @buf: the memory region to write data into + * @buflen: the number of bytes to @buf + * @errp: pointer to a NULL-initialized error object + * + * Writes @buflen bytes from @buf, possibly blocking or (if the + * channel is non-blocking) yielding from the current coroutine + * multiple times until the entire content is written. Otherwise + * behaves as qio_channel_write(). + * + * Returns: 0 if all bytes were written, or -1 on error + */ +int qio_channel_write_all(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp); + +/** * qio_channel_set_blocking: * @ioc: the channel object * @enabled: the blocking flag state diff --git a/io/channel.c b/io/channel.c index 1cfb8b33a2..5e8c2f0a91 100644 --- a/io/channel.c +++ b/io/channel.c @@ -22,6 +22,7 @@ #include "io/channel.h" #include "qapi/error.h" #include "qemu/main-loop.h" +#include "qemu/iov.h" =20 bool qio_channel_has_feature(QIOChannel *ioc, QIOChannelFeature feature) @@ -85,6 +86,79 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc, } =20 =20 + +int qio_channel_readv_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + int ret =3D -1; + struct iovec *local_iov =3D g_new(struct iovec, niov); + struct iovec *local_iov_head =3D local_iov; + unsigned int nlocal_iov =3D niov; + + nlocal_iov =3D iov_copy(local_iov, nlocal_iov, + iov, niov, + 0, iov_size(iov, niov)); + + while (nlocal_iov > 0) { + ssize_t len; + len =3D qio_channel_readv(ioc, local_iov, nlocal_iov, errp); + if (len =3D=3D QIO_CHANNEL_ERR_BLOCK) { + qio_channel_wait(ioc, G_IO_IN); + continue; + } else if (len < 0) { + goto cleanup; + } else if (len =3D=3D 0) { + error_setg(errp, + "Unexpected end-of-file before all bytes were read"= ); + goto cleanup; + } + + iov_discard_front(&local_iov, &nlocal_iov, len); + } + + ret =3D 0; + + cleanup: + g_free(local_iov_head); + return ret; +} + +int qio_channel_writev_all(QIOChannel *ioc, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + int ret =3D -1; + struct iovec *local_iov =3D g_new(struct iovec, niov); + struct iovec *local_iov_head =3D local_iov; + unsigned int nlocal_iov =3D niov; + + nlocal_iov =3D iov_copy(local_iov, nlocal_iov, + iov, niov, + 0, iov_size(iov, niov)); + + while (nlocal_iov > 0) { + ssize_t len; + len =3D qio_channel_writev(ioc, local_iov, nlocal_iov, errp); + if (len =3D=3D QIO_CHANNEL_ERR_BLOCK) { + qio_channel_wait(ioc, G_IO_OUT); + continue; + } + if (len < 0) { + goto cleanup; + } + + iov_discard_front(&local_iov, &nlocal_iov, len); + } + + ret =3D 0; + cleanup: + g_free(local_iov_head); + return ret; +} + ssize_t qio_channel_readv(QIOChannel *ioc, const struct iovec *iov, size_t niov, @@ -123,6 +197,26 @@ ssize_t qio_channel_write(QIOChannel *ioc, } =20 =20 +int qio_channel_read_all(QIOChannel *ioc, + char *buf, + size_t buflen, + Error **errp) +{ + struct iovec iov =3D { .iov_base =3D buf, .iov_len =3D buflen }; + return qio_channel_readv_all(ioc, &iov, 1, errp); +} + + +int qio_channel_write_all(QIOChannel *ioc, + const char *buf, + size_t buflen, + Error **errp) +{ + struct iovec iov =3D { .iov_base =3D (char *)buf, .iov_len =3D buflen = }; + return qio_channel_writev_all(ioc, &iov, 1, errp); +} + + int qio_channel_set_blocking(QIOChannel *ioc, bool enabled, Error **errp) diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c index 05e5579cf8..5430e1389d 100644 --- a/tests/io-channel-helpers.c +++ b/tests/io-channel-helpers.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "io-channel-helpers.h" #include "qapi/error.h" +#include "qemu/iov.h" =20 struct QIOChannelTest { QIOChannel *src; @@ -37,77 +38,17 @@ struct QIOChannelTest { }; =20 =20 -static void test_skip_iovec(struct iovec **iov, - size_t *niov, - size_t skip, - struct iovec *old) -{ - size_t offset =3D 0; - size_t i; - - for (i =3D 0; i < *niov; i++) { - if (skip < (*iov)[i].iov_len) { - old->iov_len =3D (*iov)[i].iov_len; - old->iov_base =3D (*iov)[i].iov_base; - - (*iov)[i].iov_len -=3D skip; - (*iov)[i].iov_base +=3D skip; - break; - } else { - skip -=3D (*iov)[i].iov_len; - - if (i =3D=3D 0 && old->iov_base) { - (*iov)[i].iov_len =3D old->iov_len; - (*iov)[i].iov_base =3D old->iov_base; - old->iov_len =3D 0; - old->iov_base =3D NULL; - } - - offset++; - } - } - - *iov =3D *iov + offset; - *niov -=3D offset; -} - - /* This thread sends all data using iovecs */ static gpointer test_io_thread_writer(gpointer opaque) { QIOChannelTest *data =3D opaque; - struct iovec *iov =3D data->inputv; - size_t niov =3D data->niov; - struct iovec old =3D { 0 }; =20 qio_channel_set_blocking(data->src, data->blocking, NULL); =20 - while (niov) { - ssize_t ret; - ret =3D qio_channel_writev(data->src, - iov, - niov, - &data->writeerr); - if (ret =3D=3D QIO_CHANNEL_ERR_BLOCK) { - if (data->blocking) { - error_setg(&data->writeerr, - "Unexpected I/O blocking"); - break; - } else { - qio_channel_wait(data->src, - G_IO_OUT); - continue; - } - } else if (ret < 0) { - break; - } else if (ret =3D=3D 0) { - error_setg(&data->writeerr, - "Unexpected zero length write"); - break; - } - - test_skip_iovec(&iov, &niov, ret, &old); - } + qio_channel_writev_all(data->src, + data->inputv, + data->niov, + &data->writeerr); =20 return NULL; } @@ -117,38 +58,13 @@ static gpointer test_io_thread_writer(gpointer opaque) static gpointer test_io_thread_reader(gpointer opaque) { QIOChannelTest *data =3D opaque; - struct iovec *iov =3D data->outputv; - size_t niov =3D data->niov; - struct iovec old =3D { 0 }; =20 qio_channel_set_blocking(data->dst, data->blocking, NULL); =20 - while (niov) { - ssize_t ret; - - ret =3D qio_channel_readv(data->dst, - iov, - niov, - &data->readerr); - - if (ret =3D=3D QIO_CHANNEL_ERR_BLOCK) { - if (data->blocking) { - error_setg(&data->readerr, - "Unexpected I/O blocking"); - break; - } else { - qio_channel_wait(data->dst, - G_IO_IN); - continue; - } - } else if (ret < 0) { - break; - } else if (ret =3D=3D 0) { - break; - } - - test_skip_iovec(&iov, &niov, ret, &old); - } + qio_channel_readv_all(data->dst, + data->outputv, + data->niov, + &data->readerr); =20 return NULL; } --=20 2.13.5