From: Stefan Hajnoczi <stefanha@redhat.com>
To: Hanna Czenczek <hreitz@redhat.com>
Cc: qemu-devel@nongnu.org, virtio-fs@redhat.com,
"Michael S . Tsirkin" <mst@redhat.com>,
"German Maglione" <gmaglione@redhat.com>,
"Eugenio Pérez" <eperezma@redhat.com>
Subject: Re: [Virtio-fs] [PATCH v2 3/4] vhost: Add high-level state save/load functions
Date: Tue, 18 Jul 2023 14:42:12 -0400 [thread overview]
Message-ID: <20230718184212.GM44841@fedora> (raw)
In-Reply-To: <20230712111703.28031-4-hreitz@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 9734 bytes --]
On Wed, Jul 12, 2023 at 01:17:01PM +0200, Hanna Czenczek wrote:
> vhost_save_backend_state() and vhost_load_backend_state() can be used by
> vhost front-ends to easily save and load the back-end's state to/from
> the migration stream.
>
> Because we do not know the full state size ahead of time,
> vhost_save_backend_state() simply reads the data in 1 MB chunks, and
> writes each chunk consecutively into the migration stream, prefixed by
> its length. EOF is indicated by a 0-length chunk.
>
> Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
> ---
> include/hw/virtio/vhost.h | 35 +++++++
> hw/virtio/vhost.c | 204 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 239 insertions(+)
>
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index d8877496e5..0c282abd4e 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -425,4 +425,39 @@ int vhost_set_device_state_fd(struct vhost_dev *dev,
> */
> int vhost_check_device_state(struct vhost_dev *dev, Error **errp);
>
> +/**
> + * vhost_save_backend_state(): High-level function to receive a vhost
> + * back-end's state, and save it in `f`. Uses
I think the GtkDoc syntax is @f instead of `f`.
> + * `vhost_set_device_state_fd()` to get the data from the back-end, and
> + * stores it in consecutive chunks that are each prefixed by their
> + * respective length (be32). The end is marked by a 0-length chunk.
> + *
> + * Must only be called while the device and all its vrings are stopped
> + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`).
> + *
> + * @dev: The vhost device from which to save the state
> + * @f: Migration stream in which to save the state
> + * @errp: Potential error message
> + *
> + * Returns 0 on success, and -errno otherwise.
> + */
> +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp);
> +
> +/**
> + * vhost_load_backend_state(): High-level function to load a vhost
> + * back-end's state from `f`, and send it over to the back-end. Reads
> + * the data from `f` in the format used by `vhost_save_state()`, and
> + * uses `vhost_set_device_state_fd()` to transfer it to the back-end.
> + *
> + * Must only be called while the device and all its vrings are stopped
> + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`).
> + *
> + * @dev: The vhost device to which to send the sate
> + * @f: Migration stream from which to load the state
> + * @errp: Potential error message
> + *
> + * Returns 0 on success, and -errno otherwise.
> + */
> +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp);
> +
> #endif
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index 756b6d55a8..332d49a310 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -2128,3 +2128,207 @@ int vhost_check_device_state(struct vhost_dev *dev, Error **errp)
> "vhost transport does not support migration state transfer");
> return -ENOSYS;
> }
> +
> +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp)
> +{
> + /* Maximum chunk size in which to transfer the state */
> + const size_t chunk_size = 1 * 1024 * 1024;
> + void *transfer_buf = NULL;
> + g_autoptr(GError) g_err = NULL;
> + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1;
> + int ret;
> +
> + /* [0] for reading (our end), [1] for writing (back-end's end) */
> + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) {
> + error_setg(errp, "Failed to set up state transfer pipe: %s",
> + g_err->message);
> + ret = -EINVAL;
> + goto fail;
> + }
> +
> + read_fd = pipe_fds[0];
> + write_fd = pipe_fds[1];
> +
> + /*
> + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped.
> + * We cannot check dev->suspended, because the back-end may not support
> + * suspending.
> + */
> + assert(!dev->started);
> +
> + /* Transfer ownership of write_fd to the back-end */
> + ret = vhost_set_device_state_fd(dev,
> + VHOST_TRANSFER_STATE_DIRECTION_SAVE,
> + VHOST_TRANSFER_STATE_PHASE_STOPPED,
> + write_fd,
> + &reply_fd,
> + errp);
> + if (ret < 0) {
> + error_prepend(errp, "Failed to initiate state transfer: ");
> + goto fail;
> + }
> +
> + /* If the back-end wishes to use a different pipe, switch over */
> + if (reply_fd >= 0) {
> + close(read_fd);
> + read_fd = reply_fd;
> + }
> +
> + transfer_buf = g_malloc(chunk_size);
> +
> + while (true) {
> + ssize_t read_ret;
> +
> + read_ret = read(read_fd, transfer_buf, chunk_size);
> + if (read_ret < 0) {
Is it necessary to handle -EINTR?
> + ret = -errno;
> + error_setg_errno(errp, -ret, "Failed to receive state");
> + goto fail;
> + }
> +
> + assert(read_ret <= chunk_size);
> + qemu_put_be32(f, read_ret);
> +
> + if (read_ret == 0) {
> + /* EOF */
> + break;
> + }
> +
> + qemu_put_buffer(f, transfer_buf, read_ret);
> + }
> +
> + /*
> + * Back-end will not really care, but be clean and close our end of the pipe
> + * before inquiring the back-end about whether transfer was successful
> + */
> + close(read_fd);
> + read_fd = -1;
> +
> + /* Also, verify that the device is still stopped */
> + assert(!dev->started);
> +
> + ret = vhost_check_device_state(dev, errp);
> + if (ret < 0) {
> + goto fail;
> + }
> +
> + ret = 0;
> +fail:
> + g_free(transfer_buf);
> + if (read_fd >= 0) {
> + close(read_fd);
> + }
> +
> + return ret;
> +}
> +
> +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp)
> +{
> + size_t transfer_buf_size = 0;
> + void *transfer_buf = NULL;
> + g_autoptr(GError) g_err = NULL;
> + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1;
> + int ret;
> +
> + /* [0] for reading (back-end's end), [1] for writing (our end) */
> + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) {
> + error_setg(errp, "Failed to set up state transfer pipe: %s",
> + g_err->message);
> + ret = -EINVAL;
> + goto fail;
> + }
> +
> + read_fd = pipe_fds[0];
> + write_fd = pipe_fds[1];
> +
> + /*
> + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped.
> + * We cannot check dev->suspended, because the back-end may not support
> + * suspending.
> + */
> + assert(!dev->started);
> +
> + /* Transfer ownership of read_fd to the back-end */
> + ret = vhost_set_device_state_fd(dev,
> + VHOST_TRANSFER_STATE_DIRECTION_LOAD,
> + VHOST_TRANSFER_STATE_PHASE_STOPPED,
> + read_fd,
> + &reply_fd,
> + errp);
> + if (ret < 0) {
> + error_prepend(errp, "Failed to initiate state transfer: ");
> + goto fail;
> + }
> +
> + /* If the back-end wishes to use a different pipe, switch over */
> + if (reply_fd >= 0) {
> + close(write_fd);
> + write_fd = reply_fd;
> + }
> +
> + while (true) {
> + size_t this_chunk_size = qemu_get_be32(f);
> + ssize_t write_ret;
> + const uint8_t *transfer_pointer;
> +
> + if (this_chunk_size == 0) {
> + /* End of state */
> + break;
> + }
> +
> + if (transfer_buf_size < this_chunk_size) {
> + transfer_buf = g_realloc(transfer_buf, this_chunk_size);
> + transfer_buf_size = this_chunk_size;
> + }
> +
> + if (qemu_get_buffer(f, transfer_buf, this_chunk_size) <
> + this_chunk_size)
> + {
> + error_setg(errp, "Failed to read state");
> + ret = -EINVAL;
> + goto fail;
> + }
> +
> + transfer_pointer = transfer_buf;
> + while (this_chunk_size > 0) {
> + write_ret = write(write_fd, transfer_pointer, this_chunk_size);
> + if (write_ret < 0) {
Is it necessary to handle -EINTR?
> + ret = -errno;
> + error_setg_errno(errp, -ret, "Failed to send state");
> + goto fail;
> + } else if (write_ret == 0) {
> + error_setg(errp, "Failed to send state: Connection is closed");
> + ret = -ECONNRESET;
> + goto fail;
> + }
> +
> + assert(write_ret <= this_chunk_size);
> + this_chunk_size -= write_ret;
> + transfer_pointer += write_ret;
> + }
> + }
> +
> + /*
> + * Close our end, thus ending transfer, before inquiring the back-end about
> + * whether transfer was successful
> + */
> + close(write_fd);
> + write_fd = -1;
> +
> + /* Also, verify that the device is still stopped */
> + assert(!dev->started);
> +
> + ret = vhost_check_device_state(dev, errp);
> + if (ret < 0) {
> + goto fail;
> + }
> +
> + ret = 0;
> +fail:
> + g_free(transfer_buf);
> + if (write_fd >= 0) {
> + close(write_fd);
> + }
> +
> + return ret;
> +}
> --
> 2.41.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
WARNING: multiple messages have this Message-ID (diff)
From: Stefan Hajnoczi <stefanha@redhat.com>
To: Hanna Czenczek <hreitz@redhat.com>
Cc: qemu-devel@nongnu.org, virtio-fs@redhat.com,
"Michael S . Tsirkin" <mst@redhat.com>,
"German Maglione" <gmaglione@redhat.com>,
"Eugenio Pérez" <eperezma@redhat.com>
Subject: Re: [PATCH v2 3/4] vhost: Add high-level state save/load functions
Date: Tue, 18 Jul 2023 14:42:12 -0400 [thread overview]
Message-ID: <20230718184212.GM44841@fedora> (raw)
In-Reply-To: <20230712111703.28031-4-hreitz@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 9734 bytes --]
On Wed, Jul 12, 2023 at 01:17:01PM +0200, Hanna Czenczek wrote:
> vhost_save_backend_state() and vhost_load_backend_state() can be used by
> vhost front-ends to easily save and load the back-end's state to/from
> the migration stream.
>
> Because we do not know the full state size ahead of time,
> vhost_save_backend_state() simply reads the data in 1 MB chunks, and
> writes each chunk consecutively into the migration stream, prefixed by
> its length. EOF is indicated by a 0-length chunk.
>
> Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
> ---
> include/hw/virtio/vhost.h | 35 +++++++
> hw/virtio/vhost.c | 204 ++++++++++++++++++++++++++++++++++++++
> 2 files changed, 239 insertions(+)
>
> diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
> index d8877496e5..0c282abd4e 100644
> --- a/include/hw/virtio/vhost.h
> +++ b/include/hw/virtio/vhost.h
> @@ -425,4 +425,39 @@ int vhost_set_device_state_fd(struct vhost_dev *dev,
> */
> int vhost_check_device_state(struct vhost_dev *dev, Error **errp);
>
> +/**
> + * vhost_save_backend_state(): High-level function to receive a vhost
> + * back-end's state, and save it in `f`. Uses
I think the GtkDoc syntax is @f instead of `f`.
> + * `vhost_set_device_state_fd()` to get the data from the back-end, and
> + * stores it in consecutive chunks that are each prefixed by their
> + * respective length (be32). The end is marked by a 0-length chunk.
> + *
> + * Must only be called while the device and all its vrings are stopped
> + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`).
> + *
> + * @dev: The vhost device from which to save the state
> + * @f: Migration stream in which to save the state
> + * @errp: Potential error message
> + *
> + * Returns 0 on success, and -errno otherwise.
> + */
> +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp);
> +
> +/**
> + * vhost_load_backend_state(): High-level function to load a vhost
> + * back-end's state from `f`, and send it over to the back-end. Reads
> + * the data from `f` in the format used by `vhost_save_state()`, and
> + * uses `vhost_set_device_state_fd()` to transfer it to the back-end.
> + *
> + * Must only be called while the device and all its vrings are stopped
> + * (`VHOST_TRANSFER_STATE_PHASE_STOPPED`).
> + *
> + * @dev: The vhost device to which to send the sate
> + * @f: Migration stream from which to load the state
> + * @errp: Potential error message
> + *
> + * Returns 0 on success, and -errno otherwise.
> + */
> +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp);
> +
> #endif
> diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
> index 756b6d55a8..332d49a310 100644
> --- a/hw/virtio/vhost.c
> +++ b/hw/virtio/vhost.c
> @@ -2128,3 +2128,207 @@ int vhost_check_device_state(struct vhost_dev *dev, Error **errp)
> "vhost transport does not support migration state transfer");
> return -ENOSYS;
> }
> +
> +int vhost_save_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp)
> +{
> + /* Maximum chunk size in which to transfer the state */
> + const size_t chunk_size = 1 * 1024 * 1024;
> + void *transfer_buf = NULL;
> + g_autoptr(GError) g_err = NULL;
> + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1;
> + int ret;
> +
> + /* [0] for reading (our end), [1] for writing (back-end's end) */
> + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) {
> + error_setg(errp, "Failed to set up state transfer pipe: %s",
> + g_err->message);
> + ret = -EINVAL;
> + goto fail;
> + }
> +
> + read_fd = pipe_fds[0];
> + write_fd = pipe_fds[1];
> +
> + /*
> + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped.
> + * We cannot check dev->suspended, because the back-end may not support
> + * suspending.
> + */
> + assert(!dev->started);
> +
> + /* Transfer ownership of write_fd to the back-end */
> + ret = vhost_set_device_state_fd(dev,
> + VHOST_TRANSFER_STATE_DIRECTION_SAVE,
> + VHOST_TRANSFER_STATE_PHASE_STOPPED,
> + write_fd,
> + &reply_fd,
> + errp);
> + if (ret < 0) {
> + error_prepend(errp, "Failed to initiate state transfer: ");
> + goto fail;
> + }
> +
> + /* If the back-end wishes to use a different pipe, switch over */
> + if (reply_fd >= 0) {
> + close(read_fd);
> + read_fd = reply_fd;
> + }
> +
> + transfer_buf = g_malloc(chunk_size);
> +
> + while (true) {
> + ssize_t read_ret;
> +
> + read_ret = read(read_fd, transfer_buf, chunk_size);
> + if (read_ret < 0) {
Is it necessary to handle -EINTR?
> + ret = -errno;
> + error_setg_errno(errp, -ret, "Failed to receive state");
> + goto fail;
> + }
> +
> + assert(read_ret <= chunk_size);
> + qemu_put_be32(f, read_ret);
> +
> + if (read_ret == 0) {
> + /* EOF */
> + break;
> + }
> +
> + qemu_put_buffer(f, transfer_buf, read_ret);
> + }
> +
> + /*
> + * Back-end will not really care, but be clean and close our end of the pipe
> + * before inquiring the back-end about whether transfer was successful
> + */
> + close(read_fd);
> + read_fd = -1;
> +
> + /* Also, verify that the device is still stopped */
> + assert(!dev->started);
> +
> + ret = vhost_check_device_state(dev, errp);
> + if (ret < 0) {
> + goto fail;
> + }
> +
> + ret = 0;
> +fail:
> + g_free(transfer_buf);
> + if (read_fd >= 0) {
> + close(read_fd);
> + }
> +
> + return ret;
> +}
> +
> +int vhost_load_backend_state(struct vhost_dev *dev, QEMUFile *f, Error **errp)
> +{
> + size_t transfer_buf_size = 0;
> + void *transfer_buf = NULL;
> + g_autoptr(GError) g_err = NULL;
> + int pipe_fds[2], read_fd = -1, write_fd = -1, reply_fd = -1;
> + int ret;
> +
> + /* [0] for reading (back-end's end), [1] for writing (our end) */
> + if (!g_unix_open_pipe(pipe_fds, FD_CLOEXEC, &g_err)) {
> + error_setg(errp, "Failed to set up state transfer pipe: %s",
> + g_err->message);
> + ret = -EINVAL;
> + goto fail;
> + }
> +
> + read_fd = pipe_fds[0];
> + write_fd = pipe_fds[1];
> +
> + /*
> + * VHOST_TRANSFER_STATE_PHASE_STOPPED means the device must be stopped.
> + * We cannot check dev->suspended, because the back-end may not support
> + * suspending.
> + */
> + assert(!dev->started);
> +
> + /* Transfer ownership of read_fd to the back-end */
> + ret = vhost_set_device_state_fd(dev,
> + VHOST_TRANSFER_STATE_DIRECTION_LOAD,
> + VHOST_TRANSFER_STATE_PHASE_STOPPED,
> + read_fd,
> + &reply_fd,
> + errp);
> + if (ret < 0) {
> + error_prepend(errp, "Failed to initiate state transfer: ");
> + goto fail;
> + }
> +
> + /* If the back-end wishes to use a different pipe, switch over */
> + if (reply_fd >= 0) {
> + close(write_fd);
> + write_fd = reply_fd;
> + }
> +
> + while (true) {
> + size_t this_chunk_size = qemu_get_be32(f);
> + ssize_t write_ret;
> + const uint8_t *transfer_pointer;
> +
> + if (this_chunk_size == 0) {
> + /* End of state */
> + break;
> + }
> +
> + if (transfer_buf_size < this_chunk_size) {
> + transfer_buf = g_realloc(transfer_buf, this_chunk_size);
> + transfer_buf_size = this_chunk_size;
> + }
> +
> + if (qemu_get_buffer(f, transfer_buf, this_chunk_size) <
> + this_chunk_size)
> + {
> + error_setg(errp, "Failed to read state");
> + ret = -EINVAL;
> + goto fail;
> + }
> +
> + transfer_pointer = transfer_buf;
> + while (this_chunk_size > 0) {
> + write_ret = write(write_fd, transfer_pointer, this_chunk_size);
> + if (write_ret < 0) {
Is it necessary to handle -EINTR?
> + ret = -errno;
> + error_setg_errno(errp, -ret, "Failed to send state");
> + goto fail;
> + } else if (write_ret == 0) {
> + error_setg(errp, "Failed to send state: Connection is closed");
> + ret = -ECONNRESET;
> + goto fail;
> + }
> +
> + assert(write_ret <= this_chunk_size);
> + this_chunk_size -= write_ret;
> + transfer_pointer += write_ret;
> + }
> + }
> +
> + /*
> + * Close our end, thus ending transfer, before inquiring the back-end about
> + * whether transfer was successful
> + */
> + close(write_fd);
> + write_fd = -1;
> +
> + /* Also, verify that the device is still stopped */
> + assert(!dev->started);
> +
> + ret = vhost_check_device_state(dev, errp);
> + if (ret < 0) {
> + goto fail;
> + }
> +
> + ret = 0;
> +fail:
> + g_free(transfer_buf);
> + if (write_fd >= 0) {
> + close(write_fd);
> + }
> +
> + return ret;
> +}
> --
> 2.41.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
next prev parent reply other threads:[~2023-07-18 18:42 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-07-12 11:16 [Virtio-fs] [PATCH v2 0/4] vhost-user: Back-end state migration Hanna Czenczek
2023-07-12 11:16 ` Hanna Czenczek
2023-07-12 11:16 ` [Virtio-fs] [PATCH v2 1/4] vhost-user.rst: Migrating back-end-internal state Hanna Czenczek
2023-07-12 11:16 ` Hanna Czenczek
2023-07-18 15:57 ` [Virtio-fs] " Stefan Hajnoczi
2023-07-18 15:57 ` Stefan Hajnoczi
2023-07-19 16:33 ` [Virtio-fs] " Hanna Czenczek
2023-07-19 16:33 ` Hanna Czenczek
2023-07-20 11:43 ` [Virtio-fs] " Stefan Hajnoczi
2023-07-20 11:43 ` Stefan Hajnoczi
2023-07-18 16:12 ` [Virtio-fs] " Stefan Hajnoczi
2023-07-18 16:12 ` Stefan Hajnoczi
2023-07-20 11:32 ` [Virtio-fs] " Hanna Czenczek
2023-07-12 11:17 ` [Virtio-fs] [PATCH v2 2/4] vhost-user: Interface for migration state transfer Hanna Czenczek
2023-07-12 11:17 ` Hanna Czenczek
2023-07-18 18:32 ` [Virtio-fs] " Stefan Hajnoczi
2023-07-18 18:32 ` Stefan Hajnoczi
2023-07-20 12:13 ` [Virtio-fs] " Hao Xu
2023-07-20 13:20 ` Hanna Czenczek
2023-07-20 15:05 ` Hao Xu
2023-07-21 8:07 ` Hanna Czenczek
2023-07-21 8:23 ` Hao Xu
2023-07-12 11:17 ` [Virtio-fs] [PATCH v2 3/4] vhost: Add high-level state save/load functions Hanna Czenczek
2023-07-12 11:17 ` Hanna Czenczek
2023-07-18 18:42 ` Stefan Hajnoczi [this message]
2023-07-18 18:42 ` Stefan Hajnoczi
2023-07-20 11:42 ` [Virtio-fs] " Hanna Czenczek
2023-07-20 11:42 ` Hanna Czenczek
2023-07-21 15:18 ` [Virtio-fs] " Eugenio Perez Martin
2023-07-21 15:18 ` Eugenio Perez Martin
2023-07-21 16:09 ` [Virtio-fs] " Hanna Czenczek
2023-07-21 16:09 ` Hanna Czenczek
2023-07-12 11:17 ` [Virtio-fs] [PATCH v2 4/4] vhost-user-fs: Implement internal migration Hanna Czenczek
2023-07-12 11:17 ` Hanna Czenczek
2023-07-18 18:44 ` [Virtio-fs] " Stefan Hajnoczi
2023-07-18 18:44 ` Stefan Hajnoczi
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=20230718184212.GM44841@fedora \
--to=stefanha@redhat.com \
--cc=eperezma@redhat.com \
--cc=gmaglione@redhat.com \
--cc=hreitz@redhat.com \
--cc=mst@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=virtio-fs@redhat.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.