]>
Commit | Line | Data |
---|---|---|
4ff04bdf TL |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Hanna Czenczek <hreitz@redhat.com> | |
3 | Date: Fri, 2 Feb 2024 16:31:57 +0100 | |
4 | Subject: [PATCH] virtio: Re-enable notifications after drain | |
5 | ||
6 | During drain, we do not care about virtqueue notifications, which is why | |
7 | we remove the handlers on it. When removing those handlers, whether vq | |
8 | notifications are enabled or not depends on whether we were in polling | |
9 | mode or not; if not, they are enabled (by default); if so, they have | |
10 | been disabled by the io_poll_start callback. | |
11 | ||
12 | Because we do not care about those notifications after removing the | |
13 | handlers, this is fine. However, we have to explicitly ensure they are | |
14 | enabled when re-attaching the handlers, so we will resume receiving | |
15 | notifications. We do this in virtio_queue_aio_attach_host_notifier*(). | |
16 | If such a function is called while we are in a polling section, | |
17 | attaching the notifiers will then invoke the io_poll_start callback, | |
18 | re-disabling notifications. | |
19 | ||
20 | Because we will always miss virtqueue updates in the drained section, we | |
21 | also need to poll the virtqueue once after attaching the notifiers. | |
22 | ||
23 | Buglink: https://issues.redhat.com/browse/RHEL-3934 | |
24 | Signed-off-by: Hanna Czenczek <hreitz@redhat.com> | |
25 | Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> | |
26 | --- | |
27 | hw/virtio/virtio.c | 42 ++++++++++++++++++++++++++++++++++++++++++ | |
28 | include/block/aio.h | 7 ++++++- | |
29 | 2 files changed, 48 insertions(+), 1 deletion(-) | |
30 | ||
31 | diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c | |
32 | index 969c25f4cf..02cce83111 100644 | |
33 | --- a/hw/virtio/virtio.c | |
34 | +++ b/hw/virtio/virtio.c | |
35 | @@ -3526,6 +3526,17 @@ static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n) | |
36 | ||
37 | void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) | |
38 | { | |
39 | + /* | |
40 | + * virtio_queue_aio_detach_host_notifier() can leave notifications disabled. | |
41 | + * Re-enable them. (And if detach has not been used before, notifications | |
42 | + * being enabled is still the default state while a notifier is attached; | |
43 | + * see virtio_queue_host_notifier_aio_poll_end(), which will always leave | |
44 | + * notifications enabled once the polling section is left.) | |
45 | + */ | |
46 | + if (!virtio_queue_get_notification(vq)) { | |
47 | + virtio_queue_set_notification(vq, 1); | |
48 | + } | |
49 | + | |
50 | aio_set_event_notifier(ctx, &vq->host_notifier, | |
51 | virtio_queue_host_notifier_read, | |
52 | virtio_queue_host_notifier_aio_poll, | |
53 | @@ -3533,6 +3544,13 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) | |
54 | aio_set_event_notifier_poll(ctx, &vq->host_notifier, | |
55 | virtio_queue_host_notifier_aio_poll_begin, | |
56 | virtio_queue_host_notifier_aio_poll_end); | |
57 | + | |
58 | + /* | |
59 | + * We will have ignored notifications about new requests from the guest | |
60 | + * while no notifiers were attached, so "kick" the virt queue to process | |
61 | + * those requests now. | |
62 | + */ | |
63 | + event_notifier_set(&vq->host_notifier); | |
64 | } | |
65 | ||
66 | /* | |
67 | @@ -3543,14 +3561,38 @@ void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx) | |
68 | */ | |
69 | void virtio_queue_aio_attach_host_notifier_no_poll(VirtQueue *vq, AioContext *ctx) | |
70 | { | |
71 | + /* See virtio_queue_aio_attach_host_notifier() */ | |
72 | + if (!virtio_queue_get_notification(vq)) { | |
73 | + virtio_queue_set_notification(vq, 1); | |
74 | + } | |
75 | + | |
76 | aio_set_event_notifier(ctx, &vq->host_notifier, | |
77 | virtio_queue_host_notifier_read, | |
78 | NULL, NULL); | |
79 | + | |
80 | + /* | |
81 | + * See virtio_queue_aio_attach_host_notifier(). | |
82 | + * Note that this may be unnecessary for the type of virtqueues this | |
83 | + * function is used for. Still, it will not hurt to have a quick look into | |
84 | + * whether we can/should process any of the virtqueue elements. | |
85 | + */ | |
86 | + event_notifier_set(&vq->host_notifier); | |
87 | } | |
88 | ||
89 | void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx) | |
90 | { | |
91 | aio_set_event_notifier(ctx, &vq->host_notifier, NULL, NULL, NULL); | |
92 | + | |
93 | + /* | |
94 | + * aio_set_event_notifier_poll() does not guarantee whether io_poll_end() | |
95 | + * will run after io_poll_begin(), so by removing the notifier, we do not | |
96 | + * know whether virtio_queue_host_notifier_aio_poll_end() has run after a | |
97 | + * previous virtio_queue_host_notifier_aio_poll_begin(), i.e. whether | |
98 | + * notifications are enabled or disabled. It does not really matter anyway; | |
99 | + * we just removed the notifier, so we do not care about notifications until | |
100 | + * we potentially re-attach it. The attach_host_notifier functions will | |
101 | + * ensure that notifications are enabled again when they are needed. | |
102 | + */ | |
103 | } | |
104 | ||
105 | void virtio_queue_host_notifier_read(EventNotifier *n) | |
106 | diff --git a/include/block/aio.h b/include/block/aio.h | |
107 | index 32042e8905..79efadfa48 100644 | |
108 | --- a/include/block/aio.h | |
109 | +++ b/include/block/aio.h | |
110 | @@ -498,9 +498,14 @@ void aio_set_event_notifier(AioContext *ctx, | |
111 | AioPollFn *io_poll, | |
112 | EventNotifierHandler *io_poll_ready); | |
113 | ||
114 | -/* Set polling begin/end callbacks for an event notifier that has already been | |
115 | +/* | |
116 | + * Set polling begin/end callbacks for an event notifier that has already been | |
117 | * registered with aio_set_event_notifier. Do nothing if the event notifier is | |
118 | * not registered. | |
119 | + * | |
120 | + * Note that if the io_poll_end() callback (or the entire notifier) is removed | |
121 | + * during polling, it will not be called, so an io_poll_begin() is not | |
122 | + * necessarily always followed by an io_poll_end(). | |
123 | */ | |
124 | void aio_set_event_notifier_poll(AioContext *ctx, | |
125 | EventNotifier *notifier, |