qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
To: qemu-block@nongnu.org, Stefan Hajnoczi <stefanha@redhat.com>,
	qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Hanna Reitz <hreitz@redhat.com>,
	"Michael S. Tsirkin" <mst@redhat.com>,
	Michael Roth <michael.roth@amd.com>,
	Markus Armbruster <armbru@redhat.com>,
	Stefan Hajnoczi <stefanha@redhat.com>,
	qemu-block@nongnu.org
Subject: Re: [PATCH 1/5] virtio-blk: enforce iothread-vq-mapping validation
Date: Tue, 06 Feb 2024 09:29:29 +0200	[thread overview]
Message-ID: <8fbve.tkrjtk9401p1@linaro.org> (raw)
In-Reply-To: <20240205172659.476970-2-stefanha@redhat.com>

On Mon, 05 Feb 2024 19:26, Stefan Hajnoczi <stefanha@redhat.com> wrote:
>Hanna Czenczek <hreitz@redhat.com> noticed that the safety of
>`vq_aio_context[vq->value] = ctx;` with user-defined vq->value inputs is
>not obvious.
>
>The code is structured in validate() + apply() steps so input validation
>is there, but it happens way earlier and there is nothing that
>guarantees apply() can only be called with validated inputs.
>
>This patch moves the validate() call inside the apply() function so
>validation is guaranteed. I also added the bounds checking assertion
>that Hanna suggested.
>
>Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>---
> hw/block/virtio-blk.c | 192 +++++++++++++++++++++++-------------------
> 1 file changed, 107 insertions(+), 85 deletions(-)
>
>diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
>index 227d83569f..e8b37fd5f4 100644
>--- a/hw/block/virtio-blk.c
>+++ b/hw/block/virtio-blk.c
>@@ -1485,6 +1485,72 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f,
>     return 0;
> }
> 
>+static void virtio_resize_cb(void *opaque)
>+{
>+    VirtIODevice *vdev = opaque;
>+
>+    assert(qemu_get_current_aio_context() == qemu_get_aio_context());
>+    virtio_notify_config(vdev);
>+}
>+
>+static void virtio_blk_resize(void *opaque)
>+{
>+    VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
>+
>+    /*
>+     * virtio_notify_config() needs to acquire the BQL,
>+     * so it can't be called from an iothread. Instead, schedule
>+     * it to be run in the main context BH.
>+     */
>+    aio_bh_schedule_oneshot(qemu_get_aio_context(), virtio_resize_cb, vdev);
>+}
>+
>+static void virtio_blk_ioeventfd_detach(VirtIOBlock *s)
>+{
>+    VirtIODevice *vdev = VIRTIO_DEVICE(s);
>+
>+    for (uint16_t i = 0; i < s->conf.num_queues; i++) {
>+        VirtQueue *vq = virtio_get_queue(vdev, i);
>+        virtio_queue_aio_detach_host_notifier(vq, s->vq_aio_context[i]);
>+    }
>+}
>+
>+static void virtio_blk_ioeventfd_attach(VirtIOBlock *s)
>+{
>+    VirtIODevice *vdev = VIRTIO_DEVICE(s);
>+
>+    for (uint16_t i = 0; i < s->conf.num_queues; i++) {
>+        VirtQueue *vq = virtio_get_queue(vdev, i);
>+        virtio_queue_aio_attach_host_notifier(vq, s->vq_aio_context[i]);
>+    }
>+}
>+
>+/* Suspend virtqueue ioeventfd processing during drain */
>+static void virtio_blk_drained_begin(void *opaque)
>+{
>+    VirtIOBlock *s = opaque;
>+
>+    if (s->ioeventfd_started) {
>+        virtio_blk_ioeventfd_detach(s);
>+    }
>+}
>+
>+/* Resume virtqueue ioeventfd processing after drain */
>+static void virtio_blk_drained_end(void *opaque)
>+{
>+    VirtIOBlock *s = opaque;
>+
>+    if (s->ioeventfd_started) {
>+        virtio_blk_ioeventfd_attach(s);
>+    }
>+}
>+
>+static const BlockDevOps virtio_block_ops = {
>+    .resize_cb     = virtio_blk_resize,
>+    .drained_begin = virtio_blk_drained_begin,
>+    .drained_end   = virtio_blk_drained_end,
>+};
>+
> static bool
> validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list,
>         uint16_t num_queues, Error **errp)
>@@ -1547,81 +1613,33 @@ validate_iothread_vq_mapping_list(IOThreadVirtQueueMappingList *list,
>     return true;
> }
> 
>-static void virtio_resize_cb(void *opaque)
>-{
>-    VirtIODevice *vdev = opaque;
>-
>-    assert(qemu_get_current_aio_context() == qemu_get_aio_context());
>-    virtio_notify_config(vdev);
>-}
>-
>-static void virtio_blk_resize(void *opaque)
>-{
>-    VirtIODevice *vdev = VIRTIO_DEVICE(opaque);
>-
>-    /*
>-     * virtio_notify_config() needs to acquire the BQL,
>-     * so it can't be called from an iothread. Instead, schedule
>-     * it to be run in the main context BH.
>-     */
>-    aio_bh_schedule_oneshot(qemu_get_aio_context(), virtio_resize_cb, vdev);
>-}
>-
>-static void virtio_blk_ioeventfd_detach(VirtIOBlock *s)
>-{
>-    VirtIODevice *vdev = VIRTIO_DEVICE(s);
>-
>-    for (uint16_t i = 0; i < s->conf.num_queues; i++) {
>-        VirtQueue *vq = virtio_get_queue(vdev, i);
>-        virtio_queue_aio_detach_host_notifier(vq, s->vq_aio_context[i]);
>-    }
>-}
>-
>-static void virtio_blk_ioeventfd_attach(VirtIOBlock *s)
>-{
>-    VirtIODevice *vdev = VIRTIO_DEVICE(s);
>-
>-    for (uint16_t i = 0; i < s->conf.num_queues; i++) {
>-        VirtQueue *vq = virtio_get_queue(vdev, i);
>-        virtio_queue_aio_attach_host_notifier(vq, s->vq_aio_context[i]);
>-    }
>-}
>-
>-/* Suspend virtqueue ioeventfd processing during drain */
>-static void virtio_blk_drained_begin(void *opaque)
>-{
>-    VirtIOBlock *s = opaque;
>-
>-    if (s->ioeventfd_started) {
>-        virtio_blk_ioeventfd_detach(s);
>-    }
>-}
>-
>-/* Resume virtqueue ioeventfd processing after drain */
>-static void virtio_blk_drained_end(void *opaque)
>-{
>-    VirtIOBlock *s = opaque;
>-
>-    if (s->ioeventfd_started) {
>-        virtio_blk_ioeventfd_attach(s);
>-    }
>-}
>-
>-static const BlockDevOps virtio_block_ops = {
>-    .resize_cb     = virtio_blk_resize,
>-    .drained_begin = virtio_blk_drained_begin,
>-    .drained_end   = virtio_blk_drained_end,
>-};
>-
>-/* Generate vq:AioContext mappings from a validated iothread-vq-mapping list */
>-static void
>-apply_vq_mapping(IOThreadVirtQueueMappingList *iothread_vq_mapping_list,
>-                 AioContext **vq_aio_context, uint16_t num_queues)
>+/**
>+ * apply_iothread_vq_mapping:
>+ * @iothread_vq_mapping_list: The mapping of virtqueues to IOThreads.
>+ * @vq_aio_context: The array of AioContext pointers to fill in.
>+ * @num_queues: The length of @vq_aio_context.
>+ * @errp: If an error occurs, a pointer to the area to store the error.
>+ *
>+ * Fill in the AioContext for each virtqueue in the @vq_aio_context array given
>+ * the iothread-vq-mapping parameter in @iothread_vq_mapping_list.
>+ *
>+ * Returns: %true on success, %false on failure.
>+ **/
>+static bool apply_iothread_vq_mapping(
>+        IOThreadVirtQueueMappingList *iothread_vq_mapping_list,
>+        AioContext **vq_aio_context,
>+        uint16_t num_queues,
>+        Error **errp)
> {
>     IOThreadVirtQueueMappingList *node;
>     size_t num_iothreads = 0;
>     size_t cur_iothread = 0;
> 
>+    if (!validate_iothread_vq_mapping_list(iothread_vq_mapping_list,
>+                                           num_queues, errp)) {
>+        return false;
>+    }
>+
>     for (node = iothread_vq_mapping_list; node; node = node->next) {
>         num_iothreads++;
>     }
>@@ -1638,6 +1656,7 @@ apply_vq_mapping(IOThreadVirtQueueMappingList *iothread_vq_mapping_list,
> 
>             /* Explicit vq:IOThread assignment */
>             for (vq = node->value->vqs; vq; vq = vq->next) {
>+                assert(vq->value < num_queues);
>                 vq_aio_context[vq->value] = ctx;
>             }
>         } else {
>@@ -1650,6 +1669,8 @@ apply_vq_mapping(IOThreadVirtQueueMappingList *iothread_vq_mapping_list,
> 
>         cur_iothread++;
>     }
>+
>+    return true;
> }
> 
> /* Context: BQL held */
>@@ -1660,6 +1681,14 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
>     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
>     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
> 
>+    if (conf->iothread && conf->iothread_vq_mapping_list) {
>+        if (conf->iothread) {
>+            error_setg(errp, "iothread and iothread-vq-mapping properties "
>+                             "cannot be set at the same time");
>+            return false;
>+        }
>+    }
>+
>     if (conf->iothread || conf->iothread_vq_mapping_list) {
>         if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
>             error_setg(errp,
>@@ -1685,8 +1714,14 @@ static bool virtio_blk_vq_aio_context_init(VirtIOBlock *s, Error **errp)
>     s->vq_aio_context = g_new(AioContext *, conf->num_queues);
> 
>     if (conf->iothread_vq_mapping_list) {
>-        apply_vq_mapping(conf->iothread_vq_mapping_list, s->vq_aio_context,
>-                         conf->num_queues);
>+        if (!apply_iothread_vq_mapping(conf->iothread_vq_mapping_list,
>+                                       s->vq_aio_context,
>+                                       conf->num_queues,
>+                                       errp)) {
>+            g_free(s->vq_aio_context);
>+            s->vq_aio_context = NULL;
>+            return false;
>+        }
>     } else if (conf->iothread) {
>         AioContext *ctx = iothread_get_aio_context(conf->iothread);
>         for (unsigned i = 0; i < conf->num_queues; i++) {
>@@ -1996,19 +2031,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
>         return;
>     }
> 
>-    if (conf->iothread_vq_mapping_list) {
>-        if (conf->iothread) {
>-            error_setg(errp, "iothread and iothread-vq-mapping properties "
>-                             "cannot be set at the same time");
>-            return;
>-        }
>-
>-        if (!validate_iothread_vq_mapping_list(conf->iothread_vq_mapping_list,
>-                                               conf->num_queues, errp)) {
>-            return;
>-        }
>-    }
>-
>     s->config_size = virtio_get_config_size(&virtio_blk_cfg_size_params,
>                                             s->host_features);
>     virtio_init(vdev, VIRTIO_ID_BLOCK, s->config_size);
>-- 
>2.43.0
>
>


virtio_block_ops and methods are moved around without changes in the 
diff, is that on purpose? If no the patch and history would be less 
noisy.


Regardless:

Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>


  reply	other threads:[~2024-02-06  7:39 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-05 17:26 [PATCH 0/5] virtio-blk: iothread-vq-mapping cleanups Stefan Hajnoczi
2024-02-05 17:26 ` [PATCH 1/5] virtio-blk: enforce iothread-vq-mapping validation Stefan Hajnoczi
2024-02-06  7:29   ` Manos Pitsidianakis [this message]
2024-02-06 15:18     ` Stefan Hajnoczi
2024-02-06 15:07   ` Hanna Czenczek
2024-02-05 17:26 ` [PATCH 2/5] virtio-blk: clarify that there is at least 1 virtqueue Stefan Hajnoczi
2024-02-06  7:23   ` Manos Pitsidianakis
2024-02-06 15:08   ` Hanna Czenczek
2024-02-05 17:26 ` [PATCH 3/5] virtio-blk: add vq_rq[] bounds check in virtio_blk_dma_restart_cb() Stefan Hajnoczi
2024-02-06  7:20   ` Manos Pitsidianakis
2024-02-06 15:09   ` Hanna Czenczek
2024-02-05 17:26 ` [PATCH 4/5] virtio-blk: declare VirtIOBlock::rq with a type Stefan Hajnoczi
2024-02-06  7:16   ` Manos Pitsidianakis
2024-02-06 15:10   ` Hanna Czenczek
2024-02-05 17:26 ` [PATCH 5/5] monitor: use aio_co_reschedule_self() Stefan Hajnoczi
2024-02-06  7:28   ` Manos Pitsidianakis
2024-02-06 15:11   ` Hanna Czenczek
2024-02-07  7:04   ` Markus Armbruster

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=8fbve.tkrjtk9401p1@linaro.org \
    --to=manos.pitsidianakis@linaro.org \
    --cc=armbru@redhat.com \
    --cc=hreitz@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=michael.roth@amd.com \
    --cc=mst@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).