1
The following changes since commit d48125de38f48a61d6423ef6a01156d6dff9ee2c:
1
The following changes since commit 352998df1c53b366413690d95b35f76d0721ebed:
2
2
3
Merge tag 'kraxel-20220719-pull-request' of https://gitlab.com/kraxel/qemu into staging (2022-07-19 17:40:36 +0100)
3
Merge tag 'i2c-20220314' of https://github.com/philmd/qemu into staging (2022-03-14 14:39:33 +0000)
4
4
5
are available in the git repository at:
5
are available in the git repository at:
6
6
7
https://github.com/jasowang/qemu.git tags/net-pull-request
7
https://github.com/jasowang/qemu.git tags/net-pull-request
8
8
9
for you to fetch changes up to 8bdab83b34efb0b598be4e5b98e4f466ca5f2f80:
9
for you to fetch changes up to 12a195fa343aae2ead1301ce04727bd0ae25eb15:
10
10
11
net/colo.c: fix segmentation fault when packet is not parsed correctly (2022-07-20 16:58:08 +0800)
11
vdpa: Expose VHOST_F_LOG_ALL on SVQ (2022-03-15 13:57:44 +0800)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
14
15
Changes since V1:
15
Changes since V2:
16
- Fix build erros of vhost-vdpa when virtio-net is not set
16
- fix 32bit build errros
17
17
18
----------------------------------------------------------------
18
----------------------------------------------------------------
19
Eugenio Pérez (21):
19
Eugenio Pérez (14):
20
vhost: move descriptor translation to vhost_svq_vring_write_descs
20
vhost: Add VhostShadowVirtqueue
21
virtio-net: Expose MAC_TABLE_ENTRIES
21
vhost: Add Shadow VirtQueue kick forwarding capabilities
22
virtio-net: Expose ctrl virtqueue logic
22
vhost: Add Shadow VirtQueue call forwarding capabilities
23
vdpa: Avoid compiler to squash reads to used idx
23
vhost: Add vhost_svq_valid_features to shadow vq
24
vhost: Reorder vhost_svq_kick
24
virtio: Add vhost_svq_get_vring_addr
25
vhost: Move vhost_svq_kick call to vhost_svq_add
25
vdpa: adapt vhost_ops callbacks to svq
26
vhost: Check for queue full at vhost_svq_add
26
vhost: Shadow virtqueue buffers forwarding
27
vhost: Decouple vhost_svq_add from VirtQueueElement
27
util: Add iova_tree_alloc_map
28
vhost: Add SVQDescState
28
util: add iova_tree_find_iova
29
vhost: Track number of descs in SVQDescState
29
vhost: Add VhostIOVATree
30
vhost: add vhost_svq_push_elem
30
vdpa: Add custom IOTLB translations to SVQ
31
vhost: Expose vhost_svq_add
31
vdpa: Adapt vhost_vdpa_get_vring_base to SVQ
32
vhost: add vhost_svq_poll
32
vdpa: Never set log_base addr if SVQ is enabled
33
vhost: Add svq avail_handler callback
33
vdpa: Expose VHOST_F_LOG_ALL on SVQ
34
vdpa: Export vhost_vdpa_dma_map and unmap calls
35
vhost-net-vdpa: add stubs for when no virtio-net device is present
36
vdpa: manual forward CVQ buffers
37
vdpa: Buffer CVQ support on shadow virtqueue
38
vdpa: Extract get features part from vhost_vdpa_get_max_queue_pairs
39
vdpa: Add device migration blocker
40
vdpa: Add x-svq to NetdevVhostVDPAOptions
41
34
42
Zhang Chen (4):
35
Jason Wang (1):
43
softmmu/runstate.c: add RunStateTransition support form COLO to PRELAUNCH
36
virtio-net: fix map leaking on error during receive
44
net/colo: Fix a "double free" crash to clear the conn_list
45
net/colo.c: No need to track conn_list for filter-rewriter
46
net/colo.c: fix segmentation fault when packet is not parsed correctly
47
37
48
hw/net/virtio-net.c | 85 +++++----
38
hw/net/virtio-net.c | 1 +
49
hw/virtio/vhost-shadow-virtqueue.c | 210 +++++++++++++++-------
39
hw/virtio/meson.build | 2 +-
50
hw/virtio/vhost-shadow-virtqueue.h | 52 +++++-
40
hw/virtio/vhost-iova-tree.c | 110 +++++++
51
hw/virtio/vhost-vdpa.c | 26 ++-
41
hw/virtio/vhost-iova-tree.h | 27 ++
42
hw/virtio/vhost-shadow-virtqueue.c | 636 +++++++++++++++++++++++++++++++++++++
43
hw/virtio/vhost-shadow-virtqueue.h | 87 +++++
44
hw/virtio/vhost-vdpa.c | 522 +++++++++++++++++++++++++++++-
52
include/hw/virtio/vhost-vdpa.h | 8 +
45
include/hw/virtio/vhost-vdpa.h | 8 +
53
include/hw/virtio/virtio-net.h | 7 +
46
include/qemu/iova-tree.h | 38 ++-
54
net/colo-compare.c | 2 +-
47
util/iova-tree.c | 170 ++++++++++
55
net/colo.c | 11 +-
48
10 files changed, 1584 insertions(+), 17 deletions(-)
56
net/filter-rewriter.c | 2 +-
49
create mode 100644 hw/virtio/vhost-iova-tree.c
57
net/meson.build | 3 +-
50
create mode 100644 hw/virtio/vhost-iova-tree.h
58
net/trace-events | 1 +
51
create mode 100644 hw/virtio/vhost-shadow-virtqueue.c
59
net/vhost-vdpa-stub.c | 21 +++
52
create mode 100644 hw/virtio/vhost-shadow-virtqueue.h
60
net/vhost-vdpa.c | 357 +++++++++++++++++++++++++++++++++++--
61
qapi/net.json | 9 +-
62
softmmu/runstate.c | 1 +
63
15 files changed, 671 insertions(+), 124 deletions(-)
64
create mode 100644 net/vhost-vdpa-stub.c
65
53
66
54
67
55
diff view generated by jsdifflib
Deleted patch
1
From: Eugenio Pérez <eperezma@redhat.com>
2
1
3
It's done for both in and out descriptors so it's better placed here.
4
5
Acked-by: Jason Wang <jasowang@redhat.com>
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
hw/virtio/vhost-shadow-virtqueue.c | 38 +++++++++++++++++++++++++++-----------
11
1 file changed, 27 insertions(+), 11 deletions(-)
12
13
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/hw/virtio/vhost-shadow-virtqueue.c
16
+++ b/hw/virtio/vhost-shadow-virtqueue.c
17
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
18
return true;
19
}
20
21
-static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
22
- const struct iovec *iovec, size_t num,
23
- bool more_descs, bool write)
24
+/**
25
+ * Write descriptors to SVQ vring
26
+ *
27
+ * @svq: The shadow virtqueue
28
+ * @sg: Cache for hwaddr
29
+ * @iovec: The iovec from the guest
30
+ * @num: iovec length
31
+ * @more_descs: True if more descriptors come in the chain
32
+ * @write: True if they are writeable descriptors
33
+ *
34
+ * Return true if success, false otherwise and print error.
35
+ */
36
+static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
37
+ const struct iovec *iovec, size_t num,
38
+ bool more_descs, bool write)
39
{
40
uint16_t i = svq->free_head, last = svq->free_head;
41
unsigned n;
42
uint16_t flags = write ? cpu_to_le16(VRING_DESC_F_WRITE) : 0;
43
vring_desc_t *descs = svq->vring.desc;
44
+ bool ok;
45
46
if (num == 0) {
47
- return;
48
+ return true;
49
+ }
50
+
51
+ ok = vhost_svq_translate_addr(svq, sg, iovec, num);
52
+ if (unlikely(!ok)) {
53
+ return false;
54
}
55
56
for (n = 0; n < num; n++) {
57
@@ -XXX,XX +XXX,XX @@ static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
58
}
59
60
svq->free_head = le16_to_cpu(svq->desc_next[last]);
61
+ return true;
62
}
63
64
static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
65
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
66
return false;
67
}
68
69
- ok = vhost_svq_translate_addr(svq, sgs, elem->out_sg, elem->out_num);
70
+ ok = vhost_svq_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num,
71
+ elem->in_num > 0, false);
72
if (unlikely(!ok)) {
73
return false;
74
}
75
- vhost_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num,
76
- elem->in_num > 0, false);
77
-
78
79
- ok = vhost_svq_translate_addr(svq, sgs, elem->in_sg, elem->in_num);
80
+ ok = vhost_svq_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false,
81
+ true);
82
if (unlikely(!ok)) {
83
return false;
84
}
85
86
- vhost_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false, true);
87
-
88
/*
89
* Put the entry in the available array (but don't update avail->idx until
90
* they do sync).
91
--
92
2.7.4
93
94
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
Commit bedd7e93d0196 ("virtio-net: fix use after unmap/free for sg")
2
tries to fix the use after free of the sg by caching the virtqueue
3
elements in an array and unmap them at once after receiving the
4
packets, But it forgot to unmap the cached elements on error which
5
will lead to leaking of mapping and other unexpected results.
2
6
3
vhost-vdpa control virtqueue needs to know the maximum entries supported
7
Fixing this by detaching the cached elements on error. This addresses
4
by the virtio-net device, so we know if it is possible to apply the
8
CVE-2022-26353.
5
filter.
6
9
7
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
10
Reported-by: Victor Tom <vv474172261@gmail.com>
11
Cc: qemu-stable@nongnu.org
12
Fixes: CVE-2022-26353
13
Fixes: bedd7e93d0196 ("virtio-net: fix use after unmap/free for sg")
8
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
14
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
16
---
11
hw/net/virtio-net.c | 1 -
17
hw/net/virtio-net.c | 1 +
12
include/hw/virtio/virtio-net.h | 3 +++
18
1 file changed, 1 insertion(+)
13
2 files changed, 3 insertions(+), 1 deletion(-)
14
19
15
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
20
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
16
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
17
--- a/hw/net/virtio-net.c
22
--- a/hw/net/virtio-net.c
18
+++ b/hw/net/virtio-net.c
23
+++ b/hw/net/virtio-net.c
19
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf,
20
25
21
#define VIRTIO_NET_VM_VERSION 11
26
err:
22
27
for (j = 0; j < i; j++) {
23
-#define MAC_TABLE_ENTRIES 64
28
+ virtqueue_detach_element(q->rx_vq, elems[j], lens[j]);
24
#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */
29
g_free(elems[j]);
25
30
}
26
/* previously fixed value */
31
27
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
28
index XXXXXXX..XXXXXXX 100644
29
--- a/include/hw/virtio/virtio-net.h
30
+++ b/include/hw/virtio/virtio-net.h
31
@@ -XXX,XX +XXX,XX @@ OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET)
32
* and latency. */
33
#define TX_BURST 256
34
35
+/* Maximum VIRTIO_NET_CTRL_MAC_TABLE_SET unicast + multicast entries. */
36
+#define MAC_TABLE_ENTRIES 64
37
+
38
typedef struct virtio_net_conf
39
{
40
uint32_t txtimer;
41
--
32
--
42
2.7.4
33
2.7.4
43
44
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
net/vhost-vdpa.c will need functions that are declared in
3
Vhost shadow virtqueue (SVQ) is an intermediate jump for virtqueue
4
vhost-shadow-virtqueue.c, that needs functions of virtio-net.c.
4
notifications and buffers, allowing qemu to track them. While qemu is
5
forwarding the buffers and virtqueue changes, it is able to commit the
6
memory it's being dirtied, the same way regular qemu's VirtIO devices
7
do.
5
8
6
Copy the vhost-vdpa-stub.c code so
9
This commit only exposes basic SVQ allocation and free. Next patches of
7
only the constructor net_init_vhost_vdpa needs to be defined.
10
the series add functionality like notifications and buffers forwarding.
8
11
9
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
12
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
13
Acked-by: Michael S. Tsirkin <mst@redhat.com>
10
Signed-off-by: Jason Wang <jasowang@redhat.com>
14
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
---
15
---
12
net/meson.build | 3 ++-
16
hw/virtio/meson.build | 2 +-
13
net/vhost-vdpa-stub.c | 21 +++++++++++++++++++++
17
hw/virtio/vhost-shadow-virtqueue.c | 62 ++++++++++++++++++++++++++++++++++++++
14
2 files changed, 23 insertions(+), 1 deletion(-)
18
hw/virtio/vhost-shadow-virtqueue.h | 28 +++++++++++++++++
15
create mode 100644 net/vhost-vdpa-stub.c
19
3 files changed, 91 insertions(+), 1 deletion(-)
20
create mode 100644 hw/virtio/vhost-shadow-virtqueue.c
21
create mode 100644 hw/virtio/vhost-shadow-virtqueue.h
16
22
17
diff --git a/net/meson.build b/net/meson.build
23
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
18
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
19
--- a/net/meson.build
25
--- a/hw/virtio/meson.build
20
+++ b/net/meson.build
26
+++ b/hw/virtio/meson.build
21
@@ -XXX,XX +XXX,XX @@ endif
27
@@ -XXX,XX +XXX,XX @@ softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c'))
22
softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files(tap_posix))
28
23
softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('tap-win32.c'))
29
virtio_ss = ss.source_set()
24
if have_vhost_net_vdpa
30
virtio_ss.add(files('virtio.c'))
25
- softmmu_ss.add(files('vhost-vdpa.c'))
31
-virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c'))
26
+ softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c'))
32
+virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c', 'vhost-shadow-virtqueue.c'))
27
+ softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-vdpa-stub.c'))
33
virtio_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user.c'))
28
endif
34
virtio_ss.add(when: 'CONFIG_VHOST_VDPA', if_true: files('vhost-vdpa.c'))
29
35
virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c'))
30
vmnet_files = files(
36
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
31
diff --git a/net/vhost-vdpa-stub.c b/net/vhost-vdpa-stub.c
32
new file mode 100644
37
new file mode 100644
33
index XXXXXXX..XXXXXXX
38
index XXXXXXX..XXXXXXX
34
--- /dev/null
39
--- /dev/null
35
+++ b/net/vhost-vdpa-stub.c
40
+++ b/hw/virtio/vhost-shadow-virtqueue.c
36
@@ -XXX,XX +XXX,XX @@
41
@@ -XXX,XX +XXX,XX @@
37
+/*
42
+/*
38
+ * vhost-vdpa-stub.c
43
+ * vhost shadow virtqueue
39
+ *
44
+ *
40
+ * Copyright (c) 2022 Red Hat, Inc.
45
+ * SPDX-FileCopyrightText: Red Hat, Inc. 2021
46
+ * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
41
+ *
47
+ *
42
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
48
+ * SPDX-License-Identifier: GPL-2.0-or-later
43
+ * See the COPYING file in the top-level directory.
44
+ *
45
+ */
49
+ */
46
+
50
+
47
+#include "qemu/osdep.h"
51
+#include "qemu/osdep.h"
48
+#include "clients.h"
52
+#include "hw/virtio/vhost-shadow-virtqueue.h"
49
+#include "net/vhost-vdpa.h"
50
+#include "qapi/error.h"
51
+
53
+
52
+int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
54
+#include "qemu/error-report.h"
53
+ NetClientState *peer, Error **errp)
55
+
56
+/**
57
+ * Creates vhost shadow virtqueue, and instructs the vhost device to use the
58
+ * shadow methods and file descriptors.
59
+ *
60
+ * Returns the new virtqueue or NULL.
61
+ *
62
+ * In case of error, reason is reported through error_report.
63
+ */
64
+VhostShadowVirtqueue *vhost_svq_new(void)
54
+{
65
+{
55
+ error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*");
66
+ g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1);
56
+ return -1;
67
+ int r;
68
+
69
+ r = event_notifier_init(&svq->hdev_kick, 0);
70
+ if (r != 0) {
71
+ error_report("Couldn't create kick event notifier: %s (%d)",
72
+ g_strerror(errno), errno);
73
+ goto err_init_hdev_kick;
74
+ }
75
+
76
+ r = event_notifier_init(&svq->hdev_call, 0);
77
+ if (r != 0) {
78
+ error_report("Couldn't create call event notifier: %s (%d)",
79
+ g_strerror(errno), errno);
80
+ goto err_init_hdev_call;
81
+ }
82
+
83
+ return g_steal_pointer(&svq);
84
+
85
+err_init_hdev_call:
86
+ event_notifier_cleanup(&svq->hdev_kick);
87
+
88
+err_init_hdev_kick:
89
+ return NULL;
57
+}
90
+}
91
+
92
+/**
93
+ * Free the resources of the shadow virtqueue.
94
+ *
95
+ * @pvq: gpointer to SVQ so it can be used by autofree functions.
96
+ */
97
+void vhost_svq_free(gpointer pvq)
98
+{
99
+ VhostShadowVirtqueue *vq = pvq;
100
+ event_notifier_cleanup(&vq->hdev_kick);
101
+ event_notifier_cleanup(&vq->hdev_call);
102
+ g_free(vq);
103
+}
104
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
105
new file mode 100644
106
index XXXXXXX..XXXXXXX
107
--- /dev/null
108
+++ b/hw/virtio/vhost-shadow-virtqueue.h
109
@@ -XXX,XX +XXX,XX @@
110
+/*
111
+ * vhost shadow virtqueue
112
+ *
113
+ * SPDX-FileCopyrightText: Red Hat, Inc. 2021
114
+ * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
115
+ *
116
+ * SPDX-License-Identifier: GPL-2.0-or-later
117
+ */
118
+
119
+#ifndef VHOST_SHADOW_VIRTQUEUE_H
120
+#define VHOST_SHADOW_VIRTQUEUE_H
121
+
122
+#include "qemu/event_notifier.h"
123
+
124
+/* Shadow virtqueue to relay notifications */
125
+typedef struct VhostShadowVirtqueue {
126
+ /* Shadow kick notifier, sent to vhost */
127
+ EventNotifier hdev_kick;
128
+ /* Shadow call notifier, sent to vhost */
129
+ EventNotifier hdev_call;
130
+} VhostShadowVirtqueue;
131
+
132
+VhostShadowVirtqueue *vhost_svq_new(void);
133
+
134
+void vhost_svq_free(gpointer vq);
135
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free);
136
+
137
+#endif
58
--
138
--
59
2.7.4
139
2.7.4
60
140
61
141
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Do a simple forwarding of CVQ buffers, the same work SVQ could do but
3
At this mode no buffer forwarding will be performed in SVQ mode: Qemu
4
through callbacks. No functional change intended.
4
will just forward the guest's kicks to the device.
5
6
Host memory notifiers regions are left out for simplicity, and they will
7
not be addressed in this series.
5
8
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
9
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
10
Acked-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
12
---
10
hw/virtio/vhost-vdpa.c | 3 ++-
13
hw/virtio/vhost-shadow-virtqueue.c | 55 ++++++++++++++
11
include/hw/virtio/vhost-vdpa.h | 3 +++
14
hw/virtio/vhost-shadow-virtqueue.h | 14 ++++
12
net/vhost-vdpa.c | 58 ++++++++++++++++++++++++++++++++++++++++++
15
hw/virtio/vhost-vdpa.c | 144 ++++++++++++++++++++++++++++++++++++-
13
3 files changed, 63 insertions(+), 1 deletion(-)
16
include/hw/virtio/vhost-vdpa.h | 4 ++
14
17
4 files changed, 215 insertions(+), 2 deletions(-)
18
19
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/virtio/vhost-shadow-virtqueue.c
22
+++ b/hw/virtio/vhost-shadow-virtqueue.c
23
@@ -XXX,XX +XXX,XX @@
24
#include "hw/virtio/vhost-shadow-virtqueue.h"
25
26
#include "qemu/error-report.h"
27
+#include "qemu/main-loop.h"
28
+#include "linux-headers/linux/vhost.h"
29
+
30
+/**
31
+ * Forward guest notifications.
32
+ *
33
+ * @n: guest kick event notifier, the one that guest set to notify svq.
34
+ */
35
+static void vhost_handle_guest_kick(EventNotifier *n)
36
+{
37
+ VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, svq_kick);
38
+ event_notifier_test_and_clear(n);
39
+ event_notifier_set(&svq->hdev_kick);
40
+}
41
+
42
+/**
43
+ * Set a new file descriptor for the guest to kick the SVQ and notify for avail
44
+ *
45
+ * @svq: The svq
46
+ * @svq_kick_fd: The svq kick fd
47
+ *
48
+ * Note that the SVQ will never close the old file descriptor.
49
+ */
50
+void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
51
+{
52
+ EventNotifier *svq_kick = &svq->svq_kick;
53
+ bool poll_stop = VHOST_FILE_UNBIND != event_notifier_get_fd(svq_kick);
54
+ bool poll_start = svq_kick_fd != VHOST_FILE_UNBIND;
55
+
56
+ if (poll_stop) {
57
+ event_notifier_set_handler(svq_kick, NULL);
58
+ }
59
+
60
+ /*
61
+ * event_notifier_set_handler already checks for guest's notifications if
62
+ * they arrive at the new file descriptor in the switch, so there is no
63
+ * need to explicitly check for them.
64
+ */
65
+ if (poll_start) {
66
+ event_notifier_init_fd(svq_kick, svq_kick_fd);
67
+ event_notifier_set(svq_kick);
68
+ event_notifier_set_handler(svq_kick, vhost_handle_guest_kick);
69
+ }
70
+}
71
+
72
+/**
73
+ * Stop the shadow virtqueue operation.
74
+ * @svq: Shadow Virtqueue
75
+ */
76
+void vhost_svq_stop(VhostShadowVirtqueue *svq)
77
+{
78
+ event_notifier_set_handler(&svq->svq_kick, NULL);
79
+}
80
81
/**
82
* Creates vhost shadow virtqueue, and instructs the vhost device to use the
83
@@ -XXX,XX +XXX,XX @@ VhostShadowVirtqueue *vhost_svq_new(void)
84
goto err_init_hdev_call;
85
}
86
87
+ event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
88
return g_steal_pointer(&svq);
89
90
err_init_hdev_call:
91
@@ -XXX,XX +XXX,XX @@ err_init_hdev_kick:
92
void vhost_svq_free(gpointer pvq)
93
{
94
VhostShadowVirtqueue *vq = pvq;
95
+ vhost_svq_stop(vq);
96
event_notifier_cleanup(&vq->hdev_kick);
97
event_notifier_cleanup(&vq->hdev_call);
98
g_free(vq);
99
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
100
index XXXXXXX..XXXXXXX 100644
101
--- a/hw/virtio/vhost-shadow-virtqueue.h
102
+++ b/hw/virtio/vhost-shadow-virtqueue.h
103
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
104
EventNotifier hdev_kick;
105
/* Shadow call notifier, sent to vhost */
106
EventNotifier hdev_call;
107
+
108
+ /*
109
+ * Borrowed virtqueue's guest to host notifier. To borrow it in this event
110
+ * notifier allows to recover the VhostShadowVirtqueue from the event loop
111
+ * easily. If we use the VirtQueue's one, we don't have an easy way to
112
+ * retrieve VhostShadowVirtqueue.
113
+ *
114
+ * So shadow virtqueue must not clean it, or we would lose VirtQueue one.
115
+ */
116
+ EventNotifier svq_kick;
117
} VhostShadowVirtqueue;
118
119
+void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
120
+
121
+void vhost_svq_stop(VhostShadowVirtqueue *svq);
122
+
123
VhostShadowVirtqueue *vhost_svq_new(void);
124
125
void vhost_svq_free(gpointer vq);
15
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
126
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
16
index XXXXXXX..XXXXXXX 100644
127
index XXXXXXX..XXXXXXX 100644
17
--- a/hw/virtio/vhost-vdpa.c
128
--- a/hw/virtio/vhost-vdpa.c
18
+++ b/hw/virtio/vhost-vdpa.c
129
+++ b/hw/virtio/vhost-vdpa.c
19
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
130
@@ -XXX,XX +XXX,XX @@
20
for (unsigned n = 0; n < hdev->nvqs; ++n) {
131
#include "hw/virtio/vhost.h"
21
g_autoptr(VhostShadowVirtqueue) svq;
132
#include "hw/virtio/vhost-backend.h"
22
133
#include "hw/virtio/virtio-net.h"
23
- svq = vhost_svq_new(v->iova_tree, NULL, NULL);
134
+#include "hw/virtio/vhost-shadow-virtqueue.h"
24
+ svq = vhost_svq_new(v->iova_tree, v->shadow_vq_ops,
135
#include "hw/virtio/vhost-vdpa.h"
25
+ v->shadow_vq_ops_opaque);
136
#include "exec/address-spaces.h"
26
if (unlikely(!svq)) {
137
#include "qemu/main-loop.h"
27
error_setg(errp, "Cannot create svq %u", n);
138
#include "cpu.h"
28
return -1;
139
#include "trace.h"
140
#include "qemu-common.h"
141
+#include "qapi/error.h"
142
143
/*
144
* Return one past the end of the end of section. Be careful with uint64_t
145
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_one_time_request(struct vhost_dev *dev)
146
return v->index != 0;
147
}
148
149
+static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
150
+ Error **errp)
151
+{
152
+ g_autoptr(GPtrArray) shadow_vqs = NULL;
153
+
154
+ if (!v->shadow_vqs_enabled) {
155
+ return 0;
156
+ }
157
+
158
+ shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free);
159
+ for (unsigned n = 0; n < hdev->nvqs; ++n) {
160
+ g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new();
161
+
162
+ if (unlikely(!svq)) {
163
+ error_setg(errp, "Cannot create svq %u", n);
164
+ return -1;
165
+ }
166
+ g_ptr_array_add(shadow_vqs, g_steal_pointer(&svq));
167
+ }
168
+
169
+ v->shadow_vqs = g_steal_pointer(&shadow_vqs);
170
+ return 0;
171
+}
172
+
173
static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
174
{
175
struct vhost_vdpa *v;
176
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
177
dev->opaque = opaque ;
178
v->listener = vhost_vdpa_memory_listener;
179
v->msg_type = VHOST_IOTLB_MSG_V2;
180
+ ret = vhost_vdpa_init_svq(dev, v, errp);
181
+ if (ret) {
182
+ goto err;
183
+ }
184
185
vhost_vdpa_get_iova_range(v);
186
187
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
188
VIRTIO_CONFIG_S_DRIVER);
189
190
return 0;
191
+
192
+err:
193
+ ram_block_discard_disable(false);
194
+ return ret;
195
}
196
197
static void vhost_vdpa_host_notifier_uninit(struct vhost_dev *dev,
198
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_host_notifiers_uninit(struct vhost_dev *dev, int n)
199
200
static void vhost_vdpa_host_notifiers_init(struct vhost_dev *dev)
201
{
202
+ struct vhost_vdpa *v = dev->opaque;
203
int i;
204
205
+ if (v->shadow_vqs_enabled) {
206
+ /* FIXME SVQ is not compatible with host notifiers mr */
207
+ return;
208
+ }
209
+
210
for (i = dev->vq_index; i < dev->vq_index + dev->nvqs; i++) {
211
if (vhost_vdpa_host_notifier_init(dev, i)) {
212
goto err;
213
@@ -XXX,XX +XXX,XX @@ err:
214
return;
215
}
216
217
+static void vhost_vdpa_svq_cleanup(struct vhost_dev *dev)
218
+{
219
+ struct vhost_vdpa *v = dev->opaque;
220
+ size_t idx;
221
+
222
+ if (!v->shadow_vqs) {
223
+ return;
224
+ }
225
+
226
+ for (idx = 0; idx < v->shadow_vqs->len; ++idx) {
227
+ vhost_svq_stop(g_ptr_array_index(v->shadow_vqs, idx));
228
+ }
229
+ g_ptr_array_free(v->shadow_vqs, true);
230
+}
231
+
232
static int vhost_vdpa_cleanup(struct vhost_dev *dev)
233
{
234
struct vhost_vdpa *v;
235
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_cleanup(struct vhost_dev *dev)
236
trace_vhost_vdpa_cleanup(dev, v);
237
vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs);
238
memory_listener_unregister(&v->listener);
239
+ vhost_vdpa_svq_cleanup(dev);
240
241
dev->opaque = NULL;
242
ram_block_discard_disable(false);
243
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_get_device_id(struct vhost_dev *dev,
244
return ret;
245
}
246
247
+static void vhost_vdpa_reset_svq(struct vhost_vdpa *v)
248
+{
249
+ if (!v->shadow_vqs_enabled) {
250
+ return;
251
+ }
252
+
253
+ for (unsigned i = 0; i < v->shadow_vqs->len; ++i) {
254
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i);
255
+ vhost_svq_stop(svq);
256
+ }
257
+}
258
+
259
static int vhost_vdpa_reset_device(struct vhost_dev *dev)
260
{
261
+ struct vhost_vdpa *v = dev->opaque;
262
int ret;
263
uint8_t status = 0;
264
265
+ vhost_vdpa_reset_svq(v);
266
+
267
ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status);
268
trace_vhost_vdpa_reset_device(dev, status);
269
return ret;
270
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config,
271
return ret;
272
}
273
274
+static int vhost_vdpa_set_vring_dev_kick(struct vhost_dev *dev,
275
+ struct vhost_vring_file *file)
276
+{
277
+ trace_vhost_vdpa_set_vring_kick(dev, file->index, file->fd);
278
+ return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file);
279
+}
280
+
281
+/**
282
+ * Set the shadow virtqueue descriptors to the device
283
+ *
284
+ * @dev: The vhost device model
285
+ * @svq: The shadow virtqueue
286
+ * @idx: The index of the virtqueue in the vhost device
287
+ * @errp: Error
288
+ */
289
+static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
290
+ VhostShadowVirtqueue *svq, unsigned idx,
291
+ Error **errp)
292
+{
293
+ struct vhost_vring_file file = {
294
+ .index = dev->vq_index + idx,
295
+ };
296
+ const EventNotifier *event_notifier = &svq->hdev_kick;
297
+ int r;
298
+
299
+ file.fd = event_notifier_get_fd(event_notifier);
300
+ r = vhost_vdpa_set_vring_dev_kick(dev, &file);
301
+ if (unlikely(r != 0)) {
302
+ error_setg_errno(errp, -r, "Can't set device kick fd");
303
+ }
304
+
305
+ return r == 0;
306
+}
307
+
308
+static bool vhost_vdpa_svqs_start(struct vhost_dev *dev)
309
+{
310
+ struct vhost_vdpa *v = dev->opaque;
311
+ Error *err = NULL;
312
+ unsigned i;
313
+
314
+ if (!v->shadow_vqs) {
315
+ return true;
316
+ }
317
+
318
+ for (i = 0; i < v->shadow_vqs->len; ++i) {
319
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i);
320
+ bool ok = vhost_vdpa_svq_setup(dev, svq, i, &err);
321
+ if (unlikely(!ok)) {
322
+ error_reportf_err(err, "Cannot setup SVQ %u: ", i);
323
+ return false;
324
+ }
325
+ }
326
+
327
+ return true;
328
+}
329
+
330
static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
331
{
332
struct vhost_vdpa *v = dev->opaque;
333
+ bool ok;
334
trace_vhost_vdpa_dev_start(dev, started);
335
336
if (started) {
337
vhost_vdpa_host_notifiers_init(dev);
338
+ ok = vhost_vdpa_svqs_start(dev);
339
+ if (unlikely(!ok)) {
340
+ return -1;
341
+ }
342
vhost_vdpa_set_vring_ready(dev);
343
} else {
344
vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs);
345
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_get_vring_base(struct vhost_dev *dev,
346
static int vhost_vdpa_set_vring_kick(struct vhost_dev *dev,
347
struct vhost_vring_file *file)
348
{
349
- trace_vhost_vdpa_set_vring_kick(dev, file->index, file->fd);
350
- return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file);
351
+ struct vhost_vdpa *v = dev->opaque;
352
+ int vdpa_idx = file->index - dev->vq_index;
353
+
354
+ if (v->shadow_vqs_enabled) {
355
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx);
356
+ vhost_svq_set_svq_kick_fd(svq, file->fd);
357
+ return 0;
358
+ } else {
359
+ return vhost_vdpa_set_vring_dev_kick(dev, file);
360
+ }
361
}
362
363
static int vhost_vdpa_set_vring_call(struct vhost_dev *dev,
29
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
364
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
30
index XXXXXXX..XXXXXXX 100644
365
index XXXXXXX..XXXXXXX 100644
31
--- a/include/hw/virtio/vhost-vdpa.h
366
--- a/include/hw/virtio/vhost-vdpa.h
32
+++ b/include/hw/virtio/vhost-vdpa.h
367
+++ b/include/hw/virtio/vhost-vdpa.h
33
@@ -XXX,XX +XXX,XX @@
368
@@ -XXX,XX +XXX,XX @@
34
#include <gmodule.h>
369
#ifndef HW_VIRTIO_VHOST_VDPA_H
35
370
#define HW_VIRTIO_VHOST_VDPA_H
36
#include "hw/virtio/vhost-iova-tree.h"
371
37
+#include "hw/virtio/vhost-shadow-virtqueue.h"
372
+#include <gmodule.h>
373
+
38
#include "hw/virtio/virtio.h"
374
#include "hw/virtio/virtio.h"
39
#include "standard-headers/linux/vhost_types.h"
375
#include "standard-headers/linux/vhost_types.h"
40
376
41
@@ -XXX,XX +XXX,XX @@ typedef struct vhost_vdpa {
377
@@ -XXX,XX +XXX,XX @@ typedef struct vhost_vdpa {
42
/* IOVA mapping used by the Shadow Virtqueue */
378
bool iotlb_batch_begin_sent;
43
VhostIOVATree *iova_tree;
379
MemoryListener listener;
44
GPtrArray *shadow_vqs;
380
struct vhost_vdpa_iova_range iova_range;
45
+ const VhostShadowVirtqueueOps *shadow_vq_ops;
381
+ bool shadow_vqs_enabled;
46
+ void *shadow_vq_ops_opaque;
382
+ GPtrArray *shadow_vqs;
47
struct vhost_dev *dev;
383
struct vhost_dev *dev;
48
VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX];
384
VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX];
49
} VhostVDPA;
385
} VhostVDPA;
50
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/net/vhost-vdpa.c
53
+++ b/net/vhost-vdpa.c
54
@@ -XXX,XX +XXX,XX @@
55
56
#include "qemu/osdep.h"
57
#include "clients.h"
58
+#include "hw/virtio/virtio-net.h"
59
#include "net/vhost_net.h"
60
#include "net/vhost-vdpa.h"
61
#include "hw/virtio/vhost-vdpa.h"
62
#include "qemu/config-file.h"
63
#include "qemu/error-report.h"
64
+#include "qemu/log.h"
65
+#include "qemu/memalign.h"
66
#include "qemu/option.h"
67
#include "qapi/error.h"
68
#include <linux/vhost.h>
69
@@ -XXX,XX +XXX,XX @@ static NetClientInfo net_vhost_vdpa_info = {
70
.check_peer_type = vhost_vdpa_check_peer_type,
71
};
72
73
+/**
74
+ * Forward buffer for the moment.
75
+ */
76
+static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
77
+ VirtQueueElement *elem,
78
+ void *opaque)
79
+{
80
+ unsigned int n = elem->out_num + elem->in_num;
81
+ g_autofree struct iovec *dev_buffers = g_new(struct iovec, n);
82
+ size_t in_len, dev_written;
83
+ virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
84
+ int r;
85
+
86
+ memcpy(dev_buffers, elem->out_sg, elem->out_num);
87
+ memcpy(dev_buffers + elem->out_num, elem->in_sg, elem->in_num);
88
+
89
+ r = vhost_svq_add(svq, &dev_buffers[0], elem->out_num, &dev_buffers[1],
90
+ elem->in_num, elem);
91
+ if (unlikely(r != 0)) {
92
+ if (unlikely(r == -ENOSPC)) {
93
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
94
+ __func__);
95
+ }
96
+ goto out;
97
+ }
98
+
99
+ /*
100
+ * We can poll here since we've had BQL from the time we sent the
101
+ * descriptor. Also, we need to take the answer before SVQ pulls by itself,
102
+ * when BQL is released
103
+ */
104
+ dev_written = vhost_svq_poll(svq);
105
+ if (unlikely(dev_written < sizeof(status))) {
106
+ error_report("Insufficient written data (%zu)", dev_written);
107
+ }
108
+
109
+out:
110
+ in_len = iov_from_buf(elem->in_sg, elem->in_num, 0, &status,
111
+ sizeof(status));
112
+ if (unlikely(in_len < sizeof(status))) {
113
+ error_report("Bad device CVQ written length");
114
+ }
115
+ vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status)));
116
+ g_free(elem);
117
+ return r;
118
+}
119
+
120
+static const VhostShadowVirtqueueOps vhost_vdpa_net_svq_ops = {
121
+ .avail_handler = vhost_vdpa_net_handle_ctrl_avail,
122
+};
123
+
124
static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
125
const char *device,
126
const char *name,
127
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
128
129
s->vhost_vdpa.device_fd = vdpa_device_fd;
130
s->vhost_vdpa.index = queue_pair_index;
131
+ if (!is_datapath) {
132
+ s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops;
133
+ s->vhost_vdpa.shadow_vq_ops_opaque = s;
134
+ }
135
ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs);
136
if (ret) {
137
qemu_del_net_client(nc);
138
--
386
--
139
2.7.4
387
2.7.4
140
388
141
389
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
This will allow SVQ to add context to the different queue elements.
3
This will make qemu aware of the device used buffers, allowing it to
4
4
write the guest memory with its contents if needed.
5
This patch only store the actual element, no functional change intended.
6
5
7
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
8
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
7
Acked-by: Michael S. Tsirkin <mst@redhat.com>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
9
---
11
hw/virtio/vhost-shadow-virtqueue.c | 16 ++++++++--------
10
hw/virtio/vhost-shadow-virtqueue.c | 38 ++++++++++++++++++++++++++++++++++++++
12
hw/virtio/vhost-shadow-virtqueue.h | 8 ++++++--
11
hw/virtio/vhost-shadow-virtqueue.h | 4 ++++
13
2 files changed, 14 insertions(+), 10 deletions(-)
12
hw/virtio/vhost-vdpa.c | 31 +++++++++++++++++++++++++++++--
13
3 files changed, 71 insertions(+), 2 deletions(-)
14
14
15
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
15
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
16
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
17
--- a/hw/virtio/vhost-shadow-virtqueue.c
17
--- a/hw/virtio/vhost-shadow-virtqueue.c
18
+++ b/hw/virtio/vhost-shadow-virtqueue.c
18
+++ b/hw/virtio/vhost-shadow-virtqueue.c
19
@@ -XXX,XX +XXX,XX @@ static int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
19
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick(EventNotifier *n)
20
return -EINVAL;
20
}
21
22
/**
23
+ * Forward vhost notifications
24
+ *
25
+ * @n: hdev call event notifier, the one that device set to notify svq.
26
+ */
27
+static void vhost_svq_handle_call(EventNotifier *n)
28
+{
29
+ VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue,
30
+ hdev_call);
31
+ event_notifier_test_and_clear(n);
32
+ event_notifier_set(&svq->svq_call);
33
+}
34
+
35
+/**
36
+ * Set the call notifier for the SVQ to call the guest
37
+ *
38
+ * @svq: Shadow virtqueue
39
+ * @call_fd: call notifier
40
+ *
41
+ * Called on BQL context.
42
+ */
43
+void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd)
44
+{
45
+ if (call_fd == VHOST_FILE_UNBIND) {
46
+ /*
47
+ * Fail event_notifier_set if called handling device call.
48
+ *
49
+ * SVQ still needs device notifications, since it needs to keep
50
+ * forwarding used buffers even with the unbind.
51
+ */
52
+ memset(&svq->svq_call, 0, sizeof(svq->svq_call));
53
+ } else {
54
+ event_notifier_init_fd(&svq->svq_call, call_fd);
55
+ }
56
+}
57
+
58
+/**
59
* Set a new file descriptor for the guest to kick the SVQ and notify for avail
60
*
61
* @svq: The svq
62
@@ -XXX,XX +XXX,XX @@ VhostShadowVirtqueue *vhost_svq_new(void)
21
}
63
}
22
64
23
- svq->ring_id_maps[qemu_head] = elem;
65
event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
24
+ svq->desc_state[qemu_head].elem = elem;
66
+ event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call);
25
vhost_svq_kick(svq);
67
return g_steal_pointer(&svq);
26
return 0;
68
27
}
69
err_init_hdev_call:
28
@@ -XXX,XX +XXX,XX @@ static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq,
70
@@ -XXX,XX +XXX,XX @@ void vhost_svq_free(gpointer pvq)
29
return NULL;
71
VhostShadowVirtqueue *vq = pvq;
30
}
72
vhost_svq_stop(vq);
31
73
event_notifier_cleanup(&vq->hdev_kick);
32
- if (unlikely(!svq->ring_id_maps[used_elem.id])) {
74
+ event_notifier_set_handler(&vq->hdev_call, NULL);
33
+ if (unlikely(!svq->desc_state[used_elem.id].elem)) {
75
event_notifier_cleanup(&vq->hdev_call);
34
qemu_log_mask(LOG_GUEST_ERROR,
76
g_free(vq);
35
"Device %s says index %u is used, but it was not available",
36
svq->vdev->name, used_elem.id);
37
return NULL;
38
}
39
40
- num = svq->ring_id_maps[used_elem.id]->in_num +
41
- svq->ring_id_maps[used_elem.id]->out_num;
42
+ num = svq->desc_state[used_elem.id].elem->in_num +
43
+ svq->desc_state[used_elem.id].elem->out_num;
44
last_used_chain = vhost_svq_last_desc_of_chain(svq, num, used_elem.id);
45
svq->desc_next[last_used_chain] = svq->free_head;
46
svq->free_head = used_elem.id;
47
48
*len = used_elem.len;
49
- return g_steal_pointer(&svq->ring_id_maps[used_elem.id]);
50
+ return g_steal_pointer(&svq->desc_state[used_elem.id].elem);
51
}
52
53
static void vhost_svq_flush(VhostShadowVirtqueue *svq,
54
@@ -XXX,XX +XXX,XX @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
55
memset(svq->vring.desc, 0, driver_size);
56
svq->vring.used = qemu_memalign(qemu_real_host_page_size(), device_size);
57
memset(svq->vring.used, 0, device_size);
58
- svq->ring_id_maps = g_new0(VirtQueueElement *, svq->vring.num);
59
+ svq->desc_state = g_new0(SVQDescState, svq->vring.num);
60
svq->desc_next = g_new0(uint16_t, svq->vring.num);
61
for (unsigned i = 0; i < svq->vring.num - 1; i++) {
62
svq->desc_next[i] = cpu_to_le16(i + 1);
63
@@ -XXX,XX +XXX,XX @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
64
65
for (unsigned i = 0; i < svq->vring.num; ++i) {
66
g_autofree VirtQueueElement *elem = NULL;
67
- elem = g_steal_pointer(&svq->ring_id_maps[i]);
68
+ elem = g_steal_pointer(&svq->desc_state[i].elem);
69
if (elem) {
70
virtqueue_detach_element(svq->vq, elem, 0);
71
}
72
@@ -XXX,XX +XXX,XX @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
73
}
74
svq->vq = NULL;
75
g_free(svq->desc_next);
76
- g_free(svq->ring_id_maps);
77
+ g_free(svq->desc_state);
78
qemu_vfree(svq->vring.desc);
79
qemu_vfree(svq->vring.used);
80
}
77
}
81
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
78
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
82
index XXXXXXX..XXXXXXX 100644
79
index XXXXXXX..XXXXXXX 100644
83
--- a/hw/virtio/vhost-shadow-virtqueue.h
80
--- a/hw/virtio/vhost-shadow-virtqueue.h
84
+++ b/hw/virtio/vhost-shadow-virtqueue.h
81
+++ b/hw/virtio/vhost-shadow-virtqueue.h
85
@@ -XXX,XX +XXX,XX @@
82
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
86
#include "standard-headers/linux/vhost_types.h"
83
* So shadow virtqueue must not clean it, or we would lose VirtQueue one.
87
#include "hw/virtio/vhost-iova-tree.h"
84
*/
88
85
EventNotifier svq_kick;
89
+typedef struct SVQDescState {
90
+ VirtQueueElement *elem;
91
+} SVQDescState;
92
+
86
+
93
/* Shadow virtqueue to relay notifications */
87
+ /* Guest's call notifier, where the SVQ calls guest. */
94
typedef struct VhostShadowVirtqueue {
88
+ EventNotifier svq_call;
95
/* Shadow vring */
89
} VhostShadowVirtqueue;
96
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
90
97
/* IOVA mapping */
91
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
98
VhostIOVATree *iova_tree;
92
+void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
99
93
100
- /* Map for use the guest's descriptors */
94
void vhost_svq_stop(VhostShadowVirtqueue *svq);
101
- VirtQueueElement **ring_id_maps;
95
102
+ /* SVQ vring descriptors state */
96
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
103
+ SVQDescState *desc_state;
97
index XXXXXXX..XXXXXXX 100644
104
98
--- a/hw/virtio/vhost-vdpa.c
105
/* Next VirtQueue element that guest made available */
99
+++ b/hw/virtio/vhost-vdpa.c
106
VirtQueueElement *next_guest_avail_elem;
100
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_dev_kick(struct vhost_dev *dev,
101
return vhost_vdpa_call(dev, VHOST_SET_VRING_KICK, file);
102
}
103
104
+static int vhost_vdpa_set_vring_dev_call(struct vhost_dev *dev,
105
+ struct vhost_vring_file *file)
106
+{
107
+ trace_vhost_vdpa_set_vring_call(dev, file->index, file->fd);
108
+ return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file);
109
+}
110
+
111
/**
112
* Set the shadow virtqueue descriptors to the device
113
*
114
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_dev_kick(struct vhost_dev *dev,
115
* @svq: The shadow virtqueue
116
* @idx: The index of the virtqueue in the vhost device
117
* @errp: Error
118
+ *
119
+ * Note that this function does not rewind kick file descriptor if cannot set
120
+ * call one.
121
*/
122
static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
123
VhostShadowVirtqueue *svq, unsigned idx,
124
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
125
r = vhost_vdpa_set_vring_dev_kick(dev, &file);
126
if (unlikely(r != 0)) {
127
error_setg_errno(errp, -r, "Can't set device kick fd");
128
+ return false;
129
+ }
130
+
131
+ event_notifier = &svq->hdev_call;
132
+ file.fd = event_notifier_get_fd(event_notifier);
133
+ r = vhost_vdpa_set_vring_dev_call(dev, &file);
134
+ if (unlikely(r != 0)) {
135
+ error_setg_errno(errp, -r, "Can't set device call fd");
136
}
137
138
return r == 0;
139
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_kick(struct vhost_dev *dev,
140
static int vhost_vdpa_set_vring_call(struct vhost_dev *dev,
141
struct vhost_vring_file *file)
142
{
143
- trace_vhost_vdpa_set_vring_call(dev, file->index, file->fd);
144
- return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file);
145
+ struct vhost_vdpa *v = dev->opaque;
146
+
147
+ if (v->shadow_vqs_enabled) {
148
+ int vdpa_idx = file->index - dev->vq_index;
149
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, vdpa_idx);
150
+
151
+ vhost_svq_set_svq_call_fd(svq, file->fd);
152
+ return 0;
153
+ } else {
154
+ return vhost_vdpa_set_vring_dev_call(dev, file);
155
+ }
156
}
157
158
static int vhost_vdpa_get_features(struct vhost_dev *dev,
107
--
159
--
108
2.7.4
160
2.7.4
109
161
110
162
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
This function allows external SVQ users to return guest's available
3
This allows SVQ to negotiate features with the guest and the device. For
4
buffers.
4
the device, SVQ is a driver. While this function bypasses all
5
non-transport features, it needs to disable the features that SVQ does
6
not support when forwarding buffers. This includes packed vq layout,
7
indirect descriptors or event idx.
8
9
Future changes can add support to offer more features to the guest,
10
since the use of VirtQueue gives this for free. This is left out at the
11
moment for simplicity.
5
12
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
13
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
14
Acked-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
16
---
10
hw/virtio/vhost-shadow-virtqueue.c | 16 ++++++++++++++++
17
hw/virtio/vhost-shadow-virtqueue.c | 44 ++++++++++++++++++++++++++++++++++++++
11
hw/virtio/vhost-shadow-virtqueue.h | 3 +++
18
hw/virtio/vhost-shadow-virtqueue.h | 2 ++
12
2 files changed, 19 insertions(+)
19
hw/virtio/vhost-vdpa.c | 15 +++++++++++++
20
3 files changed, 61 insertions(+)
13
21
14
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
22
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
15
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/virtio/vhost-shadow-virtqueue.c
24
--- a/hw/virtio/vhost-shadow-virtqueue.c
17
+++ b/hw/virtio/vhost-shadow-virtqueue.c
25
+++ b/hw/virtio/vhost-shadow-virtqueue.c
18
@@ -XXX,XX +XXX,XX @@ static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq,
26
@@ -XXX,XX +XXX,XX @@
19
return g_steal_pointer(&svq->desc_state[used_elem.id].elem);
27
#include "hw/virtio/vhost-shadow-virtqueue.h"
20
}
28
21
29
#include "qemu/error-report.h"
22
+/**
30
+#include "qapi/error.h"
23
+ * Push an element to SVQ, returning it to the guest.
31
#include "qemu/main-loop.h"
32
#include "linux-headers/linux/vhost.h"
33
34
/**
35
+ * Validate the transport device features that both guests can use with the SVQ
36
+ * and SVQs can use with the device.
37
+ *
38
+ * @dev_features: The features
39
+ * @errp: Error pointer
24
+ */
40
+ */
25
+void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
41
+bool vhost_svq_valid_features(uint64_t features, Error **errp)
26
+ const VirtQueueElement *elem, uint32_t len)
27
+{
42
+{
28
+ virtqueue_push(svq->vq, elem, len);
43
+ bool ok = true;
29
+ if (svq->next_guest_avail_elem) {
44
+ uint64_t svq_features = features;
30
+ /*
45
+
31
+ * Avail ring was full when vhost_svq_flush was called, so it's a
46
+ for (uint64_t b = VIRTIO_TRANSPORT_F_START; b <= VIRTIO_TRANSPORT_F_END;
32
+ * good moment to make more descriptors available if possible.
47
+ ++b) {
33
+ */
48
+ switch (b) {
34
+ vhost_handle_guest_kick(svq);
49
+ case VIRTIO_F_ANY_LAYOUT:
50
+ continue;
51
+
52
+ case VIRTIO_F_ACCESS_PLATFORM:
53
+ /* SVQ trust in the host's IOMMU to translate addresses */
54
+ case VIRTIO_F_VERSION_1:
55
+ /* SVQ trust that the guest vring is little endian */
56
+ if (!(svq_features & BIT_ULL(b))) {
57
+ svq_features |= BIT_ULL(b);
58
+ ok = false;
59
+ }
60
+ continue;
61
+
62
+ default:
63
+ if (svq_features & BIT_ULL(b)) {
64
+ svq_features &= ~BIT_ULL(b);
65
+ ok = false;
66
+ }
67
+ }
35
+ }
68
+ }
69
+
70
+ if (!ok) {
71
+ error_setg(errp, "SVQ Invalid device feature flags, offer: 0x%"PRIx64
72
+ ", ok: 0x%"PRIx64, features, svq_features);
73
+ }
74
+ return ok;
36
+}
75
+}
37
+
76
+
38
static void vhost_svq_flush(VhostShadowVirtqueue *svq,
77
+/**
39
bool check_for_avail_queue)
78
* Forward guest notifications.
40
{
79
*
80
* @n: guest kick event notifier, the one that guest set to notify svq.
41
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
81
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
42
index XXXXXXX..XXXXXXX 100644
82
index XXXXXXX..XXXXXXX 100644
43
--- a/hw/virtio/vhost-shadow-virtqueue.h
83
--- a/hw/virtio/vhost-shadow-virtqueue.h
44
+++ b/hw/virtio/vhost-shadow-virtqueue.h
84
+++ b/hw/virtio/vhost-shadow-virtqueue.h
45
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
85
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
46
86
EventNotifier svq_call;
47
bool vhost_svq_valid_features(uint64_t features, Error **errp);
87
} VhostShadowVirtqueue;
48
88
49
+void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
89
+bool vhost_svq_valid_features(uint64_t features, Error **errp);
50
+ const VirtQueueElement *elem, uint32_t len);
51
+
90
+
52
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
91
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
53
void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
92
void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
54
void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
93
94
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
95
index XXXXXXX..XXXXXXX 100644
96
--- a/hw/virtio/vhost-vdpa.c
97
+++ b/hw/virtio/vhost-vdpa.c
98
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
99
Error **errp)
100
{
101
g_autoptr(GPtrArray) shadow_vqs = NULL;
102
+ uint64_t dev_features, svq_features;
103
+ int r;
104
+ bool ok;
105
106
if (!v->shadow_vqs_enabled) {
107
return 0;
108
}
109
110
+ r = hdev->vhost_ops->vhost_get_features(hdev, &dev_features);
111
+ if (r != 0) {
112
+ error_setg_errno(errp, -r, "Can't get vdpa device features");
113
+ return r;
114
+ }
115
+
116
+ svq_features = dev_features;
117
+ ok = vhost_svq_valid_features(svq_features, errp);
118
+ if (unlikely(!ok)) {
119
+ return -1;
120
+ }
121
+
122
shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free);
123
for (unsigned n = 0; n < hdev->nvqs; ++n) {
124
g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new();
55
--
125
--
56
2.7.4
126
2.7.4
57
127
58
128
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
It allows the Shadow Control VirtQueue to wait for the device to use the
3
It reports the shadow virtqueue address from qemu virtual address space.
4
available buffers.
4
5
Since this will be different from the guest's vaddr, but the device can
6
access it, SVQ takes special care about its alignment & lack of garbage
7
data. It assumes that IOMMU will work in host_page_size ranges for that.
5
8
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
9
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
10
Acked-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
12
---
10
hw/virtio/vhost-shadow-virtqueue.c | 27 +++++++++++++++++++++++++++
13
hw/virtio/vhost-shadow-virtqueue.c | 29 +++++++++++++++++++++++++++++
11
hw/virtio/vhost-shadow-virtqueue.h | 1 +
14
hw/virtio/vhost-shadow-virtqueue.h | 9 +++++++++
12
2 files changed, 28 insertions(+)
15
2 files changed, 38 insertions(+)
13
16
14
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
17
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
15
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/virtio/vhost-shadow-virtqueue.c
19
--- a/hw/virtio/vhost-shadow-virtqueue.c
17
+++ b/hw/virtio/vhost-shadow-virtqueue.c
20
+++ b/hw/virtio/vhost-shadow-virtqueue.c
18
@@ -XXX,XX +XXX,XX @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq,
21
@@ -XXX,XX +XXX,XX @@ void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd)
19
}
22
}
20
23
21
/**
24
/**
22
+ * Poll the SVQ for one device used buffer.
25
+ * Get the shadow vq vring address.
23
+ *
26
+ * @svq: Shadow virtqueue
24
+ * This function race with main event loop SVQ polling, so extra
27
+ * @addr: Destination to store address
25
+ * synchronization is needed.
26
+ *
27
+ * Return the length written by the device.
28
+ */
28
+ */
29
+size_t vhost_svq_poll(VhostShadowVirtqueue *svq)
29
+void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
30
+ struct vhost_vring_addr *addr)
30
+{
31
+{
31
+ int64_t start_us = g_get_monotonic_time();
32
+ addr->desc_user_addr = (uint64_t)(intptr_t)svq->vring.desc;
32
+ do {
33
+ addr->avail_user_addr = (uint64_t)(intptr_t)svq->vring.avail;
33
+ uint32_t len;
34
+ addr->used_user_addr = (uint64_t)(intptr_t)svq->vring.used;
34
+ VirtQueueElement *elem = vhost_svq_get_buf(svq, &len);
35
+}
35
+ if (elem) {
36
+ return len;
37
+ }
38
+
36
+
39
+ if (unlikely(g_get_monotonic_time() - start_us > 10e6)) {
37
+size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq)
40
+ return 0;
38
+{
41
+ }
39
+ size_t desc_size = sizeof(vring_desc_t) * svq->vring.num;
40
+ size_t avail_size = offsetof(vring_avail_t, ring) +
41
+ sizeof(uint16_t) * svq->vring.num;
42
+
42
+
43
+ /* Make sure we read new used_idx */
43
+ return ROUND_UP(desc_size + avail_size, qemu_real_host_page_size);
44
+ smp_rmb();
44
+}
45
+ } while (true);
45
+
46
+size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq)
47
+{
48
+ size_t used_size = offsetof(vring_used_t, ring) +
49
+ sizeof(vring_used_elem_t) * svq->vring.num;
50
+ return ROUND_UP(used_size, qemu_real_host_page_size);
46
+}
51
+}
47
+
52
+
48
+/**
53
+/**
49
* Forward used buffers.
54
* Set a new file descriptor for the guest to kick the SVQ and notify for avail
50
*
55
*
51
* @n: hdev call event notifier, the one that device set to notify svq.
56
* @svq: The svq
52
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
57
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
53
index XXXXXXX..XXXXXXX 100644
58
index XXXXXXX..XXXXXXX 100644
54
--- a/hw/virtio/vhost-shadow-virtqueue.h
59
--- a/hw/virtio/vhost-shadow-virtqueue.h
55
+++ b/hw/virtio/vhost-shadow-virtqueue.h
60
+++ b/hw/virtio/vhost-shadow-virtqueue.h
56
@@ -XXX,XX +XXX,XX @@ void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
61
@@ -XXX,XX +XXX,XX @@
57
int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
62
#define VHOST_SHADOW_VIRTQUEUE_H
58
size_t out_num, const struct iovec *in_sg, size_t in_num,
63
59
VirtQueueElement *elem);
64
#include "qemu/event_notifier.h"
60
+size_t vhost_svq_poll(VhostShadowVirtqueue *svq);
65
+#include "hw/virtio/virtio.h"
66
+#include "standard-headers/linux/vhost_types.h"
67
68
/* Shadow virtqueue to relay notifications */
69
typedef struct VhostShadowVirtqueue {
70
+ /* Shadow vring */
71
+ struct vring vring;
72
+
73
/* Shadow kick notifier, sent to vhost */
74
EventNotifier hdev_kick;
75
/* Shadow call notifier, sent to vhost */
76
@@ -XXX,XX +XXX,XX @@ bool vhost_svq_valid_features(uint64_t features, Error **errp);
61
77
62
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
78
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
63
void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
79
void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
80
+void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
81
+ struct vhost_vring_addr *addr);
82
+size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq);
83
+size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq);
84
85
void vhost_svq_stop(VhostShadowVirtqueue *svq);
86
64
--
87
--
65
2.7.4
88
2.7.4
66
89
67
90
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
This allows external vhost-net devices to modify the state of the
3
First half of the buffers forwarding part, preparing vhost-vdpa
4
VirtIO device model once the vhost-vdpa device has acknowledged the
4
callbacks to SVQ to offer it. QEMU cannot enable it at this moment, so
5
control commands.
5
this is effectively dead code at the moment, but it helps to reduce
6
patch size.
6
7
7
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
8
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
8
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
9
Acked-by: Michael S. Tsirkin <mst@redhat.com>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
11
---
11
hw/net/virtio-net.c | 84 ++++++++++++++++++++++++------------------
12
hw/virtio/vhost-vdpa.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
12
include/hw/virtio/virtio-net.h | 4 ++
13
1 file changed, 41 insertions(+), 7 deletions(-)
13
2 files changed, 53 insertions(+), 35 deletions(-)
14
14
15
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
15
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
16
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
17
--- a/hw/net/virtio-net.c
17
--- a/hw/virtio/vhost-vdpa.c
18
+++ b/hw/net/virtio-net.c
18
+++ b/hw/virtio/vhost-vdpa.c
19
@@ -XXX,XX +XXX,XX @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
19
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_get_config(struct vhost_dev *dev, uint8_t *config,
20
return VIRTIO_NET_OK;
20
return ret;
21
}
22
23
+static int vhost_vdpa_set_dev_vring_base(struct vhost_dev *dev,
24
+ struct vhost_vring_state *ring)
25
+{
26
+ trace_vhost_vdpa_set_vring_base(dev, ring->index, ring->num);
27
+ return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring);
28
+}
29
+
30
static int vhost_vdpa_set_vring_dev_kick(struct vhost_dev *dev,
31
struct vhost_vring_file *file)
32
{
33
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_dev_call(struct vhost_dev *dev,
34
return vhost_vdpa_call(dev, VHOST_SET_VRING_CALL, file);
21
}
35
}
22
36
23
-static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
37
+static int vhost_vdpa_set_vring_dev_addr(struct vhost_dev *dev,
24
+size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev,
38
+ struct vhost_vring_addr *addr)
25
+ const struct iovec *in_sg, unsigned in_num,
39
+{
26
+ const struct iovec *out_sg,
40
+ trace_vhost_vdpa_set_vring_addr(dev, addr->index, addr->flags,
27
+ unsigned out_num)
41
+ addr->desc_user_addr, addr->used_user_addr,
42
+ addr->avail_user_addr,
43
+ addr->log_guest_addr);
44
+
45
+ return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr);
46
+
47
+}
48
+
49
/**
50
* Set the shadow virtqueue descriptors to the device
51
*
52
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base,
53
static int vhost_vdpa_set_vring_addr(struct vhost_dev *dev,
54
struct vhost_vring_addr *addr)
28
{
55
{
29
VirtIONet *n = VIRTIO_NET(vdev);
56
- trace_vhost_vdpa_set_vring_addr(dev, addr->index, addr->flags,
30
struct virtio_net_ctrl_hdr ctrl;
57
- addr->desc_user_addr, addr->used_user_addr,
31
virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
58
- addr->avail_user_addr,
32
- VirtQueueElement *elem;
59
- addr->log_guest_addr);
33
size_t s;
60
- return vhost_vdpa_call(dev, VHOST_SET_VRING_ADDR, addr);
34
struct iovec *iov, *iov2;
61
+ struct vhost_vdpa *v = dev->opaque;
35
- unsigned int iov_cnt;
36
+
62
+
37
+ if (iov_size(in_sg, in_num) < sizeof(status) ||
63
+ if (v->shadow_vqs_enabled) {
38
+ iov_size(out_sg, out_num) < sizeof(ctrl)) {
64
+ /*
39
+ virtio_error(vdev, "virtio-net ctrl missing headers");
65
+ * Device vring addr was set at device start. SVQ base is handled by
66
+ * VirtQueue code.
67
+ */
40
+ return 0;
68
+ return 0;
41
+ }
69
+ }
42
+
70
+
43
+ iov2 = iov = g_memdup2(out_sg, sizeof(struct iovec) * out_num);
71
+ return vhost_vdpa_set_vring_dev_addr(dev, addr);
44
+ s = iov_to_buf(iov, out_num, 0, &ctrl, sizeof(ctrl));
72
}
45
+ iov_discard_front(&iov, &out_num, sizeof(ctrl));
73
46
+ if (s != sizeof(ctrl)) {
74
static int vhost_vdpa_set_vring_num(struct vhost_dev *dev,
47
+ status = VIRTIO_NET_ERR;
75
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_num(struct vhost_dev *dev,
48
+ } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
76
static int vhost_vdpa_set_vring_base(struct vhost_dev *dev,
49
+ status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, out_num);
77
struct vhost_vring_state *ring)
50
+ } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
78
{
51
+ status = virtio_net_handle_mac(n, ctrl.cmd, iov, out_num);
79
- trace_vhost_vdpa_set_vring_base(dev, ring->index, ring->num);
52
+ } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
80
- return vhost_vdpa_call(dev, VHOST_SET_VRING_BASE, ring);
53
+ status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, out_num);
81
+ struct vhost_vdpa *v = dev->opaque;
54
+ } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
82
+
55
+ status = virtio_net_handle_announce(n, ctrl.cmd, iov, out_num);
83
+ if (v->shadow_vqs_enabled) {
56
+ } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
84
+ /*
57
+ status = virtio_net_handle_mq(n, ctrl.cmd, iov, out_num);
85
+ * Device vring base was set at device start. SVQ base is handled by
58
+ } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
86
+ * VirtQueue code.
59
+ status = virtio_net_handle_offloads(n, ctrl.cmd, iov, out_num);
87
+ */
88
+ return 0;
60
+ }
89
+ }
61
+
90
+
62
+ s = iov_from_buf(in_sg, in_num, 0, &status, sizeof(status));
91
+ return vhost_vdpa_set_dev_vring_base(dev, ring);
63
+ assert(s == sizeof(status));
64
+
65
+ g_free(iov2);
66
+ return sizeof(status);
67
+}
68
+
69
+static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
70
+{
71
+ VirtQueueElement *elem;
72
73
for (;;) {
74
+ size_t written;
75
elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
76
if (!elem) {
77
break;
78
}
79
- if (iov_size(elem->in_sg, elem->in_num) < sizeof(status) ||
80
- iov_size(elem->out_sg, elem->out_num) < sizeof(ctrl)) {
81
- virtio_error(vdev, "virtio-net ctrl missing headers");
82
+
83
+ written = virtio_net_handle_ctrl_iov(vdev, elem->in_sg, elem->in_num,
84
+ elem->out_sg, elem->out_num);
85
+ if (written > 0) {
86
+ virtqueue_push(vq, elem, written);
87
+ virtio_notify(vdev, vq);
88
+ g_free(elem);
89
+ } else {
90
virtqueue_detach_element(vq, elem, 0);
91
g_free(elem);
92
break;
93
}
94
-
95
- iov_cnt = elem->out_num;
96
- iov2 = iov = g_memdup2(elem->out_sg,
97
- sizeof(struct iovec) * elem->out_num);
98
- s = iov_to_buf(iov, iov_cnt, 0, &ctrl, sizeof(ctrl));
99
- iov_discard_front(&iov, &iov_cnt, sizeof(ctrl));
100
- if (s != sizeof(ctrl)) {
101
- status = VIRTIO_NET_ERR;
102
- } else if (ctrl.class == VIRTIO_NET_CTRL_RX) {
103
- status = virtio_net_handle_rx_mode(n, ctrl.cmd, iov, iov_cnt);
104
- } else if (ctrl.class == VIRTIO_NET_CTRL_MAC) {
105
- status = virtio_net_handle_mac(n, ctrl.cmd, iov, iov_cnt);
106
- } else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) {
107
- status = virtio_net_handle_vlan_table(n, ctrl.cmd, iov, iov_cnt);
108
- } else if (ctrl.class == VIRTIO_NET_CTRL_ANNOUNCE) {
109
- status = virtio_net_handle_announce(n, ctrl.cmd, iov, iov_cnt);
110
- } else if (ctrl.class == VIRTIO_NET_CTRL_MQ) {
111
- status = virtio_net_handle_mq(n, ctrl.cmd, iov, iov_cnt);
112
- } else if (ctrl.class == VIRTIO_NET_CTRL_GUEST_OFFLOADS) {
113
- status = virtio_net_handle_offloads(n, ctrl.cmd, iov, iov_cnt);
114
- }
115
-
116
- s = iov_from_buf(elem->in_sg, elem->in_num, 0, &status, sizeof(status));
117
- assert(s == sizeof(status));
118
-
119
- virtqueue_push(vq, elem, sizeof(status));
120
- virtio_notify(vdev, vq);
121
- g_free(iov2);
122
- g_free(elem);
123
}
124
}
92
}
125
93
126
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
94
static int vhost_vdpa_get_vring_base(struct vhost_dev *dev,
127
index XXXXXXX..XXXXXXX 100644
128
--- a/include/hw/virtio/virtio-net.h
129
+++ b/include/hw/virtio/virtio-net.h
130
@@ -XXX,XX +XXX,XX @@ struct VirtIONet {
131
struct EBPFRSSContext ebpf_rss;
132
};
133
134
+size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev,
135
+ const struct iovec *in_sg, unsigned in_num,
136
+ const struct iovec *out_sg,
137
+ unsigned out_num);
138
void virtio_net_set_netclient_name(VirtIONet *n, const char *name,
139
const char *type);
140
141
--
95
--
142
2.7.4
96
2.7.4
143
97
144
98
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Future code needs to call it from vhost_svq_add.
3
Initial version of shadow virtqueue that actually forward buffers. There
4
4
is no iommu support at the moment, and that will be addressed in future
5
No functional change intended.
5
patches of this series. Since all vhost-vdpa devices use forced IOMMU,
6
this means that SVQ is not usable at this point of the series on any
7
device.
8
9
For simplicity it only supports modern devices, that expects vring
10
in little endian, with split ring and no event idx or indirect
11
descriptors. Support for them will not be added in this series.
12
13
It reuses the VirtQueue code for the device part. The driver part is
14
based on Linux's virtio_ring driver, but with stripped functionality
15
and optimizations so it's easier to review.
16
17
However, forwarding buffers have some particular pieces: One of the most
18
unexpected ones is that a guest's buffer can expand through more than
19
one descriptor in SVQ. While this is handled gracefully by qemu's
20
emulated virtio devices, it may cause unexpected SVQ queue full. This
21
patch also solves it by checking for this condition at both guest's
22
kicks and device's calls. The code may be more elegant in the future if
23
SVQ code runs in its own iocontext.
6
24
7
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
25
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
8
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
26
Acked-by: Michael S. Tsirkin <mst@redhat.com>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
27
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
28
---
11
hw/virtio/vhost-shadow-virtqueue.c | 28 ++++++++++++++--------------
29
hw/virtio/vhost-shadow-virtqueue.c | 352 ++++++++++++++++++++++++++++++++++++-
12
1 file changed, 14 insertions(+), 14 deletions(-)
30
hw/virtio/vhost-shadow-virtqueue.h | 26 +++
31
hw/virtio/vhost-vdpa.c | 155 +++++++++++++++-
32
3 files changed, 522 insertions(+), 11 deletions(-)
13
33
14
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
34
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
15
index XXXXXXX..XXXXXXX 100644
35
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/virtio/vhost-shadow-virtqueue.c
36
--- a/hw/virtio/vhost-shadow-virtqueue.c
17
+++ b/hw/virtio/vhost-shadow-virtqueue.c
37
+++ b/hw/virtio/vhost-shadow-virtqueue.c
18
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
38
@@ -XXX,XX +XXX,XX @@
19
return true;
39
#include "qemu/error-report.h"
40
#include "qapi/error.h"
41
#include "qemu/main-loop.h"
42
+#include "qemu/log.h"
43
+#include "qemu/memalign.h"
44
#include "linux-headers/linux/vhost.h"
45
46
/**
47
@@ -XXX,XX +XXX,XX @@ bool vhost_svq_valid_features(uint64_t features, Error **errp)
20
}
48
}
21
49
50
/**
51
- * Forward guest notifications.
52
+ * Number of descriptors that the SVQ can make available from the guest.
53
+ *
54
+ * @svq: The svq
55
+ */
56
+static uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq)
57
+{
58
+ return svq->vring.num - (svq->shadow_avail_idx - svq->shadow_used_idx);
59
+}
60
+
61
+static void vhost_vring_write_descs(VhostShadowVirtqueue *svq,
62
+ const struct iovec *iovec, size_t num,
63
+ bool more_descs, bool write)
64
+{
65
+ uint16_t i = svq->free_head, last = svq->free_head;
66
+ unsigned n;
67
+ uint16_t flags = write ? cpu_to_le16(VRING_DESC_F_WRITE) : 0;
68
+ vring_desc_t *descs = svq->vring.desc;
69
+
70
+ if (num == 0) {
71
+ return;
72
+ }
73
+
74
+ for (n = 0; n < num; n++) {
75
+ if (more_descs || (n + 1 < num)) {
76
+ descs[i].flags = flags | cpu_to_le16(VRING_DESC_F_NEXT);
77
+ } else {
78
+ descs[i].flags = flags;
79
+ }
80
+ descs[i].addr = cpu_to_le64((hwaddr)(intptr_t)iovec[n].iov_base);
81
+ descs[i].len = cpu_to_le32(iovec[n].iov_len);
82
+
83
+ last = i;
84
+ i = cpu_to_le16(descs[i].next);
85
+ }
86
+
87
+ svq->free_head = le16_to_cpu(descs[last].next);
88
+}
89
+
90
+static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
91
+ VirtQueueElement *elem, unsigned *head)
92
+{
93
+ unsigned avail_idx;
94
+ vring_avail_t *avail = svq->vring.avail;
95
+
96
+ *head = svq->free_head;
97
+
98
+ /* We need some descriptors here */
99
+ if (unlikely(!elem->out_num && !elem->in_num)) {
100
+ qemu_log_mask(LOG_GUEST_ERROR,
101
+ "Guest provided element with no descriptors");
102
+ return false;
103
+ }
104
+
105
+ vhost_vring_write_descs(svq, elem->out_sg, elem->out_num, elem->in_num > 0,
106
+ false);
107
+ vhost_vring_write_descs(svq, elem->in_sg, elem->in_num, false, true);
108
+
109
+ /*
110
+ * Put the entry in the available array (but don't update avail->idx until
111
+ * they do sync).
112
+ */
113
+ avail_idx = svq->shadow_avail_idx & (svq->vring.num - 1);
114
+ avail->ring[avail_idx] = cpu_to_le16(*head);
115
+ svq->shadow_avail_idx++;
116
+
117
+ /* Update the avail index after write the descriptor */
118
+ smp_wmb();
119
+ avail->idx = cpu_to_le16(svq->shadow_avail_idx);
120
+
121
+ return true;
122
+}
123
+
124
+static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
125
+{
126
+ unsigned qemu_head;
127
+ bool ok = vhost_svq_add_split(svq, elem, &qemu_head);
128
+ if (unlikely(!ok)) {
129
+ return false;
130
+ }
131
+
132
+ svq->ring_id_maps[qemu_head] = elem;
133
+ return true;
134
+}
135
+
22
+static void vhost_svq_kick(VhostShadowVirtqueue *svq)
136
+static void vhost_svq_kick(VhostShadowVirtqueue *svq)
23
+{
137
+{
24
+ /*
138
+ /*
25
+ * We need to expose the available array entries before checking the used
139
+ * We need to expose the available array entries before checking the used
26
+ * flags
140
+ * flags
...
...
31
+ }
145
+ }
32
+
146
+
33
+ event_notifier_set(&svq->hdev_kick);
147
+ event_notifier_set(&svq->hdev_kick);
34
+}
148
+}
35
+
149
+
150
+/**
151
+ * Forward available buffers.
152
+ *
153
+ * @svq: Shadow VirtQueue
154
+ *
155
+ * Note that this function does not guarantee that all guest's available
156
+ * buffers are available to the device in SVQ avail ring. The guest may have
157
+ * exposed a GPA / GIOVA contiguous buffer, but it may not be contiguous in
158
+ * qemu vaddr.
159
+ *
160
+ * If that happens, guest's kick notifications will be disabled until the
161
+ * device uses some buffers.
162
+ */
163
+static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq)
164
+{
165
+ /* Clear event notifier */
166
+ event_notifier_test_and_clear(&svq->svq_kick);
167
+
168
+ /* Forward to the device as many available buffers as possible */
169
+ do {
170
+ virtio_queue_set_notification(svq->vq, false);
171
+
172
+ while (true) {
173
+ VirtQueueElement *elem;
174
+ bool ok;
175
+
176
+ if (svq->next_guest_avail_elem) {
177
+ elem = g_steal_pointer(&svq->next_guest_avail_elem);
178
+ } else {
179
+ elem = virtqueue_pop(svq->vq, sizeof(*elem));
180
+ }
181
+
182
+ if (!elem) {
183
+ break;
184
+ }
185
+
186
+ if (elem->out_num + elem->in_num > vhost_svq_available_slots(svq)) {
187
+ /*
188
+ * This condition is possible since a contiguous buffer in GPA
189
+ * does not imply a contiguous buffer in qemu's VA
190
+ * scatter-gather segments. If that happens, the buffer exposed
191
+ * to the device needs to be a chain of descriptors at this
192
+ * moment.
193
+ *
194
+ * SVQ cannot hold more available buffers if we are here:
195
+ * queue the current guest descriptor and ignore further kicks
196
+ * until some elements are used.
197
+ */
198
+ svq->next_guest_avail_elem = elem;
199
+ return;
200
+ }
201
+
202
+ ok = vhost_svq_add(svq, elem);
203
+ if (unlikely(!ok)) {
204
+ /* VQ is broken, just return and ignore any other kicks */
205
+ return;
206
+ }
207
+ vhost_svq_kick(svq);
208
+ }
209
+
210
+ virtio_queue_set_notification(svq->vq, true);
211
+ } while (!virtio_queue_empty(svq->vq));
212
+}
213
+
214
+/**
215
+ * Handle guest's kick.
216
*
217
* @n: guest kick event notifier, the one that guest set to notify svq.
218
*/
219
-static void vhost_handle_guest_kick(EventNotifier *n)
220
+static void vhost_handle_guest_kick_notifier(EventNotifier *n)
221
{
222
VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue, svq_kick);
223
event_notifier_test_and_clear(n);
224
- event_notifier_set(&svq->hdev_kick);
225
+ vhost_handle_guest_kick(svq);
226
+}
227
+
228
+static bool vhost_svq_more_used(VhostShadowVirtqueue *svq)
229
+{
230
+ if (svq->last_used_idx != svq->shadow_used_idx) {
231
+ return true;
232
+ }
233
+
234
+ svq->shadow_used_idx = cpu_to_le16(svq->vring.used->idx);
235
+
236
+ return svq->last_used_idx != svq->shadow_used_idx;
237
}
238
36
/**
239
/**
37
* Add an element to a SVQ.
240
- * Forward vhost notifications
241
+ * Enable vhost device calls after disable them.
242
+ *
243
+ * @svq: The svq
244
+ *
245
+ * It returns false if there are pending used buffers from the vhost device,
246
+ * avoiding the possible races between SVQ checking for more work and enabling
247
+ * callbacks. True if SVQ used vring has no more pending buffers.
248
+ */
249
+static bool vhost_svq_enable_notification(VhostShadowVirtqueue *svq)
250
+{
251
+ svq->vring.avail->flags &= ~cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT);
252
+ /* Make sure the flag is written before the read of used_idx */
253
+ smp_mb();
254
+ return !vhost_svq_more_used(svq);
255
+}
256
+
257
+static void vhost_svq_disable_notification(VhostShadowVirtqueue *svq)
258
+{
259
+ svq->vring.avail->flags |= cpu_to_le16(VRING_AVAIL_F_NO_INTERRUPT);
260
+}
261
+
262
+static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq,
263
+ uint32_t *len)
264
+{
265
+ vring_desc_t *descs = svq->vring.desc;
266
+ const vring_used_t *used = svq->vring.used;
267
+ vring_used_elem_t used_elem;
268
+ uint16_t last_used;
269
+
270
+ if (!vhost_svq_more_used(svq)) {
271
+ return NULL;
272
+ }
273
+
274
+ /* Only get used array entries after they have been exposed by dev */
275
+ smp_rmb();
276
+ last_used = svq->last_used_idx & (svq->vring.num - 1);
277
+ used_elem.id = le32_to_cpu(used->ring[last_used].id);
278
+ used_elem.len = le32_to_cpu(used->ring[last_used].len);
279
+
280
+ svq->last_used_idx++;
281
+ if (unlikely(used_elem.id >= svq->vring.num)) {
282
+ qemu_log_mask(LOG_GUEST_ERROR, "Device %s says index %u is used",
283
+ svq->vdev->name, used_elem.id);
284
+ return NULL;
285
+ }
286
+
287
+ if (unlikely(!svq->ring_id_maps[used_elem.id])) {
288
+ qemu_log_mask(LOG_GUEST_ERROR,
289
+ "Device %s says index %u is used, but it was not available",
290
+ svq->vdev->name, used_elem.id);
291
+ return NULL;
292
+ }
293
+
294
+ descs[used_elem.id].next = svq->free_head;
295
+ svq->free_head = used_elem.id;
296
+
297
+ *len = used_elem.len;
298
+ return g_steal_pointer(&svq->ring_id_maps[used_elem.id]);
299
+}
300
+
301
+static void vhost_svq_flush(VhostShadowVirtqueue *svq,
302
+ bool check_for_avail_queue)
303
+{
304
+ VirtQueue *vq = svq->vq;
305
+
306
+ /* Forward as many used buffers as possible. */
307
+ do {
308
+ unsigned i = 0;
309
+
310
+ vhost_svq_disable_notification(svq);
311
+ while (true) {
312
+ uint32_t len;
313
+ g_autofree VirtQueueElement *elem = vhost_svq_get_buf(svq, &len);
314
+ if (!elem) {
315
+ break;
316
+ }
317
+
318
+ if (unlikely(i >= svq->vring.num)) {
319
+ qemu_log_mask(LOG_GUEST_ERROR,
320
+ "More than %u used buffers obtained in a %u size SVQ",
321
+ i, svq->vring.num);
322
+ virtqueue_fill(vq, elem, len, i);
323
+ virtqueue_flush(vq, i);
324
+ return;
325
+ }
326
+ virtqueue_fill(vq, elem, len, i++);
327
+ }
328
+
329
+ virtqueue_flush(vq, i);
330
+ event_notifier_set(&svq->svq_call);
331
+
332
+ if (check_for_avail_queue && svq->next_guest_avail_elem) {
333
+ /*
334
+ * Avail ring was full when vhost_svq_flush was called, so it's a
335
+ * good moment to make more descriptors available if possible.
336
+ */
337
+ vhost_handle_guest_kick(svq);
338
+ }
339
+ } while (!vhost_svq_enable_notification(svq));
340
+}
341
+
342
+/**
343
+ * Forward used buffers.
38
*
344
*
39
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
345
* @n: hdev call event notifier, the one that device set to notify svq.
40
return true;
346
+ *
347
+ * Note that we are not making any buffers available in the loop, there is no
348
+ * way that it runs more than virtqueue size times.
349
*/
350
static void vhost_svq_handle_call(EventNotifier *n)
351
{
352
VhostShadowVirtqueue *svq = container_of(n, VhostShadowVirtqueue,
353
hdev_call);
354
event_notifier_test_and_clear(n);
355
- event_notifier_set(&svq->svq_call);
356
+ vhost_svq_flush(svq, true);
41
}
357
}
42
358
43
-static void vhost_svq_kick(VhostShadowVirtqueue *svq)
44
-{
45
- /*
46
- * We need to expose the available array entries before checking the used
47
- * flags
48
- */
49
- smp_mb();
50
- if (svq->vring.used->flags & VRING_USED_F_NO_NOTIFY) {
51
- return;
52
- }
53
-
54
- event_notifier_set(&svq->hdev_kick);
55
-}
56
-
57
/**
359
/**
58
* Forward available buffers.
360
@@ -XXX,XX +XXX,XX @@ void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
59
*
361
if (poll_start) {
362
event_notifier_init_fd(svq_kick, svq_kick_fd);
363
event_notifier_set(svq_kick);
364
- event_notifier_set_handler(svq_kick, vhost_handle_guest_kick);
365
+ event_notifier_set_handler(svq_kick, vhost_handle_guest_kick_notifier);
366
+ }
367
+}
368
+
369
+/**
370
+ * Start the shadow virtqueue operation.
371
+ *
372
+ * @svq: Shadow Virtqueue
373
+ * @vdev: VirtIO device
374
+ * @vq: Virtqueue to shadow
375
+ */
376
+void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
377
+ VirtQueue *vq)
378
+{
379
+ size_t desc_size, driver_size, device_size;
380
+
381
+ svq->next_guest_avail_elem = NULL;
382
+ svq->shadow_avail_idx = 0;
383
+ svq->shadow_used_idx = 0;
384
+ svq->last_used_idx = 0;
385
+ svq->vdev = vdev;
386
+ svq->vq = vq;
387
+
388
+ svq->vring.num = virtio_queue_get_num(vdev, virtio_get_queue_index(vq));
389
+ driver_size = vhost_svq_driver_area_size(svq);
390
+ device_size = vhost_svq_device_area_size(svq);
391
+ svq->vring.desc = qemu_memalign(qemu_real_host_page_size, driver_size);
392
+ desc_size = sizeof(vring_desc_t) * svq->vring.num;
393
+ svq->vring.avail = (void *)((char *)svq->vring.desc + desc_size);
394
+ memset(svq->vring.desc, 0, driver_size);
395
+ svq->vring.used = qemu_memalign(qemu_real_host_page_size, device_size);
396
+ memset(svq->vring.used, 0, device_size);
397
+ svq->ring_id_maps = g_new0(VirtQueueElement *, svq->vring.num);
398
+ for (unsigned i = 0; i < svq->vring.num - 1; i++) {
399
+ svq->vring.desc[i].next = cpu_to_le16(i + 1);
400
}
401
}
402
403
@@ -XXX,XX +XXX,XX @@ void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd)
404
void vhost_svq_stop(VhostShadowVirtqueue *svq)
405
{
406
event_notifier_set_handler(&svq->svq_kick, NULL);
407
+ g_autofree VirtQueueElement *next_avail_elem = NULL;
408
+
409
+ if (!svq->vq) {
410
+ return;
411
+ }
412
+
413
+ /* Send all pending used descriptors to guest */
414
+ vhost_svq_flush(svq, false);
415
+
416
+ for (unsigned i = 0; i < svq->vring.num; ++i) {
417
+ g_autofree VirtQueueElement *elem = NULL;
418
+ elem = g_steal_pointer(&svq->ring_id_maps[i]);
419
+ if (elem) {
420
+ virtqueue_detach_element(svq->vq, elem, 0);
421
+ }
422
+ }
423
+
424
+ next_avail_elem = g_steal_pointer(&svq->next_guest_avail_elem);
425
+ if (next_avail_elem) {
426
+ virtqueue_detach_element(svq->vq, next_avail_elem, 0);
427
+ }
428
+ svq->vq = NULL;
429
+ g_free(svq->ring_id_maps);
430
+ qemu_vfree(svq->vring.desc);
431
+ qemu_vfree(svq->vring.used);
432
}
433
434
/**
435
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
436
index XXXXXXX..XXXXXXX 100644
437
--- a/hw/virtio/vhost-shadow-virtqueue.h
438
+++ b/hw/virtio/vhost-shadow-virtqueue.h
439
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
440
441
/* Guest's call notifier, where the SVQ calls guest. */
442
EventNotifier svq_call;
443
+
444
+ /* Virtio queue shadowing */
445
+ VirtQueue *vq;
446
+
447
+ /* Virtio device */
448
+ VirtIODevice *vdev;
449
+
450
+ /* Map for use the guest's descriptors */
451
+ VirtQueueElement **ring_id_maps;
452
+
453
+ /* Next VirtQueue element that guest made available */
454
+ VirtQueueElement *next_guest_avail_elem;
455
+
456
+ /* Next head to expose to the device */
457
+ uint16_t shadow_avail_idx;
458
+
459
+ /* Next free descriptor */
460
+ uint16_t free_head;
461
+
462
+ /* Last seen used idx */
463
+ uint16_t shadow_used_idx;
464
+
465
+ /* Next head to consume from the device */
466
+ uint16_t last_used_idx;
467
} VhostShadowVirtqueue;
468
469
bool vhost_svq_valid_features(uint64_t features, Error **errp);
470
@@ -XXX,XX +XXX,XX @@ void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
471
size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq);
472
size_t vhost_svq_device_area_size(const VhostShadowVirtqueue *svq);
473
474
+void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
475
+ VirtQueue *vq);
476
void vhost_svq_stop(VhostShadowVirtqueue *svq);
477
478
VhostShadowVirtqueue *vhost_svq_new(void);
479
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
480
index XXXXXXX..XXXXXXX 100644
481
--- a/hw/virtio/vhost-vdpa.c
482
+++ b/hw/virtio/vhost-vdpa.c
483
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_dev_addr(struct vhost_dev *dev,
484
* Note that this function does not rewind kick file descriptor if cannot set
485
* call one.
486
*/
487
-static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
488
- VhostShadowVirtqueue *svq, unsigned idx,
489
- Error **errp)
490
+static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev,
491
+ VhostShadowVirtqueue *svq, unsigned idx,
492
+ Error **errp)
493
{
494
struct vhost_vring_file file = {
495
.index = dev->vq_index + idx,
496
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
497
r = vhost_vdpa_set_vring_dev_kick(dev, &file);
498
if (unlikely(r != 0)) {
499
error_setg_errno(errp, -r, "Can't set device kick fd");
500
- return false;
501
+ return r;
502
}
503
504
event_notifier = &svq->hdev_call;
505
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
506
error_setg_errno(errp, -r, "Can't set device call fd");
507
}
508
509
+ return r;
510
+}
511
+
512
+/**
513
+ * Unmap a SVQ area in the device
514
+ */
515
+static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, hwaddr iova,
516
+ hwaddr size)
517
+{
518
+ int r;
519
+
520
+ size = ROUND_UP(size, qemu_real_host_page_size);
521
+ r = vhost_vdpa_dma_unmap(v, iova, size);
522
+ return r == 0;
523
+}
524
+
525
+static bool vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev,
526
+ const VhostShadowVirtqueue *svq)
527
+{
528
+ struct vhost_vdpa *v = dev->opaque;
529
+ struct vhost_vring_addr svq_addr;
530
+ size_t device_size = vhost_svq_device_area_size(svq);
531
+ size_t driver_size = vhost_svq_driver_area_size(svq);
532
+ bool ok;
533
+
534
+ vhost_svq_get_vring_addr(svq, &svq_addr);
535
+
536
+ ok = vhost_vdpa_svq_unmap_ring(v, svq_addr.desc_user_addr, driver_size);
537
+ if (unlikely(!ok)) {
538
+ return false;
539
+ }
540
+
541
+ return vhost_vdpa_svq_unmap_ring(v, svq_addr.used_user_addr, device_size);
542
+}
543
+
544
+/**
545
+ * Map the shadow virtqueue rings in the device
546
+ *
547
+ * @dev: The vhost device
548
+ * @svq: The shadow virtqueue
549
+ * @addr: Assigned IOVA addresses
550
+ * @errp: Error pointer
551
+ */
552
+static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev,
553
+ const VhostShadowVirtqueue *svq,
554
+ struct vhost_vring_addr *addr,
555
+ Error **errp)
556
+{
557
+ struct vhost_vdpa *v = dev->opaque;
558
+ size_t device_size = vhost_svq_device_area_size(svq);
559
+ size_t driver_size = vhost_svq_driver_area_size(svq);
560
+ int r;
561
+
562
+ ERRP_GUARD();
563
+ vhost_svq_get_vring_addr(svq, addr);
564
+
565
+ r = vhost_vdpa_dma_map(v, addr->desc_user_addr, driver_size,
566
+ (void *)(uintptr_t)addr->desc_user_addr, true);
567
+ if (unlikely(r != 0)) {
568
+ error_setg_errno(errp, -r, "Cannot create vq driver region: ");
569
+ return false;
570
+ }
571
+
572
+ r = vhost_vdpa_dma_map(v, addr->used_user_addr, device_size,
573
+ (void *)(intptr_t)addr->used_user_addr, false);
574
+ if (unlikely(r != 0)) {
575
+ error_setg_errno(errp, -r, "Cannot create vq device region: ");
576
+ }
577
+
578
+ return r == 0;
579
+}
580
+
581
+static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
582
+ VhostShadowVirtqueue *svq, unsigned idx,
583
+ Error **errp)
584
+{
585
+ uint16_t vq_index = dev->vq_index + idx;
586
+ struct vhost_vring_state s = {
587
+ .index = vq_index,
588
+ };
589
+ int r;
590
+
591
+ r = vhost_vdpa_set_dev_vring_base(dev, &s);
592
+ if (unlikely(r)) {
593
+ error_setg_errno(errp, -r, "Cannot set vring base");
594
+ return false;
595
+ }
596
+
597
+ r = vhost_vdpa_svq_set_fds(dev, svq, idx, errp);
598
return r == 0;
599
}
600
601
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev)
602
}
603
604
for (i = 0; i < v->shadow_vqs->len; ++i) {
605
+ VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i);
606
VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i);
607
+ struct vhost_vring_addr addr = {
608
+ .index = i,
609
+ };
610
+ int r;
611
bool ok = vhost_vdpa_svq_setup(dev, svq, i, &err);
612
if (unlikely(!ok)) {
613
- error_reportf_err(err, "Cannot setup SVQ %u: ", i);
614
+ goto err;
615
+ }
616
+
617
+ vhost_svq_start(svq, dev->vdev, vq);
618
+ ok = vhost_vdpa_svq_map_rings(dev, svq, &addr, &err);
619
+ if (unlikely(!ok)) {
620
+ goto err_map;
621
+ }
622
+
623
+ /* Override vring GPA set by vhost subsystem */
624
+ r = vhost_vdpa_set_vring_dev_addr(dev, &addr);
625
+ if (unlikely(r != 0)) {
626
+ error_setg_errno(&err, -r, "Cannot set device address");
627
+ goto err_set_addr;
628
+ }
629
+ }
630
+
631
+ return true;
632
+
633
+err_set_addr:
634
+ vhost_vdpa_svq_unmap_rings(dev, g_ptr_array_index(v->shadow_vqs, i));
635
+
636
+err_map:
637
+ vhost_svq_stop(g_ptr_array_index(v->shadow_vqs, i));
638
+
639
+err:
640
+ error_reportf_err(err, "Cannot setup SVQ %u: ", i);
641
+ for (unsigned j = 0; j < i; ++j) {
642
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, j);
643
+ vhost_vdpa_svq_unmap_rings(dev, svq);
644
+ vhost_svq_stop(svq);
645
+ }
646
+
647
+ return false;
648
+}
649
+
650
+static bool vhost_vdpa_svqs_stop(struct vhost_dev *dev)
651
+{
652
+ struct vhost_vdpa *v = dev->opaque;
653
+
654
+ if (!v->shadow_vqs) {
655
+ return true;
656
+ }
657
+
658
+ for (unsigned i = 0; i < v->shadow_vqs->len; ++i) {
659
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i);
660
+ bool ok = vhost_vdpa_svq_unmap_rings(dev, svq);
661
+ if (unlikely(!ok)) {
662
return false;
663
}
664
}
665
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
666
}
667
vhost_vdpa_set_vring_ready(dev);
668
} else {
669
+ ok = vhost_vdpa_svqs_stop(dev);
670
+ if (unlikely(!ok)) {
671
+ return -1;
672
+ }
673
vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs);
674
}
675
60
--
676
--
61
2.7.4
677
2.7.4
62
678
63
679
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
To know the device features is needed for CVQ SVQ, so SVQ knows if it
3
This iova tree function allows it to look for a hole in allocated
4
can handle all commands or not. Extract from
4
regions and return a totally new translation for a given translated
5
vhost_vdpa_get_max_queue_pairs so we can reuse it.
5
address.
6
7
It's usage is mainly to allow devices to access qemu address space,
8
remapping guest's one into a new iova space where qemu can add chunks of
9
addresses.
6
10
7
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
11
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
8
Acked-by: Jason Wang <jasowang@redhat.com>
12
Reviewed-by: Peter Xu <peterx@redhat.com>
9
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
13
Acked-by: Michael S. Tsirkin <mst@redhat.com>
10
Signed-off-by: Jason Wang <jasowang@redhat.com>
14
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
---
15
---
12
net/vhost-vdpa.c | 30 ++++++++++++++++++++----------
16
include/qemu/iova-tree.h | 18 +++++++
13
1 file changed, 20 insertions(+), 10 deletions(-)
17
util/iova-tree.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++
14
18
2 files changed, 154 insertions(+)
15
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
19
20
diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h
16
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
17
--- a/net/vhost-vdpa.c
22
--- a/include/qemu/iova-tree.h
18
+++ b/net/vhost-vdpa.c
23
+++ b/include/qemu/iova-tree.h
19
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
24
@@ -XXX,XX +XXX,XX @@
20
return nc;
25
#define IOVA_OK (0)
26
#define IOVA_ERR_INVALID (-1) /* Invalid parameters */
27
#define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */
28
+#define IOVA_ERR_NOMEM (-3) /* Cannot allocate */
29
30
typedef struct IOVATree IOVATree;
31
typedef struct DMAMap {
32
@@ -XXX,XX +XXX,XX @@ const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova);
33
void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator);
34
35
/**
36
+ * iova_tree_alloc_map:
37
+ *
38
+ * @tree: the iova tree to allocate from
39
+ * @map: the new map (as translated addr & size) to allocate in the iova region
40
+ * @iova_begin: the minimum address of the allocation
41
+ * @iova_end: the maximum addressable direction of the allocation
42
+ *
43
+ * Allocates a new region of a given size, between iova_min and iova_max.
44
+ *
45
+ * Return: Same as iova_tree_insert, but cannot overlap and can return error if
46
+ * iova tree is out of free contiguous range. The caller gets the assigned iova
47
+ * in map->iova.
48
+ */
49
+int iova_tree_alloc_map(IOVATree *tree, DMAMap *map, hwaddr iova_begin,
50
+ hwaddr iova_end);
51
+
52
+/**
53
* iova_tree_destroy:
54
*
55
* @tree: the iova tree to destroy
56
diff --git a/util/iova-tree.c b/util/iova-tree.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/util/iova-tree.c
59
+++ b/util/iova-tree.c
60
@@ -XXX,XX +XXX,XX @@ struct IOVATree {
61
GTree *tree;
62
};
63
64
+/* Args to pass to iova_tree_alloc foreach function. */
65
+struct IOVATreeAllocArgs {
66
+ /* Size of the desired allocation */
67
+ size_t new_size;
68
+
69
+ /* The minimum address allowed in the allocation */
70
+ hwaddr iova_begin;
71
+
72
+ /* Map at the left of the hole, can be NULL if "this" is first one */
73
+ const DMAMap *prev;
74
+
75
+ /* Map at the right of the hole, can be NULL if "prev" is the last one */
76
+ const DMAMap *this;
77
+
78
+ /* If found, we fill in the IOVA here */
79
+ hwaddr iova_result;
80
+
81
+ /* Whether have we found a valid IOVA */
82
+ bool iova_found;
83
+};
84
+
85
+/**
86
+ * Iterate args to the next hole
87
+ *
88
+ * @args: The alloc arguments
89
+ * @next: The next mapping in the tree. Can be NULL to signal the last one
90
+ */
91
+static void iova_tree_alloc_args_iterate(struct IOVATreeAllocArgs *args,
92
+ const DMAMap *next)
93
+{
94
+ args->prev = args->this;
95
+ args->this = next;
96
+}
97
+
98
static int iova_tree_compare(gconstpointer a, gconstpointer b, gpointer data)
99
{
100
const DMAMap *m1 = a, *m2 = b;
101
@@ -XXX,XX +XXX,XX @@ int iova_tree_remove(IOVATree *tree, const DMAMap *map)
102
return IOVA_OK;
21
}
103
}
22
104
23
-static int vhost_vdpa_get_max_queue_pairs(int fd, int *has_cvq, Error **errp)
105
+/**
24
+static int vhost_vdpa_get_features(int fd, uint64_t *features, Error **errp)
106
+ * Try to find an unallocated IOVA range between prev and this elements.
25
+{
107
+ *
26
+ int ret = ioctl(fd, VHOST_GET_FEATURES, features);
108
+ * @args: Arguments to allocation
27
+ if (unlikely(ret < 0)) {
109
+ *
28
+ error_setg_errno(errp, errno,
110
+ * Cases:
29
+ "Fail to query features from vhost-vDPA device");
111
+ *
30
+ }
112
+ * (1) !prev, !this: No entries allocated, always succeed
31
+ return ret;
113
+ *
32
+}
114
+ * (2) !prev, this: We're iterating at the 1st element.
33
+
115
+ *
34
+static int vhost_vdpa_get_max_queue_pairs(int fd, uint64_t features,
116
+ * (3) prev, !this: We're iterating at the last element.
35
+ int *has_cvq, Error **errp)
117
+ *
118
+ * (4) prev, this: this is the most common case, we'll try to find a hole
119
+ * between "prev" and "this" mapping.
120
+ *
121
+ * Note that this function assumes the last valid iova is HWADDR_MAX, but it
122
+ * searches linearly so it's easy to discard the result if it's not the case.
123
+ */
124
+static void iova_tree_alloc_map_in_hole(struct IOVATreeAllocArgs *args)
125
+{
126
+ const DMAMap *prev = args->prev, *this = args->this;
127
+ uint64_t hole_start, hole_last;
128
+
129
+ if (this && this->iova + this->size < args->iova_begin) {
130
+ return;
131
+ }
132
+
133
+ hole_start = MAX(prev ? prev->iova + prev->size + 1 : 0, args->iova_begin);
134
+ hole_last = this ? this->iova : HWADDR_MAX;
135
+
136
+ if (hole_last - hole_start > args->new_size) {
137
+ args->iova_result = hole_start;
138
+ args->iova_found = true;
139
+ }
140
+}
141
+
142
+/**
143
+ * Foreach dma node in the tree, compare if there is a hole with its previous
144
+ * node (or minimum iova address allowed) and the node.
145
+ *
146
+ * @key: Node iterating
147
+ * @value: Node iterating
148
+ * @pargs: Struct to communicate with the outside world
149
+ *
150
+ * Return: false to keep iterating, true if needs break.
151
+ */
152
+static gboolean iova_tree_alloc_traverse(gpointer key, gpointer value,
153
+ gpointer pargs)
154
+{
155
+ struct IOVATreeAllocArgs *args = pargs;
156
+ DMAMap *node = value;
157
+
158
+ assert(key == value);
159
+
160
+ iova_tree_alloc_args_iterate(args, node);
161
+ iova_tree_alloc_map_in_hole(args);
162
+ return args->iova_found;
163
+}
164
+
165
+int iova_tree_alloc_map(IOVATree *tree, DMAMap *map, hwaddr iova_begin,
166
+ hwaddr iova_last)
167
+{
168
+ struct IOVATreeAllocArgs args = {
169
+ .new_size = map->size,
170
+ .iova_begin = iova_begin,
171
+ };
172
+
173
+ if (unlikely(iova_last < iova_begin)) {
174
+ return IOVA_ERR_INVALID;
175
+ }
176
+
177
+ /*
178
+ * Find a valid hole for the mapping
179
+ *
180
+ * Assuming low iova_begin, so no need to do a binary search to
181
+ * locate the first node.
182
+ *
183
+ * TODO: Replace all this with g_tree_node_first/next/last when available
184
+ * (from glib since 2.68). To do it with g_tree_foreach complicates the
185
+ * code a lot.
186
+ *
187
+ */
188
+ g_tree_foreach(tree->tree, iova_tree_alloc_traverse, &args);
189
+ if (!args.iova_found) {
190
+ /*
191
+ * Either tree is empty or the last hole is still not checked.
192
+ * g_tree_foreach does not compare (last, iova_last] range, so we check
193
+ * it here.
194
+ */
195
+ iova_tree_alloc_args_iterate(&args, NULL);
196
+ iova_tree_alloc_map_in_hole(&args);
197
+ }
198
+
199
+ if (!args.iova_found || args.iova_result + map->size > iova_last) {
200
+ return IOVA_ERR_NOMEM;
201
+ }
202
+
203
+ map->iova = args.iova_result;
204
+ return iova_tree_insert(tree, map);
205
+}
206
+
207
void iova_tree_destroy(IOVATree *tree)
36
{
208
{
37
unsigned long config_size = offsetof(struct vhost_vdpa_config, buf);
209
g_tree_destroy(tree->tree);
38
g_autofree struct vhost_vdpa_config *config = NULL;
39
__virtio16 *max_queue_pairs;
40
- uint64_t features;
41
int ret;
42
43
- ret = ioctl(fd, VHOST_GET_FEATURES, &features);
44
- if (ret) {
45
- error_setg(errp, "Fail to query features from vhost-vDPA device");
46
- return ret;
47
- }
48
-
49
if (features & (1 << VIRTIO_NET_F_CTRL_VQ)) {
50
*has_cvq = 1;
51
} else {
52
@@ -XXX,XX +XXX,XX @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
53
NetClientState *peer, Error **errp)
54
{
55
const NetdevVhostVDPAOptions *opts;
56
+ uint64_t features;
57
int vdpa_device_fd;
58
g_autofree NetClientState **ncs = NULL;
59
NetClientState *nc;
60
- int queue_pairs, i, has_cvq = 0;
61
+ int queue_pairs, r, i, has_cvq = 0;
62
63
assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA);
64
opts = &netdev->u.vhost_vdpa;
65
@@ -XXX,XX +XXX,XX @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
66
return -errno;
67
}
68
69
- queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd,
70
+ r = vhost_vdpa_get_features(vdpa_device_fd, &features, errp);
71
+ if (unlikely(r < 0)) {
72
+ return r;
73
+ }
74
+
75
+ queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, features,
76
&has_cvq, errp);
77
if (queue_pairs < 0) {
78
qemu_close(vdpa_device_fd);
79
--
210
--
80
2.7.4
211
2.7.4
81
212
82
213
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Introduce the control virtqueue support for vDPA shadow virtqueue. This
3
This function does the reverse operation of iova_tree_find: To look for
4
is needed for advanced networking features like rx filtering.
4
a mapping that match a translated address so we can do the reverse.
5
5
6
Virtio-net control VQ copies the descriptors to qemu's VA, so we avoid
6
This have linear complexity instead of logarithmic, but it supports
7
TOCTOU with the guest's or device's memory every time there is a device
7
overlapping HVA. Future developments could reduce it.
8
model change. Otherwise, the guest could change the memory content in
9
the time between qemu and the device read it.
10
11
To demonstrate command handling, VIRTIO_NET_F_CTRL_MACADDR is
12
implemented. If the virtio-net driver changes MAC the virtio-net device
13
model will be updated with the new one, and a rx filtering change event
14
will be raised.
15
16
More cvq commands could be added here straightforwardly but they have
17
not been tested.
18
8
19
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
9
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
20
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
10
Acked-by: Michael S. Tsirkin <mst@redhat.com>
21
Signed-off-by: Jason Wang <jasowang@redhat.com>
11
Signed-off-by: Jason Wang <jasowang@redhat.com>
22
---
12
---
23
net/vhost-vdpa.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
13
include/qemu/iova-tree.h | 20 +++++++++++++++++++-
24
1 file changed, 205 insertions(+), 8 deletions(-)
14
util/iova-tree.c | 34 ++++++++++++++++++++++++++++++++++
15
2 files changed, 53 insertions(+), 1 deletion(-)
25
16
26
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
17
diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h
27
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
28
--- a/net/vhost-vdpa.c
19
--- a/include/qemu/iova-tree.h
29
+++ b/net/vhost-vdpa.c
20
+++ b/include/qemu/iova-tree.h
30
@@ -XXX,XX +XXX,XX @@ typedef struct VhostVDPAState {
21
@@ -XXX,XX +XXX,XX @@ int iova_tree_remove(IOVATree *tree, const DMAMap *map);
31
NetClientState nc;
22
* @tree: the iova tree to search from
32
struct vhost_vdpa vhost_vdpa;
23
* @map: the mapping to search
33
VHostNetState *vhost_net;
24
*
25
- * Search for a mapping in the iova tree that overlaps with the
26
+ * Search for a mapping in the iova tree that iova overlaps with the
27
* mapping range specified. Only the first found mapping will be
28
* returned.
29
*
30
@@ -XXX,XX +XXX,XX @@ int iova_tree_remove(IOVATree *tree, const DMAMap *map);
31
const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map);
32
33
/**
34
+ * iova_tree_find_iova:
35
+ *
36
+ * @tree: the iova tree to search from
37
+ * @map: the mapping to search
38
+ *
39
+ * Search for a mapping in the iova tree that translated_addr overlaps with the
40
+ * mapping range specified. Only the first found mapping will be
41
+ * returned.
42
+ *
43
+ * Return: DMAMap pointer if found, or NULL if not found. Note that
44
+ * the returned DMAMap pointer is maintained internally. User should
45
+ * only read the content but never modify or free the content. Also,
46
+ * user is responsible to make sure the pointer is valid (say, no
47
+ * concurrent deletion in progress).
48
+ */
49
+const DMAMap *iova_tree_find_iova(const IOVATree *tree, const DMAMap *map);
34
+
50
+
35
+ /* Control commands shadow buffers */
51
+/**
36
+ void *cvq_cmd_out_buffer, *cvq_cmd_in_buffer;
52
* iova_tree_find_address:
37
bool started;
53
*
38
} VhostVDPAState;
54
* @tree: the iova tree to search from
39
55
diff --git a/util/iova-tree.c b/util/iova-tree.c
40
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_cleanup(NetClientState *nc)
56
index XXXXXXX..XXXXXXX 100644
41
{
57
--- a/util/iova-tree.c
42
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
58
+++ b/util/iova-tree.c
43
59
@@ -XXX,XX +XXX,XX @@ struct IOVATreeAllocArgs {
44
+ qemu_vfree(s->cvq_cmd_out_buffer);
60
bool iova_found;
45
+ qemu_vfree(s->cvq_cmd_in_buffer);
46
if (s->vhost_net) {
47
vhost_net_cleanup(s->vhost_net);
48
g_free(s->vhost_net);
49
@@ -XXX,XX +XXX,XX @@ static NetClientInfo net_vhost_vdpa_info = {
50
.check_peer_type = vhost_vdpa_check_peer_type,
51
};
61
};
52
62
53
+static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr)
63
+typedef struct IOVATreeFindIOVAArgs {
64
+ const DMAMap *needle;
65
+ const DMAMap *result;
66
+} IOVATreeFindIOVAArgs;
67
+
68
/**
69
* Iterate args to the next hole
70
*
71
@@ -XXX,XX +XXX,XX @@ const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map)
72
return g_tree_lookup(tree->tree, map);
73
}
74
75
+static gboolean iova_tree_find_address_iterator(gpointer key, gpointer value,
76
+ gpointer data)
54
+{
77
+{
55
+ VhostIOVATree *tree = v->iova_tree;
78
+ const DMAMap *map = key;
56
+ DMAMap needle = {
79
+ IOVATreeFindIOVAArgs *args = data;
57
+ /*
80
+ const DMAMap *needle;
58
+ * No need to specify size or to look for more translations since
59
+ * this contiguous chunk was allocated by us.
60
+ */
61
+ .translated_addr = (hwaddr)(uintptr_t)addr,
62
+ };
63
+ const DMAMap *map = vhost_iova_tree_find_iova(tree, &needle);
64
+ int r;
65
+
81
+
66
+ if (unlikely(!map)) {
82
+ g_assert(key == value);
67
+ error_report("Cannot locate expected map");
68
+ return;
69
+ }
70
+
83
+
71
+ r = vhost_vdpa_dma_unmap(v, map->iova, map->size + 1);
84
+ needle = args->needle;
72
+ if (unlikely(r != 0)) {
85
+ if (map->translated_addr + map->size < needle->translated_addr ||
73
+ error_report("Device cannot unmap: %s(%d)", g_strerror(r), r);
86
+ needle->translated_addr + needle->size < map->translated_addr) {
74
+ }
75
+
76
+ vhost_iova_tree_remove(tree, map);
77
+}
78
+
79
+static size_t vhost_vdpa_net_cvq_cmd_len(void)
80
+{
81
+ /*
82
+ * MAC_TABLE_SET is the ctrl command that produces the longer out buffer.
83
+ * In buffer is always 1 byte, so it should fit here
84
+ */
85
+ return sizeof(struct virtio_net_ctrl_hdr) +
86
+ 2 * sizeof(struct virtio_net_ctrl_mac) +
87
+ MAC_TABLE_ENTRIES * ETH_ALEN;
88
+}
89
+
90
+static size_t vhost_vdpa_net_cvq_cmd_page_len(void)
91
+{
92
+ return ROUND_UP(vhost_vdpa_net_cvq_cmd_len(), qemu_real_host_page_size());
93
+}
94
+
95
+/** Copy and map a guest buffer. */
96
+static bool vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v,
97
+ const struct iovec *out_data,
98
+ size_t out_num, size_t data_len, void *buf,
99
+ size_t *written, bool write)
100
+{
101
+ DMAMap map = {};
102
+ int r;
103
+
104
+ if (unlikely(!data_len)) {
105
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid legnth of %s buffer\n",
106
+ __func__, write ? "in" : "out");
107
+ return false;
87
+ return false;
108
+ }
88
+ }
109
+
89
+
110
+ *written = iov_to_buf(out_data, out_num, 0, buf, data_len);
90
+ args->result = map;
111
+ map.translated_addr = (hwaddr)(uintptr_t)buf;
112
+ map.size = vhost_vdpa_net_cvq_cmd_page_len() - 1;
113
+ map.perm = write ? IOMMU_RW : IOMMU_RO,
114
+ r = vhost_iova_tree_map_alloc(v->iova_tree, &map);
115
+ if (unlikely(r != IOVA_OK)) {
116
+ error_report("Cannot map injected element");
117
+ return false;
118
+ }
119
+
120
+ r = vhost_vdpa_dma_map(v, map.iova, vhost_vdpa_net_cvq_cmd_page_len(), buf,
121
+ !write);
122
+ if (unlikely(r < 0)) {
123
+ goto dma_map_err;
124
+ }
125
+
126
+ return true;
127
+
128
+dma_map_err:
129
+ vhost_iova_tree_remove(v->iova_tree, &map);
130
+ return false;
131
+}
132
+
133
/**
134
- * Forward buffer for the moment.
135
+ * Copy the guest element into a dedicated buffer suitable to be sent to NIC
136
+ *
137
+ * @iov: [0] is the out buffer, [1] is the in one
138
+ */
139
+static bool vhost_vdpa_net_cvq_map_elem(VhostVDPAState *s,
140
+ VirtQueueElement *elem,
141
+ struct iovec *iov)
142
+{
143
+ size_t in_copied;
144
+ bool ok;
145
+
146
+ iov[0].iov_base = s->cvq_cmd_out_buffer;
147
+ ok = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, elem->out_sg, elem->out_num,
148
+ vhost_vdpa_net_cvq_cmd_len(), iov[0].iov_base,
149
+ &iov[0].iov_len, false);
150
+ if (unlikely(!ok)) {
151
+ return false;
152
+ }
153
+
154
+ iov[1].iov_base = s->cvq_cmd_in_buffer;
155
+ ok = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, NULL, 0,
156
+ sizeof(virtio_net_ctrl_ack), iov[1].iov_base,
157
+ &in_copied, true);
158
+ if (unlikely(!ok)) {
159
+ vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer);
160
+ return false;
161
+ }
162
+
163
+ iov[1].iov_len = sizeof(virtio_net_ctrl_ack);
164
+ return true;
91
+ return true;
165
+}
92
+}
166
+
93
+
167
+/**
94
+const DMAMap *iova_tree_find_iova(const IOVATree *tree, const DMAMap *map)
168
+ * Do not forward commands not supported by SVQ. Otherwise, the device could
169
+ * accept it and qemu would not know how to update the device model.
170
+ */
171
+static bool vhost_vdpa_net_cvq_validate_cmd(const struct iovec *out,
172
+ size_t out_num)
173
+{
95
+{
174
+ struct virtio_net_ctrl_hdr ctrl;
96
+ IOVATreeFindIOVAArgs args = {
175
+ size_t n;
97
+ .needle = map,
176
+
177
+ n = iov_to_buf(out, out_num, 0, &ctrl, sizeof(ctrl));
178
+ if (unlikely(n < sizeof(ctrl))) {
179
+ qemu_log_mask(LOG_GUEST_ERROR,
180
+ "%s: invalid legnth of out buffer %zu\n", __func__, n);
181
+ return false;
182
+ }
183
+
184
+ switch (ctrl.class) {
185
+ case VIRTIO_NET_CTRL_MAC:
186
+ switch (ctrl.cmd) {
187
+ case VIRTIO_NET_CTRL_MAC_ADDR_SET:
188
+ return true;
189
+ default:
190
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid mac cmd %u\n",
191
+ __func__, ctrl.cmd);
192
+ };
193
+ break;
194
+ default:
195
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid control class %u\n",
196
+ __func__, ctrl.class);
197
+ };
98
+ };
198
+
99
+
199
+ return false;
100
+ g_tree_foreach(tree->tree, iova_tree_find_address_iterator, &args);
101
+ return args.result;
200
+}
102
+}
201
+
103
+
202
+/**
104
const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova)
203
+ * Validate and copy control virtqueue commands.
204
+ *
205
+ * Following QEMU guidelines, we offer a copy of the buffers to the device to
206
+ * prevent TOCTOU bugs.
207
*/
208
static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
209
VirtQueueElement *elem,
210
void *opaque)
211
{
105
{
212
- unsigned int n = elem->out_num + elem->in_num;
106
const DMAMap map = { .iova = iova, .size = 0 };
213
- g_autofree struct iovec *dev_buffers = g_new(struct iovec, n);
214
+ VhostVDPAState *s = opaque;
215
size_t in_len, dev_written;
216
virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
217
- int r;
218
+ /* out and in buffers sent to the device */
219
+ struct iovec dev_buffers[2] = {
220
+ { .iov_base = s->cvq_cmd_out_buffer },
221
+ { .iov_base = s->cvq_cmd_in_buffer },
222
+ };
223
+ /* in buffer used for device model */
224
+ const struct iovec in = {
225
+ .iov_base = &status,
226
+ .iov_len = sizeof(status),
227
+ };
228
+ int r = -EINVAL;
229
+ bool ok;
230
+
231
+ ok = vhost_vdpa_net_cvq_map_elem(s, elem, dev_buffers);
232
+ if (unlikely(!ok)) {
233
+ goto out;
234
+ }
235
236
- memcpy(dev_buffers, elem->out_sg, elem->out_num);
237
- memcpy(dev_buffers + elem->out_num, elem->in_sg, elem->in_num);
238
+ ok = vhost_vdpa_net_cvq_validate_cmd(&dev_buffers[0], 1);
239
+ if (unlikely(!ok)) {
240
+ goto out;
241
+ }
242
243
- r = vhost_svq_add(svq, &dev_buffers[0], elem->out_num, &dev_buffers[1],
244
- elem->in_num, elem);
245
+ r = vhost_svq_add(svq, &dev_buffers[0], 1, &dev_buffers[1], 1, elem);
246
if (unlikely(r != 0)) {
247
if (unlikely(r == -ENOSPC)) {
248
qemu_log_mask(LOG_GUEST_ERROR, "%s: No space on device queue\n",
249
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq,
250
dev_written = vhost_svq_poll(svq);
251
if (unlikely(dev_written < sizeof(status))) {
252
error_report("Insufficient written data (%zu)", dev_written);
253
+ goto out;
254
+ }
255
+
256
+ memcpy(&status, dev_buffers[1].iov_base, sizeof(status));
257
+ if (status != VIRTIO_NET_OK) {
258
+ goto out;
259
+ }
260
+
261
+ status = VIRTIO_NET_ERR;
262
+ virtio_net_handle_ctrl_iov(svq->vdev, &in, 1, dev_buffers, 1);
263
+ if (status != VIRTIO_NET_OK) {
264
+ error_report("Bad CVQ processing in model");
265
}
266
267
out:
268
@@ -XXX,XX +XXX,XX @@ out:
269
}
270
vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status)));
271
g_free(elem);
272
+ if (dev_buffers[0].iov_base) {
273
+ vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, dev_buffers[0].iov_base);
274
+ }
275
+ if (dev_buffers[1].iov_base) {
276
+ vhost_vdpa_cvq_unmap_buf(&s->vhost_vdpa, dev_buffers[1].iov_base);
277
+ }
278
return r;
279
}
280
281
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
282
s->vhost_vdpa.device_fd = vdpa_device_fd;
283
s->vhost_vdpa.index = queue_pair_index;
284
if (!is_datapath) {
285
+ s->cvq_cmd_out_buffer = qemu_memalign(qemu_real_host_page_size(),
286
+ vhost_vdpa_net_cvq_cmd_page_len());
287
+ memset(s->cvq_cmd_out_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len());
288
+ s->cvq_cmd_in_buffer = qemu_memalign(qemu_real_host_page_size(),
289
+ vhost_vdpa_net_cvq_cmd_page_len());
290
+ memset(s->cvq_cmd_in_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len());
291
+
292
s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops;
293
s->vhost_vdpa.shadow_vq_ops_opaque = s;
294
}
295
--
107
--
296
2.7.4
108
2.7.4
297
109
298
110
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Finally offering the possibility to enable SVQ from the command line.
3
This tree is able to look for a translated address from an IOVA address.
4
5
At first glance it is similar to util/iova-tree. However, SVQ working on
6
devices with limited IOVA space need more capabilities, like allocating
7
IOVA chunks or performing reverse translations (qemu addresses to iova).
8
9
The allocation capability, as "assign a free IOVA address to this chunk
10
of memory in qemu's address space" allows shadow virtqueue to create a
11
new address space that is not restricted by guest's addressable one, so
12
we can allocate shadow vqs vrings outside of it.
13
14
It duplicates the tree so it can search efficiently in both directions,
15
and it will signal overlap if iova or the translated address is present
16
in any tree.
4
17
5
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
18
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
6
Acked-by: Markus Armbruster <armbru@redhat.com>
19
Acked-by: Michael S. Tsirkin <mst@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
20
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
21
---
10
net/vhost-vdpa.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
22
hw/virtio/meson.build | 2 +-
11
qapi/net.json | 9 ++++++-
23
hw/virtio/vhost-iova-tree.c | 110 ++++++++++++++++++++++++++++++++++++++++++++
12
2 files changed, 77 insertions(+), 4 deletions(-)
24
hw/virtio/vhost-iova-tree.h | 27 +++++++++++
25
3 files changed, 138 insertions(+), 1 deletion(-)
26
create mode 100644 hw/virtio/vhost-iova-tree.c
27
create mode 100644 hw/virtio/vhost-iova-tree.h
13
28
14
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
29
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
15
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
16
--- a/net/vhost-vdpa.c
31
--- a/hw/virtio/meson.build
17
+++ b/net/vhost-vdpa.c
32
+++ b/hw/virtio/meson.build
18
@@ -XXX,XX +XXX,XX @@ const int vdpa_feature_bits[] = {
33
@@ -XXX,XX +XXX,XX @@ softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c'))
19
VHOST_INVALID_FEATURE_BIT
34
20
};
35
virtio_ss = ss.source_set()
21
36
virtio_ss.add(files('virtio.c'))
22
+/** Supported device specific feature bits with SVQ */
37
-virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c', 'vhost-shadow-virtqueue.c'))
23
+static const uint64_t vdpa_svq_device_features =
38
+virtio_ss.add(when: 'CONFIG_VHOST', if_true: files('vhost.c', 'vhost-backend.c', 'vhost-shadow-virtqueue.c', 'vhost-iova-tree.c'))
24
+ BIT_ULL(VIRTIO_NET_F_CSUM) |
39
virtio_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user.c'))
25
+ BIT_ULL(VIRTIO_NET_F_GUEST_CSUM) |
40
virtio_ss.add(when: 'CONFIG_VHOST_VDPA', if_true: files('vhost-vdpa.c'))
26
+ BIT_ULL(VIRTIO_NET_F_MTU) |
41
virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c'))
27
+ BIT_ULL(VIRTIO_NET_F_MAC) |
42
diff --git a/hw/virtio/vhost-iova-tree.c b/hw/virtio/vhost-iova-tree.c
28
+ BIT_ULL(VIRTIO_NET_F_GUEST_TSO4) |
43
new file mode 100644
29
+ BIT_ULL(VIRTIO_NET_F_GUEST_TSO6) |
44
index XXXXXXX..XXXXXXX
30
+ BIT_ULL(VIRTIO_NET_F_GUEST_ECN) |
45
--- /dev/null
31
+ BIT_ULL(VIRTIO_NET_F_GUEST_UFO) |
46
+++ b/hw/virtio/vhost-iova-tree.c
32
+ BIT_ULL(VIRTIO_NET_F_HOST_TSO4) |
47
@@ -XXX,XX +XXX,XX @@
33
+ BIT_ULL(VIRTIO_NET_F_HOST_TSO6) |
48
+/*
34
+ BIT_ULL(VIRTIO_NET_F_HOST_ECN) |
49
+ * vhost software live migration iova tree
35
+ BIT_ULL(VIRTIO_NET_F_HOST_UFO) |
50
+ *
36
+ BIT_ULL(VIRTIO_NET_F_MRG_RXBUF) |
51
+ * SPDX-FileCopyrightText: Red Hat, Inc. 2021
37
+ BIT_ULL(VIRTIO_NET_F_STATUS) |
52
+ * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
38
+ BIT_ULL(VIRTIO_NET_F_CTRL_VQ) |
53
+ *
39
+ BIT_ULL(VIRTIO_F_ANY_LAYOUT) |
54
+ * SPDX-License-Identifier: GPL-2.0-or-later
40
+ BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR) |
55
+ */
41
+ BIT_ULL(VIRTIO_NET_F_RSC_EXT) |
42
+ BIT_ULL(VIRTIO_NET_F_STANDBY);
43
+
56
+
44
VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc)
57
+#include "qemu/osdep.h"
45
{
58
+#include "qemu/iova-tree.h"
46
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
59
+#include "vhost-iova-tree.h"
47
@@ -XXX,XX +XXX,XX @@ err_init:
60
+
48
static void vhost_vdpa_cleanup(NetClientState *nc)
61
+#define iova_min_addr qemu_real_host_page_size
49
{
62
+
50
VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
63
+/**
51
+ struct vhost_dev *dev = &s->vhost_net->dev;
64
+ * VhostIOVATree, able to:
52
65
+ * - Translate iova address
53
qemu_vfree(s->cvq_cmd_out_buffer);
66
+ * - Reverse translate iova address (from translated to iova)
54
qemu_vfree(s->cvq_cmd_in_buffer);
67
+ * - Allocate IOVA regions for translated range (linear operation)
55
+ if (dev->vq_index + dev->nvqs == dev->vq_index_end) {
68
+ */
56
+ g_clear_pointer(&s->vhost_vdpa.iova_tree, vhost_iova_tree_delete);
69
+struct VhostIOVATree {
57
+ }
70
+ /* First addressable iova address in the device */
58
if (s->vhost_net) {
71
+ uint64_t iova_first;
59
vhost_net_cleanup(s->vhost_net);
72
+
60
g_free(s->vhost_net);
73
+ /* Last addressable iova address in the device */
61
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
74
+ uint64_t iova_last;
62
int vdpa_device_fd,
75
+
63
int queue_pair_index,
76
+ /* IOVA address to qemu memory maps. */
64
int nvqs,
77
+ IOVATree *iova_taddr_map;
65
- bool is_datapath)
78
+};
66
+ bool is_datapath,
79
+
67
+ bool svq,
80
+/**
68
+ VhostIOVATree *iova_tree)
81
+ * Create a new IOVA tree
69
{
82
+ *
70
NetClientState *nc = NULL;
83
+ * Returns the new IOVA tree
71
VhostVDPAState *s;
84
+ */
72
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
85
+VhostIOVATree *vhost_iova_tree_new(hwaddr iova_first, hwaddr iova_last)
73
74
s->vhost_vdpa.device_fd = vdpa_device_fd;
75
s->vhost_vdpa.index = queue_pair_index;
76
+ s->vhost_vdpa.shadow_vqs_enabled = svq;
77
+ s->vhost_vdpa.iova_tree = iova_tree;
78
if (!is_datapath) {
79
s->cvq_cmd_out_buffer = qemu_memalign(qemu_real_host_page_size(),
80
vhost_vdpa_net_cvq_cmd_page_len());
81
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
82
83
s->vhost_vdpa.shadow_vq_ops = &vhost_vdpa_net_svq_ops;
84
s->vhost_vdpa.shadow_vq_ops_opaque = s;
85
+ error_setg(&s->vhost_vdpa.migration_blocker,
86
+ "Migration disabled: vhost-vdpa uses CVQ.");
87
}
88
ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa, queue_pair_index, nvqs);
89
if (ret) {
90
@@ -XXX,XX +XXX,XX @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer,
91
return nc;
92
}
93
94
+static int vhost_vdpa_get_iova_range(int fd,
95
+ struct vhost_vdpa_iova_range *iova_range)
96
+{
86
+{
97
+ int ret = ioctl(fd, VHOST_VDPA_GET_IOVA_RANGE, iova_range);
87
+ VhostIOVATree *tree = g_new(VhostIOVATree, 1);
98
+
88
+
99
+ return ret < 0 ? -errno : 0;
89
+ /* Some devices do not like 0 addresses */
90
+ tree->iova_first = MAX(iova_first, iova_min_addr);
91
+ tree->iova_last = iova_last;
92
+
93
+ tree->iova_taddr_map = iova_tree_new();
94
+ return tree;
100
+}
95
+}
101
+
96
+
102
static int vhost_vdpa_get_features(int fd, uint64_t *features, Error **errp)
97
+/**
103
{
98
+ * Delete an iova tree
104
int ret = ioctl(fd, VHOST_GET_FEATURES, features);
99
+ */
105
@@ -XXX,XX +XXX,XX @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
100
+void vhost_iova_tree_delete(VhostIOVATree *iova_tree)
106
uint64_t features;
101
+{
107
int vdpa_device_fd;
102
+ iova_tree_destroy(iova_tree->iova_taddr_map);
108
g_autofree NetClientState **ncs = NULL;
103
+ g_free(iova_tree);
109
+ g_autoptr(VhostIOVATree) iova_tree = NULL;
104
+}
110
NetClientState *nc;
111
int queue_pairs, r, i, has_cvq = 0;
112
113
@@ -XXX,XX +XXX,XX @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
114
return queue_pairs;
115
}
116
117
+ if (opts->x_svq) {
118
+ struct vhost_vdpa_iova_range iova_range;
119
+
105
+
120
+ uint64_t invalid_dev_features =
106
+/**
121
+ features & ~vdpa_svq_device_features &
107
+ * Find the IOVA address stored from a memory address
122
+ /* Transport are all accepted at this point */
108
+ *
123
+ ~MAKE_64BIT_MASK(VIRTIO_TRANSPORT_F_START,
109
+ * @tree: The iova tree
124
+ VIRTIO_TRANSPORT_F_END - VIRTIO_TRANSPORT_F_START);
110
+ * @map: The map with the memory address
111
+ *
112
+ * Return the stored mapping, or NULL if not found.
113
+ */
114
+const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *tree,
115
+ const DMAMap *map)
116
+{
117
+ return iova_tree_find_iova(tree->iova_taddr_map, map);
118
+}
125
+
119
+
126
+ if (invalid_dev_features) {
120
+/**
127
+ error_setg(errp, "vdpa svq does not work with features 0x%" PRIx64,
121
+ * Allocate a new mapping
128
+ invalid_dev_features);
122
+ *
129
+ goto err_svq;
123
+ * @tree: The iova tree
130
+ }
124
+ * @map: The iova map
125
+ *
126
+ * Returns:
127
+ * - IOVA_OK if the map fits in the container
128
+ * - IOVA_ERR_INVALID if the map does not make sense (like size overflow)
129
+ * - IOVA_ERR_NOMEM if tree cannot allocate more space.
130
+ *
131
+ * It returns assignated iova in map->iova if return value is VHOST_DMA_MAP_OK.
132
+ */
133
+int vhost_iova_tree_map_alloc(VhostIOVATree *tree, DMAMap *map)
134
+{
135
+ /* Some vhost devices do not like addr 0. Skip first page */
136
+ hwaddr iova_first = tree->iova_first ?: qemu_real_host_page_size;
131
+
137
+
132
+ vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range);
138
+ if (map->translated_addr + map->size < map->translated_addr ||
133
+ iova_tree = vhost_iova_tree_new(iova_range.first, iova_range.last);
139
+ map->perm == IOMMU_NONE) {
140
+ return IOVA_ERR_INVALID;
134
+ }
141
+ }
135
+
142
+
136
ncs = g_malloc0(sizeof(*ncs) * queue_pairs);
143
+ /* Allocate a node in IOVA address */
137
144
+ return iova_tree_alloc_map(tree->iova_taddr_map, map, iova_first,
138
for (i = 0; i < queue_pairs; i++) {
145
+ tree->iova_last);
139
ncs[i] = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
146
+}
140
- vdpa_device_fd, i, 2, true);
141
+ vdpa_device_fd, i, 2, true, opts->x_svq,
142
+ iova_tree);
143
if (!ncs[i])
144
goto err;
145
}
146
147
if (has_cvq) {
148
nc = net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name,
149
- vdpa_device_fd, i, 1, false);
150
+ vdpa_device_fd, i, 1, false,
151
+ opts->x_svq, iova_tree);
152
if (!nc)
153
goto err;
154
}
155
156
+ /* iova_tree ownership belongs to last NetClientState */
157
+ g_steal_pointer(&iova_tree);
158
return 0;
159
160
err:
161
@@ -XXX,XX +XXX,XX @@ err:
162
qemu_del_net_client(ncs[i]);
163
}
164
}
165
+
147
+
166
+err_svq:
148
+/**
167
qemu_close(vdpa_device_fd);
149
+ * Remove existing mappings from iova tree
168
150
+ *
169
return -1;
151
+ * @iova_tree: The vhost iova tree
170
diff --git a/qapi/net.json b/qapi/net.json
152
+ * @map: The map to remove
171
index XXXXXXX..XXXXXXX 100644
153
+ */
172
--- a/qapi/net.json
154
+void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map)
173
+++ b/qapi/net.json
155
+{
156
+ iova_tree_remove(iova_tree->iova_taddr_map, map);
157
+}
158
diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h
159
new file mode 100644
160
index XXXXXXX..XXXXXXX
161
--- /dev/null
162
+++ b/hw/virtio/vhost-iova-tree.h
174
@@ -XXX,XX +XXX,XX @@
163
@@ -XXX,XX +XXX,XX @@
175
# @queues: number of queues to be created for multiqueue vhost-vdpa
164
+/*
176
# (default: 1)
165
+ * vhost software live migration iova tree
177
#
166
+ *
178
+# @x-svq: Start device with (experimental) shadow virtqueue. (Since 7.1)
167
+ * SPDX-FileCopyrightText: Red Hat, Inc. 2021
179
+# (default: false)
168
+ * SPDX-FileContributor: Author: Eugenio Pérez <eperezma@redhat.com>
180
+#
169
+ *
181
+# Features:
170
+ * SPDX-License-Identifier: GPL-2.0-or-later
182
+# @unstable: Member @x-svq is experimental.
171
+ */
183
+#
172
+
184
# Since: 5.1
173
+#ifndef HW_VIRTIO_VHOST_IOVA_TREE_H
185
##
174
+#define HW_VIRTIO_VHOST_IOVA_TREE_H
186
{ 'struct': 'NetdevVhostVDPAOptions',
175
+
187
'data': {
176
+#include "qemu/iova-tree.h"
188
'*vhostdev': 'str',
177
+#include "exec/memory.h"
189
- '*queues': 'int' } }
178
+
190
+ '*queues': 'int',
179
+typedef struct VhostIOVATree VhostIOVATree;
191
+ '*x-svq': {'type': 'bool', 'features' : [ 'unstable'] } } }
180
+
192
181
+VhostIOVATree *vhost_iova_tree_new(uint64_t iova_first, uint64_t iova_last);
193
##
182
+void vhost_iova_tree_delete(VhostIOVATree *iova_tree);
194
# @NetdevVmnetHostOptions:
183
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostIOVATree, vhost_iova_tree_delete);
184
+
185
+const DMAMap *vhost_iova_tree_find_iova(const VhostIOVATree *iova_tree,
186
+ const DMAMap *map);
187
+int vhost_iova_tree_map_alloc(VhostIOVATree *iova_tree, DMAMap *map);
188
+void vhost_iova_tree_remove(VhostIOVATree *iova_tree, const DMAMap *map);
189
+
190
+#endif
195
--
191
--
196
2.7.4
192
2.7.4
197
193
198
194
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
This allows external handlers to be aware of new buffers that the guest
3
Use translations added in VhostIOVATree in SVQ.
4
places in the virtqueue.
4
5
5
Only introduce usage here, not allocation and deallocation. As with
6
When this callback is defined the ownership of the guest's virtqueue
6
previous patches, we use the dead code paths of shadow_vqs_enabled to
7
element is transferred to the callback. This means that if the user
7
avoid commiting too many changes at once. These are impossible to take
8
wants to forward the descriptor it needs to manually inject it. The
8
at the moment.
9
callback is also free to process the command by itself and use the
10
element with svq_push.
11
9
12
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
10
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
13
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
11
Acked-by: Michael S. Tsirkin <mst@redhat.com>
14
Signed-off-by: Jason Wang <jasowang@redhat.com>
12
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
---
13
---
16
hw/virtio/vhost-shadow-virtqueue.c | 14 ++++++++++++--
14
hw/virtio/vhost-shadow-virtqueue.c | 86 +++++++++++++++++++++++---
17
hw/virtio/vhost-shadow-virtqueue.h | 31 ++++++++++++++++++++++++++++++-
15
hw/virtio/vhost-shadow-virtqueue.h | 6 +-
18
hw/virtio/vhost-vdpa.c | 3 ++-
16
hw/virtio/vhost-vdpa.c | 122 +++++++++++++++++++++++++++++++------
19
3 files changed, 44 insertions(+), 4 deletions(-)
17
include/hw/virtio/vhost-vdpa.h | 3 +
18
4 files changed, 187 insertions(+), 30 deletions(-)
20
19
21
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
20
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
22
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
23
--- a/hw/virtio/vhost-shadow-virtqueue.c
22
--- a/hw/virtio/vhost-shadow-virtqueue.c
24
+++ b/hw/virtio/vhost-shadow-virtqueue.c
23
+++ b/hw/virtio/vhost-shadow-virtqueue.c
25
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq)
24
@@ -XXX,XX +XXX,XX @@ static uint16_t vhost_svq_available_slots(const VhostShadowVirtqueue *svq)
26
break;
25
return svq->vring.num - (svq->shadow_avail_idx - svq->shadow_used_idx);
27
}
26
}
28
27
29
- r = vhost_svq_add_element(svq, elem);
28
-static void vhost_vring_write_descs(VhostShadowVirtqueue *svq,
30
+ if (svq->ops) {
29
+/**
31
+ r = svq->ops->avail_handler(svq, elem, svq->ops_opaque);
30
+ * Translate addresses between the qemu's virtual address and the SVQ IOVA
32
+ } else {
31
+ *
33
+ r = vhost_svq_add_element(svq, elem);
32
+ * @svq: Shadow VirtQueue
34
+ }
33
+ * @vaddr: Translated IOVA addresses
35
if (unlikely(r != 0)) {
34
+ * @iovec: Source qemu's VA addresses
36
if (r == -ENOSPC) {
35
+ * @num: Length of iovec and minimum length of vaddr
37
/*
36
+ */
37
+static bool vhost_svq_translate_addr(const VhostShadowVirtqueue *svq,
38
+ hwaddr *addrs, const struct iovec *iovec,
39
+ size_t num)
40
+{
41
+ if (num == 0) {
42
+ return true;
43
+ }
44
+
45
+ for (size_t i = 0; i < num; ++i) {
46
+ DMAMap needle = {
47
+ .translated_addr = (hwaddr)(uintptr_t)iovec[i].iov_base,
48
+ .size = iovec[i].iov_len,
49
+ };
50
+ Int128 needle_last, map_last;
51
+ size_t off;
52
+
53
+ const DMAMap *map = vhost_iova_tree_find_iova(svq->iova_tree, &needle);
54
+ /*
55
+ * Map cannot be NULL since iova map contains all guest space and
56
+ * qemu already has a physical address mapped
57
+ */
58
+ if (unlikely(!map)) {
59
+ qemu_log_mask(LOG_GUEST_ERROR,
60
+ "Invalid address 0x%"HWADDR_PRIx" given by guest",
61
+ needle.translated_addr);
62
+ return false;
63
+ }
64
+
65
+ off = needle.translated_addr - map->translated_addr;
66
+ addrs[i] = map->iova + off;
67
+
68
+ needle_last = int128_add(int128_make64(needle.translated_addr),
69
+ int128_make64(iovec[i].iov_len));
70
+ map_last = int128_make64(map->translated_addr + map->size);
71
+ if (unlikely(int128_gt(needle_last, map_last))) {
72
+ qemu_log_mask(LOG_GUEST_ERROR,
73
+ "Guest buffer expands over iova range");
74
+ return false;
75
+ }
76
+ }
77
+
78
+ return true;
79
+}
80
+
81
+static void vhost_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
82
const struct iovec *iovec, size_t num,
83
bool more_descs, bool write)
84
{
85
@@ -XXX,XX +XXX,XX @@ static void vhost_vring_write_descs(VhostShadowVirtqueue *svq,
86
} else {
87
descs[i].flags = flags;
88
}
89
- descs[i].addr = cpu_to_le64((hwaddr)(intptr_t)iovec[n].iov_base);
90
+ descs[i].addr = cpu_to_le64(sg[n]);
91
descs[i].len = cpu_to_le32(iovec[n].iov_len);
92
93
last = i;
94
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
95
{
96
unsigned avail_idx;
97
vring_avail_t *avail = svq->vring.avail;
98
+ bool ok;
99
+ g_autofree hwaddr *sgs = g_new(hwaddr, MAX(elem->out_num, elem->in_num));
100
101
*head = svq->free_head;
102
103
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
104
return false;
105
}
106
107
- vhost_vring_write_descs(svq, elem->out_sg, elem->out_num, elem->in_num > 0,
108
- false);
109
- vhost_vring_write_descs(svq, elem->in_sg, elem->in_num, false, true);
110
+ ok = vhost_svq_translate_addr(svq, sgs, elem->out_sg, elem->out_num);
111
+ if (unlikely(!ok)) {
112
+ return false;
113
+ }
114
+ vhost_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num,
115
+ elem->in_num > 0, false);
116
+
117
+
118
+ ok = vhost_svq_translate_addr(svq, sgs, elem->in_sg, elem->in_num);
119
+ if (unlikely(!ok)) {
120
+ return false;
121
+ }
122
+
123
+ vhost_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false, true);
124
125
/*
126
* Put the entry in the available array (but don't update avail->idx until
127
@@ -XXX,XX +XXX,XX @@ void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd)
128
void vhost_svq_get_vring_addr(const VhostShadowVirtqueue *svq,
129
struct vhost_vring_addr *addr)
130
{
131
- addr->desc_user_addr = (uint64_t)(intptr_t)svq->vring.desc;
132
- addr->avail_user_addr = (uint64_t)(intptr_t)svq->vring.avail;
133
- addr->used_user_addr = (uint64_t)(intptr_t)svq->vring.used;
134
+ addr->desc_user_addr = (uint64_t)(uintptr_t)svq->vring.desc;
135
+ addr->avail_user_addr = (uint64_t)(uintptr_t)svq->vring.avail;
136
+ addr->used_user_addr = (uint64_t)(uintptr_t)svq->vring.used;
137
}
138
139
size_t vhost_svq_driver_area_size(const VhostShadowVirtqueue *svq)
38
@@ -XXX,XX +XXX,XX @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
140
@@ -XXX,XX +XXX,XX @@ void vhost_svq_stop(VhostShadowVirtqueue *svq)
141
* Creates vhost shadow virtqueue, and instructs the vhost device to use the
39
* shadow methods and file descriptors.
142
* shadow methods and file descriptors.
40
*
143
*
41
* @iova_tree: Tree to perform descriptors translations
144
+ * @iova_tree: Tree to perform descriptors translations
42
+ * @ops: SVQ owner callbacks
145
+ *
43
+ * @ops_opaque: ops opaque pointer
44
*
45
* Returns the new virtqueue or NULL.
146
* Returns the new virtqueue or NULL.
46
*
147
*
47
* In case of error, reason is reported through error_report.
148
* In case of error, reason is reported through error_report.
48
*/
149
*/
49
-VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree)
150
-VhostShadowVirtqueue *vhost_svq_new(void)
50
+VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree,
151
+VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree)
51
+ const VhostShadowVirtqueueOps *ops,
52
+ void *ops_opaque)
53
{
152
{
54
g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1);
153
g_autofree VhostShadowVirtqueue *svq = g_new0(VhostShadowVirtqueue, 1);
55
int r;
154
int r;
56
@@ -XXX,XX +XXX,XX @@ VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree)
155
@@ -XXX,XX +XXX,XX @@ VhostShadowVirtqueue *vhost_svq_new(void)
156
57
event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
157
event_notifier_init_fd(&svq->svq_kick, VHOST_FILE_UNBIND);
58
event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call);
158
event_notifier_set_handler(&svq->hdev_call, vhost_svq_handle_call);
59
svq->iova_tree = iova_tree;
159
+ svq->iova_tree = iova_tree;
60
+ svq->ops = ops;
61
+ svq->ops_opaque = ops_opaque;
62
return g_steal_pointer(&svq);
160
return g_steal_pointer(&svq);
63
161
64
err_init_hdev_call:
162
err_init_hdev_call:
65
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
163
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
66
index XXXXXXX..XXXXXXX 100644
164
index XXXXXXX..XXXXXXX 100644
67
--- a/hw/virtio/vhost-shadow-virtqueue.h
165
--- a/hw/virtio/vhost-shadow-virtqueue.h
68
+++ b/hw/virtio/vhost-shadow-virtqueue.h
166
+++ b/hw/virtio/vhost-shadow-virtqueue.h
69
@@ -XXX,XX +XXX,XX @@ typedef struct SVQDescState {
167
@@ -XXX,XX +XXX,XX @@
70
unsigned int ndescs;
168
#include "qemu/event_notifier.h"
71
} SVQDescState;
169
#include "hw/virtio/virtio.h"
72
170
#include "standard-headers/linux/vhost_types.h"
73
+typedef struct VhostShadowVirtqueue VhostShadowVirtqueue;
171
+#include "hw/virtio/vhost-iova-tree.h"
74
+
172
75
+/**
76
+ * Callback to handle an avail buffer.
77
+ *
78
+ * @svq: Shadow virtqueue
79
+ * @elem: Element placed in the queue by the guest
80
+ * @vq_callback_opaque: Opaque
81
+ *
82
+ * Returns 0 if the vq is running as expected.
83
+ *
84
+ * Note that ownership of elem is transferred to the callback.
85
+ */
86
+typedef int (*VirtQueueAvailCallback)(VhostShadowVirtqueue *svq,
87
+ VirtQueueElement *elem,
88
+ void *vq_callback_opaque);
89
+
90
+typedef struct VhostShadowVirtqueueOps {
91
+ VirtQueueAvailCallback avail_handler;
92
+} VhostShadowVirtqueueOps;
93
+
94
/* Shadow virtqueue to relay notifications */
173
/* Shadow virtqueue to relay notifications */
95
typedef struct VhostShadowVirtqueue {
174
typedef struct VhostShadowVirtqueue {
96
/* Shadow vring */
97
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
175
@@ -XXX,XX +XXX,XX @@ typedef struct VhostShadowVirtqueue {
98
*/
176
/* Virtio device */
99
uint16_t *desc_next;
177
VirtIODevice *vdev;
100
178
101
+ /* Caller callbacks */
179
+ /* IOVA mapping */
102
+ const VhostShadowVirtqueueOps *ops;
180
+ VhostIOVATree *iova_tree;
103
+
181
+
104
+ /* Caller callbacks opaque */
182
/* Map for use the guest's descriptors */
105
+ void *ops_opaque;
183
VirtQueueElement **ring_id_maps;
106
+
107
/* Next head to expose to the device */
108
uint16_t shadow_avail_idx;
109
184
110
@@ -XXX,XX +XXX,XX @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
185
@@ -XXX,XX +XXX,XX @@ void vhost_svq_start(VhostShadowVirtqueue *svq, VirtIODevice *vdev,
111
VirtQueue *vq);
186
VirtQueue *vq);
112
void vhost_svq_stop(VhostShadowVirtqueue *svq);
187
void vhost_svq_stop(VhostShadowVirtqueue *svq);
113
188
114
-VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree);
189
-VhostShadowVirtqueue *vhost_svq_new(void);
115
+VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree,
190
+VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree);
116
+ const VhostShadowVirtqueueOps *ops,
117
+ void *ops_opaque);
118
191
119
void vhost_svq_free(gpointer vq);
192
void vhost_svq_free(gpointer vq);
120
G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free);
193
G_DEFINE_AUTOPTR_CLEANUP_FUNC(VhostShadowVirtqueue, vhost_svq_free);
121
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
194
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
122
index XXXXXXX..XXXXXXX 100644
195
index XXXXXXX..XXXXXXX 100644
123
--- a/hw/virtio/vhost-vdpa.c
196
--- a/hw/virtio/vhost-vdpa.c
124
+++ b/hw/virtio/vhost-vdpa.c
197
+++ b/hw/virtio/vhost-vdpa.c
198
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
199
vaddr, section->readonly);
200
201
llsize = int128_sub(llend, int128_make64(iova));
202
+ if (v->shadow_vqs_enabled) {
203
+ DMAMap mem_region = {
204
+ .translated_addr = (hwaddr)(uintptr_t)vaddr,
205
+ .size = int128_get64(llsize) - 1,
206
+ .perm = IOMMU_ACCESS_FLAG(true, section->readonly),
207
+ };
208
+
209
+ int r = vhost_iova_tree_map_alloc(v->iova_tree, &mem_region);
210
+ if (unlikely(r != IOVA_OK)) {
211
+ error_report("Can't allocate a mapping (%d)", r);
212
+ goto fail;
213
+ }
214
+
215
+ iova = mem_region.iova;
216
+ }
217
218
vhost_vdpa_iotlb_batch_begin_once(v);
219
ret = vhost_vdpa_dma_map(v, iova, int128_get64(llsize),
220
@@ -XXX,XX +XXX,XX @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener,
221
222
llsize = int128_sub(llend, int128_make64(iova));
223
224
+ if (v->shadow_vqs_enabled) {
225
+ const DMAMap *result;
226
+ const void *vaddr = memory_region_get_ram_ptr(section->mr) +
227
+ section->offset_within_region +
228
+ (iova - section->offset_within_address_space);
229
+ DMAMap mem_region = {
230
+ .translated_addr = (hwaddr)(uintptr_t)vaddr,
231
+ .size = int128_get64(llsize) - 1,
232
+ };
233
+
234
+ result = vhost_iova_tree_find_iova(v->iova_tree, &mem_region);
235
+ iova = result->iova;
236
+ vhost_iova_tree_remove(v->iova_tree, &mem_region);
237
+ }
238
vhost_vdpa_iotlb_batch_begin_once(v);
239
ret = vhost_vdpa_dma_unmap(v, iova, int128_get64(llsize));
240
if (ret) {
125
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
241
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
126
242
127
shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free);
243
shadow_vqs = g_ptr_array_new_full(hdev->nvqs, vhost_svq_free);
128
for (unsigned n = 0; n < hdev->nvqs; ++n) {
244
for (unsigned n = 0; n < hdev->nvqs; ++n) {
129
- g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(v->iova_tree);
245
- g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new();
130
+ g_autoptr(VhostShadowVirtqueue) svq;
246
+ g_autoptr(VhostShadowVirtqueue) svq = vhost_svq_new(v->iova_tree);
131
247
132
+ svq = vhost_svq_new(v->iova_tree, NULL, NULL);
133
if (unlikely(!svq)) {
248
if (unlikely(!svq)) {
134
error_setg(errp, "Cannot create svq %u", n);
249
error_setg(errp, "Cannot create svq %u", n);
135
return -1;
250
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_svq_set_fds(struct vhost_dev *dev,
251
/**
252
* Unmap a SVQ area in the device
253
*/
254
-static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, hwaddr iova,
255
- hwaddr size)
256
+static bool vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v,
257
+ const DMAMap *needle)
258
{
259
+ const DMAMap *result = vhost_iova_tree_find_iova(v->iova_tree, needle);
260
+ hwaddr size;
261
int r;
262
263
- size = ROUND_UP(size, qemu_real_host_page_size);
264
- r = vhost_vdpa_dma_unmap(v, iova, size);
265
+ if (unlikely(!result)) {
266
+ error_report("Unable to find SVQ address to unmap");
267
+ return false;
268
+ }
269
+
270
+ size = ROUND_UP(result->size, qemu_real_host_page_size);
271
+ r = vhost_vdpa_dma_unmap(v, result->iova, size);
272
return r == 0;
273
}
274
275
static bool vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev,
276
const VhostShadowVirtqueue *svq)
277
{
278
+ DMAMap needle = {};
279
struct vhost_vdpa *v = dev->opaque;
280
struct vhost_vring_addr svq_addr;
281
- size_t device_size = vhost_svq_device_area_size(svq);
282
- size_t driver_size = vhost_svq_driver_area_size(svq);
283
bool ok;
284
285
vhost_svq_get_vring_addr(svq, &svq_addr);
286
287
- ok = vhost_vdpa_svq_unmap_ring(v, svq_addr.desc_user_addr, driver_size);
288
+ needle.translated_addr = svq_addr.desc_user_addr;
289
+ ok = vhost_vdpa_svq_unmap_ring(v, &needle);
290
if (unlikely(!ok)) {
291
return false;
292
}
293
294
- return vhost_vdpa_svq_unmap_ring(v, svq_addr.used_user_addr, device_size);
295
+ needle.translated_addr = svq_addr.used_user_addr;
296
+ return vhost_vdpa_svq_unmap_ring(v, &needle);
297
+}
298
+
299
+/**
300
+ * Map the SVQ area in the device
301
+ *
302
+ * @v: Vhost-vdpa device
303
+ * @needle: The area to search iova
304
+ * @errorp: Error pointer
305
+ */
306
+static bool vhost_vdpa_svq_map_ring(struct vhost_vdpa *v, DMAMap *needle,
307
+ Error **errp)
308
+{
309
+ int r;
310
+
311
+ r = vhost_iova_tree_map_alloc(v->iova_tree, needle);
312
+ if (unlikely(r != IOVA_OK)) {
313
+ error_setg(errp, "Cannot allocate iova (%d)", r);
314
+ return false;
315
+ }
316
+
317
+ r = vhost_vdpa_dma_map(v, needle->iova, needle->size + 1,
318
+ (void *)(uintptr_t)needle->translated_addr,
319
+ needle->perm == IOMMU_RO);
320
+ if (unlikely(r != 0)) {
321
+ error_setg_errno(errp, -r, "Cannot map region to device");
322
+ vhost_iova_tree_remove(v->iova_tree, needle);
323
+ }
324
+
325
+ return r == 0;
326
}
327
328
/**
329
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svq_map_rings(struct vhost_dev *dev,
330
struct vhost_vring_addr *addr,
331
Error **errp)
332
{
333
+ DMAMap device_region, driver_region;
334
+ struct vhost_vring_addr svq_addr;
335
struct vhost_vdpa *v = dev->opaque;
336
size_t device_size = vhost_svq_device_area_size(svq);
337
size_t driver_size = vhost_svq_driver_area_size(svq);
338
- int r;
339
+ size_t avail_offset;
340
+ bool ok;
341
342
ERRP_GUARD();
343
- vhost_svq_get_vring_addr(svq, addr);
344
+ vhost_svq_get_vring_addr(svq, &svq_addr);
345
346
- r = vhost_vdpa_dma_map(v, addr->desc_user_addr, driver_size,
347
- (void *)(uintptr_t)addr->desc_user_addr, true);
348
- if (unlikely(r != 0)) {
349
- error_setg_errno(errp, -r, "Cannot create vq driver region: ");
350
+ driver_region = (DMAMap) {
351
+ .translated_addr = svq_addr.desc_user_addr,
352
+ .size = driver_size - 1,
353
+ .perm = IOMMU_RO,
354
+ };
355
+ ok = vhost_vdpa_svq_map_ring(v, &driver_region, errp);
356
+ if (unlikely(!ok)) {
357
+ error_prepend(errp, "Cannot create vq driver region: ");
358
return false;
359
}
360
+ addr->desc_user_addr = driver_region.iova;
361
+ avail_offset = svq_addr.avail_user_addr - svq_addr.desc_user_addr;
362
+ addr->avail_user_addr = driver_region.iova + avail_offset;
363
364
- r = vhost_vdpa_dma_map(v, addr->used_user_addr, device_size,
365
- (void *)(intptr_t)addr->used_user_addr, false);
366
- if (unlikely(r != 0)) {
367
- error_setg_errno(errp, -r, "Cannot create vq device region: ");
368
+ device_region = (DMAMap) {
369
+ .translated_addr = svq_addr.used_user_addr,
370
+ .size = device_size - 1,
371
+ .perm = IOMMU_RW,
372
+ };
373
+ ok = vhost_vdpa_svq_map_ring(v, &device_region, errp);
374
+ if (unlikely(!ok)) {
375
+ error_prepend(errp, "Cannot create vq device region: ");
376
+ vhost_vdpa_svq_unmap_ring(v, &driver_region);
377
}
378
+ addr->used_user_addr = device_region.iova;
379
380
- return r == 0;
381
+ return ok;
382
}
383
384
static bool vhost_vdpa_svq_setup(struct vhost_dev *dev,
385
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
386
index XXXXXXX..XXXXXXX 100644
387
--- a/include/hw/virtio/vhost-vdpa.h
388
+++ b/include/hw/virtio/vhost-vdpa.h
389
@@ -XXX,XX +XXX,XX @@
390
391
#include <gmodule.h>
392
393
+#include "hw/virtio/vhost-iova-tree.h"
394
#include "hw/virtio/virtio.h"
395
#include "standard-headers/linux/vhost_types.h"
396
397
@@ -XXX,XX +XXX,XX @@ typedef struct vhost_vdpa {
398
MemoryListener listener;
399
struct vhost_vdpa_iova_range iova_range;
400
bool shadow_vqs_enabled;
401
+ /* IOVA mapping used by the Shadow Virtqueue */
402
+ VhostIOVATree *iova_tree;
403
GPtrArray *shadow_vqs;
404
struct vhost_dev *dev;
405
VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX];
136
--
406
--
137
2.7.4
407
2.7.4
138
408
139
409
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Shadow CVQ will copy buffers on qemu VA, so we avoid TOCTOU attacks from
3
This is needed to achieve migration, so the destination can restore its
4
the guest that could set a different state in qemu device model and vdpa
4
index.
5
device.
6
5
7
To do so, it needs to be able to map these new buffers to the device.
6
Setting base as last used idx, so destination will see as available all
7
the entries that the device did not use, including the in-flight
8
processing ones.
9
10
This is ok for networking, but other kinds of devices might have
11
problems with these retransmissions.
8
12
9
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
13
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
10
Acked-by: Jason Wang <jasowang@redhat.com>
14
Acked-by: Michael S. Tsirkin <mst@redhat.com>
11
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
12
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
Signed-off-by: Jason Wang <jasowang@redhat.com>
13
---
16
---
14
hw/virtio/vhost-vdpa.c | 7 +++----
17
hw/virtio/vhost-vdpa.c | 17 +++++++++++++++++
15
include/hw/virtio/vhost-vdpa.h | 4 ++++
18
1 file changed, 17 insertions(+)
16
2 files changed, 7 insertions(+), 4 deletions(-)
17
19
18
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
20
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
19
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
20
--- a/hw/virtio/vhost-vdpa.c
22
--- a/hw/virtio/vhost-vdpa.c
21
+++ b/hw/virtio/vhost-vdpa.c
23
+++ b/hw/virtio/vhost-vdpa.c
22
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section,
24
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_base(struct vhost_dev *dev,
23
return false;
25
static int vhost_vdpa_get_vring_base(struct vhost_dev *dev,
24
}
26
struct vhost_vring_state *ring)
25
26
-static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
27
- void *vaddr, bool readonly)
28
+int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
29
+ void *vaddr, bool readonly)
30
{
27
{
31
struct vhost_msg_v2 msg = {};
28
+ struct vhost_vdpa *v = dev->opaque;
32
int fd = v->device_fd;
29
int ret;
33
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
30
31
+ if (v->shadow_vqs_enabled) {
32
+ VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs,
33
+ ring->index);
34
+
35
+ /*
36
+ * Setting base as last used idx, so destination will see as available
37
+ * all the entries that the device did not use, including the in-flight
38
+ * processing ones.
39
+ *
40
+ * TODO: This is ok for networking, but other kinds of devices might
41
+ * have problems with these retransmissions.
42
+ */
43
+ ring->num = svq->last_used_idx;
44
+ return 0;
45
+ }
46
+
47
ret = vhost_vdpa_call(dev, VHOST_GET_VRING_BASE, ring);
48
trace_vhost_vdpa_get_vring_base(dev, ring->index, ring->num);
34
return ret;
49
return ret;
35
}
36
37
-static int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova,
38
- hwaddr size)
39
+int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, hwaddr size)
40
{
41
struct vhost_msg_v2 msg = {};
42
int fd = v->device_fd;
43
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
44
index XXXXXXX..XXXXXXX 100644
45
--- a/include/hw/virtio/vhost-vdpa.h
46
+++ b/include/hw/virtio/vhost-vdpa.h
47
@@ -XXX,XX +XXX,XX @@ typedef struct vhost_vdpa {
48
VhostVDPAHostNotifier notifier[VIRTIO_QUEUE_MAX];
49
} VhostVDPA;
50
51
+int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
52
+ void *vaddr, bool readonly);
53
+int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, hwaddr size);
54
+
55
#endif
56
--
50
--
57
2.7.4
51
2.7.4
58
52
59
53
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
In the next patch we will allow busypolling of this value. The compiler
3
Setting the log address would make the device start reporting invalid
4
have a running path where shadow_used_idx, last_used_idx, and vring used
4
dirty memory because the SVQ vrings are located in qemu's memory.
5
idx are not modified within the same thread busypolling.
6
7
This was not an issue before since we always cleared device event
8
notifier before checking it, and that could act as memory barrier.
9
However, the busypoll needs something similar to kernel READ_ONCE.
10
11
Let's add it here, sepparated from the polling.
12
5
13
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Acked-by: Michael S. Tsirkin <mst@redhat.com>
14
Signed-off-by: Jason Wang <jasowang@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
---
9
---
16
hw/virtio/vhost-shadow-virtqueue.c | 3 ++-
10
hw/virtio/vhost-vdpa.c | 3 ++-
17
1 file changed, 2 insertions(+), 1 deletion(-)
11
1 file changed, 2 insertions(+), 1 deletion(-)
18
12
19
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
13
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
20
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/virtio/vhost-shadow-virtqueue.c
15
--- a/hw/virtio/vhost-vdpa.c
22
+++ b/hw/virtio/vhost-shadow-virtqueue.c
16
+++ b/hw/virtio/vhost-vdpa.c
23
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick_notifier(EventNotifier *n)
17
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
24
18
static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base,
25
static bool vhost_svq_more_used(VhostShadowVirtqueue *svq)
19
struct vhost_log *log)
26
{
20
{
27
+ uint16_t *used_idx = &svq->vring.used->idx;
21
- if (vhost_vdpa_one_time_request(dev)) {
28
if (svq->last_used_idx != svq->shadow_used_idx) {
22
+ struct vhost_vdpa *v = dev->opaque;
29
return true;
23
+ if (v->shadow_vqs_enabled || vhost_vdpa_one_time_request(dev)) {
24
return 0;
30
}
25
}
31
26
32
- svq->shadow_used_idx = cpu_to_le16(svq->vring.used->idx);
33
+ svq->shadow_used_idx = cpu_to_le16(*(volatile uint16_t *)used_idx);
34
35
return svq->last_used_idx != svq->shadow_used_idx;
36
}
37
--
27
--
38
2.7.4
28
2.7.4
39
29
40
30
diff view generated by jsdifflib
Deleted patch
1
From: Eugenio Pérez <eperezma@redhat.com>
2
1
3
The series needs to expose vhost_svq_add with full functionality,
4
including kick
5
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
hw/virtio/vhost-shadow-virtqueue.c | 2 +-
11
1 file changed, 1 insertion(+), 1 deletion(-)
12
13
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/hw/virtio/vhost-shadow-virtqueue.c
16
+++ b/hw/virtio/vhost-shadow-virtqueue.c
17
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
18
}
19
20
svq->ring_id_maps[qemu_head] = elem;
21
+ vhost_svq_kick(svq);
22
return true;
23
}
24
25
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq)
26
/* VQ is broken, just return and ignore any other kicks */
27
return;
28
}
29
- vhost_svq_kick(svq);
30
}
31
32
virtio_queue_set_notification(svq->vq, true);
33
--
34
2.7.4
35
36
diff view generated by jsdifflib
Deleted patch
1
From: Eugenio Pérez <eperezma@redhat.com>
2
1
3
The series need to expose vhost_svq_add with full functionality,
4
including checking for full queue.
5
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
hw/virtio/vhost-shadow-virtqueue.c | 59 +++++++++++++++++++++-----------------
11
1 file changed, 33 insertions(+), 26 deletions(-)
12
13
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/hw/virtio/vhost-shadow-virtqueue.c
16
+++ b/hw/virtio/vhost-shadow-virtqueue.c
17
@@ -XXX,XX +XXX,XX @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq)
18
* Add an element to a SVQ.
19
*
20
* The caller must check that there is enough slots for the new element. It
21
- * takes ownership of the element: In case of failure, it is free and the SVQ
22
- * is considered broken.
23
+ * takes ownership of the element: In case of failure not ENOSPC, it is free.
24
+ *
25
+ * Return -EINVAL if element is invalid, -ENOSPC if dev queue is full
26
*/
27
-static bool vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
28
+static int vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
29
{
30
unsigned qemu_head;
31
- bool ok = vhost_svq_add_split(svq, elem, &qemu_head);
32
+ unsigned ndescs = elem->in_num + elem->out_num;
33
+ bool ok;
34
+
35
+ if (unlikely(ndescs > vhost_svq_available_slots(svq))) {
36
+ return -ENOSPC;
37
+ }
38
+
39
+ ok = vhost_svq_add_split(svq, elem, &qemu_head);
40
if (unlikely(!ok)) {
41
g_free(elem);
42
- return false;
43
+ return -EINVAL;
44
}
45
46
svq->ring_id_maps[qemu_head] = elem;
47
vhost_svq_kick(svq);
48
- return true;
49
+ return 0;
50
}
51
52
/**
53
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq)
54
55
while (true) {
56
VirtQueueElement *elem;
57
- bool ok;
58
+ int r;
59
60
if (svq->next_guest_avail_elem) {
61
elem = g_steal_pointer(&svq->next_guest_avail_elem);
62
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq)
63
break;
64
}
65
66
- if (elem->out_num + elem->in_num > vhost_svq_available_slots(svq)) {
67
- /*
68
- * This condition is possible since a contiguous buffer in GPA
69
- * does not imply a contiguous buffer in qemu's VA
70
- * scatter-gather segments. If that happens, the buffer exposed
71
- * to the device needs to be a chain of descriptors at this
72
- * moment.
73
- *
74
- * SVQ cannot hold more available buffers if we are here:
75
- * queue the current guest descriptor and ignore further kicks
76
- * until some elements are used.
77
- */
78
- svq->next_guest_avail_elem = elem;
79
- return;
80
- }
81
-
82
- ok = vhost_svq_add(svq, elem);
83
- if (unlikely(!ok)) {
84
- /* VQ is broken, just return and ignore any other kicks */
85
+ r = vhost_svq_add(svq, elem);
86
+ if (unlikely(r != 0)) {
87
+ if (r == -ENOSPC) {
88
+ /*
89
+ * This condition is possible since a contiguous buffer in
90
+ * GPA does not imply a contiguous buffer in qemu's VA
91
+ * scatter-gather segments. If that happens, the buffer
92
+ * exposed to the device needs to be a chain of descriptors
93
+ * at this moment.
94
+ *
95
+ * SVQ cannot hold more available buffers if we are here:
96
+ * queue the current guest descriptor and ignore kicks
97
+ * until some elements are used.
98
+ */
99
+ svq->next_guest_avail_elem = elem;
100
+ }
101
+
102
+ /* VQ is full or broken, just return and ignore kicks */
103
return;
104
}
105
}
106
--
107
2.7.4
108
109
diff view generated by jsdifflib
Deleted patch
1
From: Eugenio Pérez <eperezma@redhat.com>
2
1
3
VirtQueueElement comes from the guest, but we're heading SVQ to be able
4
to modify the element presented to the device without the guest's
5
knowledge.
6
7
To do so, make SVQ accept sg buffers directly, instead of using
8
VirtQueueElement.
9
10
Add vhost_svq_add_element to maintain element convenience.
11
12
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
13
Acked-by: Jason Wang <jasowang@redhat.com>
14
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
15
Signed-off-by: Jason Wang <jasowang@redhat.com>
16
---
17
hw/virtio/vhost-shadow-virtqueue.c | 33 ++++++++++++++++++++++-----------
18
1 file changed, 22 insertions(+), 11 deletions(-)
19
20
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
21
index XXXXXXX..XXXXXXX 100644
22
--- a/hw/virtio/vhost-shadow-virtqueue.c
23
+++ b/hw/virtio/vhost-shadow-virtqueue.c
24
@@ -XXX,XX +XXX,XX @@ static bool vhost_svq_vring_write_descs(VhostShadowVirtqueue *svq, hwaddr *sg,
25
}
26
27
static bool vhost_svq_add_split(VhostShadowVirtqueue *svq,
28
- VirtQueueElement *elem, unsigned *head)
29
+ const struct iovec *out_sg, size_t out_num,
30
+ const struct iovec *in_sg, size_t in_num,
31
+ unsigned *head)
32
{
33
unsigned avail_idx;
34
vring_avail_t *avail = svq->vring.avail;
35
bool ok;
36
- g_autofree hwaddr *sgs = g_new(hwaddr, MAX(elem->out_num, elem->in_num));
37
+ g_autofree hwaddr *sgs = g_new(hwaddr, MAX(out_num, in_num));
38
39
*head = svq->free_head;
40
41
/* We need some descriptors here */
42
- if (unlikely(!elem->out_num && !elem->in_num)) {
43
+ if (unlikely(!out_num && !in_num)) {
44
qemu_log_mask(LOG_GUEST_ERROR,
45
"Guest provided element with no descriptors");
46
return false;
47
}
48
49
- ok = vhost_svq_vring_write_descs(svq, sgs, elem->out_sg, elem->out_num,
50
- elem->in_num > 0, false);
51
+ ok = vhost_svq_vring_write_descs(svq, sgs, out_sg, out_num, in_num > 0,
52
+ false);
53
if (unlikely(!ok)) {
54
return false;
55
}
56
57
- ok = vhost_svq_vring_write_descs(svq, sgs, elem->in_sg, elem->in_num, false,
58
- true);
59
+ ok = vhost_svq_vring_write_descs(svq, sgs, in_sg, in_num, false, true);
60
if (unlikely(!ok)) {
61
return false;
62
}
63
@@ -XXX,XX +XXX,XX @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq)
64
*
65
* Return -EINVAL if element is invalid, -ENOSPC if dev queue is full
66
*/
67
-static int vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
68
+static int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
69
+ size_t out_num, const struct iovec *in_sg,
70
+ size_t in_num, VirtQueueElement *elem)
71
{
72
unsigned qemu_head;
73
- unsigned ndescs = elem->in_num + elem->out_num;
74
+ unsigned ndescs = in_num + out_num;
75
bool ok;
76
77
if (unlikely(ndescs > vhost_svq_available_slots(svq))) {
78
return -ENOSPC;
79
}
80
81
- ok = vhost_svq_add_split(svq, elem, &qemu_head);
82
+ ok = vhost_svq_add_split(svq, out_sg, out_num, in_sg, in_num, &qemu_head);
83
if (unlikely(!ok)) {
84
g_free(elem);
85
return -EINVAL;
86
@@ -XXX,XX +XXX,XX @@ static int vhost_svq_add(VhostShadowVirtqueue *svq, VirtQueueElement *elem)
87
return 0;
88
}
89
90
+/* Convenience wrapper to add a guest's element to SVQ */
91
+static int vhost_svq_add_element(VhostShadowVirtqueue *svq,
92
+ VirtQueueElement *elem)
93
+{
94
+ return vhost_svq_add(svq, elem->out_sg, elem->out_num, elem->in_sg,
95
+ elem->in_num, elem);
96
+}
97
+
98
/**
99
* Forward available buffers.
100
*
101
@@ -XXX,XX +XXX,XX @@ static void vhost_handle_guest_kick(VhostShadowVirtqueue *svq)
102
break;
103
}
104
105
- r = vhost_svq_add(svq, elem);
106
+ r = vhost_svq_add_element(svq, elem);
107
if (unlikely(r != 0)) {
108
if (r == -ENOSPC) {
109
/*
110
--
111
2.7.4
112
113
diff view generated by jsdifflib
Deleted patch
1
From: Eugenio Pérez <eperezma@redhat.com>
2
1
3
A guest's buffer continuos on GPA may need multiple descriptors on
4
qemu's VA, so SVQ should track its length sepparatedly.
5
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
hw/virtio/vhost-shadow-virtqueue.c | 4 ++--
11
hw/virtio/vhost-shadow-virtqueue.h | 6 ++++++
12
2 files changed, 8 insertions(+), 2 deletions(-)
13
14
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/virtio/vhost-shadow-virtqueue.c
17
+++ b/hw/virtio/vhost-shadow-virtqueue.c
18
@@ -XXX,XX +XXX,XX @@ static int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
19
}
20
21
svq->desc_state[qemu_head].elem = elem;
22
+ svq->desc_state[qemu_head].ndescs = ndescs;
23
vhost_svq_kick(svq);
24
return 0;
25
}
26
@@ -XXX,XX +XXX,XX @@ static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq,
27
return NULL;
28
}
29
30
- num = svq->desc_state[used_elem.id].elem->in_num +
31
- svq->desc_state[used_elem.id].elem->out_num;
32
+ num = svq->desc_state[used_elem.id].ndescs;
33
last_used_chain = vhost_svq_last_desc_of_chain(svq, num, used_elem.id);
34
svq->desc_next[last_used_chain] = svq->free_head;
35
svq->free_head = used_elem.id;
36
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
37
index XXXXXXX..XXXXXXX 100644
38
--- a/hw/virtio/vhost-shadow-virtqueue.h
39
+++ b/hw/virtio/vhost-shadow-virtqueue.h
40
@@ -XXX,XX +XXX,XX @@
41
42
typedef struct SVQDescState {
43
VirtQueueElement *elem;
44
+
45
+ /*
46
+ * Number of descriptors exposed to the device. May or may not match
47
+ * guest's
48
+ */
49
+ unsigned int ndescs;
50
} SVQDescState;
51
52
/* Shadow virtqueue to relay notifications */
53
--
54
2.7.4
55
56
diff view generated by jsdifflib
Deleted patch
1
From: Eugenio Pérez <eperezma@redhat.com>
2
1
3
This allows external parts of SVQ to forward custom buffers to the
4
device.
5
6
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
7
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
8
Signed-off-by: Jason Wang <jasowang@redhat.com>
9
---
10
hw/virtio/vhost-shadow-virtqueue.c | 6 +++---
11
hw/virtio/vhost-shadow-virtqueue.h | 3 +++
12
2 files changed, 6 insertions(+), 3 deletions(-)
13
14
diff --git a/hw/virtio/vhost-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/hw/virtio/vhost-shadow-virtqueue.c
17
+++ b/hw/virtio/vhost-shadow-virtqueue.c
18
@@ -XXX,XX +XXX,XX @@ static void vhost_svq_kick(VhostShadowVirtqueue *svq)
19
*
20
* Return -EINVAL if element is invalid, -ENOSPC if dev queue is full
21
*/
22
-static int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
23
- size_t out_num, const struct iovec *in_sg,
24
- size_t in_num, VirtQueueElement *elem)
25
+int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
26
+ size_t out_num, const struct iovec *in_sg, size_t in_num,
27
+ VirtQueueElement *elem)
28
{
29
unsigned qemu_head;
30
unsigned ndescs = in_num + out_num;
31
diff --git a/hw/virtio/vhost-shadow-virtqueue.h b/hw/virtio/vhost-shadow-virtqueue.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/hw/virtio/vhost-shadow-virtqueue.h
34
+++ b/hw/virtio/vhost-shadow-virtqueue.h
35
@@ -XXX,XX +XXX,XX @@ bool vhost_svq_valid_features(uint64_t features, Error **errp);
36
37
void vhost_svq_push_elem(VhostShadowVirtqueue *svq,
38
const VirtQueueElement *elem, uint32_t len);
39
+int vhost_svq_add(VhostShadowVirtqueue *svq, const struct iovec *out_sg,
40
+ size_t out_num, const struct iovec *in_sg, size_t in_num,
41
+ VirtQueueElement *elem);
42
43
void vhost_svq_set_svq_kick_fd(VhostShadowVirtqueue *svq, int svq_kick_fd);
44
void vhost_svq_set_svq_call_fd(VhostShadowVirtqueue *svq, int call_fd);
45
--
46
2.7.4
47
48
diff view generated by jsdifflib
1
From: Eugenio Pérez <eperezma@redhat.com>
1
From: Eugenio Pérez <eperezma@redhat.com>
2
2
3
Since the vhost-vdpa device is exposing _F_LOG, adding a migration blocker if
3
SVQ is able to log the dirty bits by itself, so let's use it to not
4
it uses CVQ.
4
block migration.
5
5
6
However, qemu is able to migrate simple devices with no CVQ as long as
6
Also, ignore set and clear of VHOST_F_LOG_ALL on set_features if SVQ is
7
they use SVQ. To allow it, add a placeholder error to vhost_vdpa, and
7
enabled. Even if the device supports it, the reports would be nonsense
8
only add to vhost_dev when used. vhost_dev machinery place the migration
8
because SVQ memory is in the qemu region.
9
blocker if needed.
9
10
The log region is still allocated. Future changes might skip that, but
11
this series is already long enough.
10
12
11
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
13
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
12
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
14
Acked-by: Michael S. Tsirkin <mst@redhat.com>
13
Signed-off-by: Jason Wang <jasowang@redhat.com>
15
Signed-off-by: Jason Wang <jasowang@redhat.com>
14
---
16
---
15
hw/virtio/vhost-vdpa.c | 15 +++++++++++++++
17
hw/virtio/vhost-vdpa.c | 39 +++++++++++++++++++++++++++++++++++----
16
include/hw/virtio/vhost-vdpa.h | 1 +
18
include/hw/virtio/vhost-vdpa.h | 1 +
17
2 files changed, 16 insertions(+)
19
2 files changed, 36 insertions(+), 4 deletions(-)
18
20
19
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
21
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
20
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
21
--- a/hw/virtio/vhost-vdpa.c
23
--- a/hw/virtio/vhost-vdpa.c
22
+++ b/hw/virtio/vhost-vdpa.c
24
+++ b/hw/virtio/vhost-vdpa.c
23
@@ -XXX,XX +XXX,XX @@
25
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_one_time_request(struct vhost_dev *dev)
24
#include "hw/virtio/vhost-shadow-virtqueue.h"
26
return v->index != 0;
25
#include "hw/virtio/vhost-vdpa.h"
27
}
26
#include "exec/address-spaces.h"
28
27
+#include "migration/blocker.h"
29
+static int vhost_vdpa_get_dev_features(struct vhost_dev *dev,
28
#include "qemu/cutils.h"
30
+ uint64_t *features)
29
#include "qemu/main-loop.h"
31
+{
30
#include "cpu.h"
32
+ int ret;
31
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svqs_start(struct vhost_dev *dev)
33
+
32
return true;
34
+ ret = vhost_vdpa_call(dev, VHOST_GET_FEATURES, features);
35
+ trace_vhost_vdpa_get_features(dev, *features);
36
+ return ret;
37
+}
38
+
39
static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
40
Error **errp)
41
{
42
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v,
43
return 0;
33
}
44
}
34
45
35
+ if (v->migration_blocker) {
46
- r = hdev->vhost_ops->vhost_get_features(hdev, &dev_features);
36
+ int r = migrate_add_blocker(v->migration_blocker, &err);
47
+ r = vhost_vdpa_get_dev_features(hdev, &dev_features);
37
+ if (unlikely(r < 0)) {
48
if (r != 0) {
38
+ return false;
49
error_setg_errno(errp, -r, "Can't get vdpa device features");
50
return r;
51
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_mem_table(struct vhost_dev *dev,
52
static int vhost_vdpa_set_features(struct vhost_dev *dev,
53
uint64_t features)
54
{
55
+ struct vhost_vdpa *v = dev->opaque;
56
int ret;
57
58
if (vhost_vdpa_one_time_request(dev)) {
59
return 0;
60
}
61
62
+ if (v->shadow_vqs_enabled) {
63
+ if ((v->acked_features ^ features) == BIT_ULL(VHOST_F_LOG_ALL)) {
64
+ /*
65
+ * QEMU is just trying to enable or disable logging. SVQ handles
66
+ * this sepparately, so no need to forward this.
67
+ */
68
+ v->acked_features = features;
69
+ return 0;
39
+ }
70
+ }
71
+
72
+ v->acked_features = features;
73
+
74
+ /* We must not ack _F_LOG if SVQ is enabled */
75
+ features &= ~BIT_ULL(VHOST_F_LOG_ALL);
40
+ }
76
+ }
41
+
77
+
42
for (i = 0; i < v->shadow_vqs->len; ++i) {
78
trace_vhost_vdpa_set_features(dev, features);
43
VirtQueue *vq = virtio_get_queue(dev->vdev, dev->vq_index + i);
79
ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features);
44
VhostShadowVirtqueue *svq = g_ptr_array_index(v->shadow_vqs, i);
80
if (ret) {
45
@@ -XXX,XX +XXX,XX @@ err:
81
@@ -XXX,XX +XXX,XX @@ static int vhost_vdpa_set_vring_call(struct vhost_dev *dev,
46
vhost_svq_stop(svq);
82
static int vhost_vdpa_get_features(struct vhost_dev *dev,
47
}
83
uint64_t *features)
48
84
{
49
+ if (v->migration_blocker) {
85
- int ret;
50
+ migrate_del_blocker(v->migration_blocker);
86
+ struct vhost_vdpa *v = dev->opaque;
87
+ int ret = vhost_vdpa_get_dev_features(dev, features);
88
+
89
+ if (ret == 0 && v->shadow_vqs_enabled) {
90
+ /* Add SVQ logging capabilities */
91
+ *features |= BIT_ULL(VHOST_F_LOG_ALL);
51
+ }
92
+ }
52
+
93
53
return false;
94
- ret = vhost_vdpa_call(dev, VHOST_GET_FEATURES, features);
95
- trace_vhost_vdpa_get_features(dev, *features);
96
return ret;
54
}
97
}
55
56
@@ -XXX,XX +XXX,XX @@ static bool vhost_vdpa_svqs_stop(struct vhost_dev *dev)
57
}
58
}
59
60
+ if (v->migration_blocker) {
61
+ migrate_del_blocker(v->migration_blocker);
62
+ }
63
return true;
64
}
65
98
66
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
99
diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h
67
index XXXXXXX..XXXXXXX 100644
100
index XXXXXXX..XXXXXXX 100644
68
--- a/include/hw/virtio/vhost-vdpa.h
101
--- a/include/hw/virtio/vhost-vdpa.h
69
+++ b/include/hw/virtio/vhost-vdpa.h
102
+++ b/include/hw/virtio/vhost-vdpa.h
70
@@ -XXX,XX +XXX,XX @@ typedef struct vhost_vdpa {
103
@@ -XXX,XX +XXX,XX @@ typedef struct vhost_vdpa {
104
bool iotlb_batch_begin_sent;
105
MemoryListener listener;
106
struct vhost_vdpa_iova_range iova_range;
107
+ uint64_t acked_features;
71
bool shadow_vqs_enabled;
108
bool shadow_vqs_enabled;
72
/* IOVA mapping used by the Shadow Virtqueue */
109
/* IOVA mapping used by the Shadow Virtqueue */
73
VhostIOVATree *iova_tree;
110
VhostIOVATree *iova_tree;
74
+ Error *migration_blocker;
75
GPtrArray *shadow_vqs;
76
const VhostShadowVirtqueueOps *shadow_vq_ops;
77
void *shadow_vq_ops_opaque;
78
--
111
--
79
2.7.4
112
2.7.4
80
113
81
114
diff view generated by jsdifflib
Deleted patch
1
From: Zhang Chen <chen.zhang@intel.com>
2
1
3
If the checkpoint occurs when the guest finishes restarting
4
but has not started running, the runstate_set() may reject
5
the transition from COLO to PRELAUNCH with the crash log:
6
7
{"timestamp": {"seconds": 1593484591, "microseconds": 26605},\
8
"event": "RESET", "data": {"guest": true, "reason": "guest-reset"}}
9
qemu-system-x86_64: invalid runstate transition: 'colo' -> 'prelaunch'
10
11
Long-term testing says that it's pretty safe.
12
13
Signed-off-by: Like Xu <like.xu@linux.intel.com>
14
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
15
Acked-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
16
Signed-off-by: Jason Wang <jasowang@redhat.com>
17
---
18
softmmu/runstate.c | 1 +
19
1 file changed, 1 insertion(+)
20
21
diff --git a/softmmu/runstate.c b/softmmu/runstate.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/softmmu/runstate.c
24
+++ b/softmmu/runstate.c
25
@@ -XXX,XX +XXX,XX @@ static const RunStateTransition runstate_transitions_def[] = {
26
{ RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH },
27
28
{ RUN_STATE_COLO, RUN_STATE_RUNNING },
29
+ { RUN_STATE_COLO, RUN_STATE_PRELAUNCH },
30
{ RUN_STATE_COLO, RUN_STATE_SHUTDOWN},
31
32
{ RUN_STATE_RUNNING, RUN_STATE_DEBUG },
33
--
34
2.7.4
diff view generated by jsdifflib
Deleted patch
1
From: Zhang Chen <chen.zhang@intel.com>
2
1
3
We notice the QEMU may crash when the guest has too many
4
incoming network connections with the following log:
5
6
15197@1593578622.668573:colo_proxy_main : colo proxy connection hashtable full, clear it
7
free(): invalid pointer
8
[1] 15195 abort (core dumped) qemu-system-x86_64 ....
9
10
This is because we create the s->connection_track_table with
11
g_hash_table_new_full() which is defined as:
12
13
GHashTable * g_hash_table_new_full (GHashFunc hash_func,
14
GEqualFunc key_equal_func,
15
GDestroyNotify key_destroy_func,
16
GDestroyNotify value_destroy_func);
17
18
The fourth parameter connection_destroy() will be called to free the
19
memory allocated for all 'Connection' values in the hashtable when
20
we call g_hash_table_remove_all() in the connection_hashtable_reset().
21
22
But both connection_track_table and conn_list reference to the same
23
conn instance. It will trigger double free in conn_list clear. So this
24
patch remove free action on hash table side to avoid double free the
25
conn.
26
27
Signed-off-by: Like Xu <like.xu@linux.intel.com>
28
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
29
Signed-off-by: Jason Wang <jasowang@redhat.com>
30
---
31
net/colo-compare.c | 2 +-
32
net/filter-rewriter.c | 2 +-
33
2 files changed, 2 insertions(+), 2 deletions(-)
34
35
diff --git a/net/colo-compare.c b/net/colo-compare.c
36
index XXXXXXX..XXXXXXX 100644
37
--- a/net/colo-compare.c
38
+++ b/net/colo-compare.c
39
@@ -XXX,XX +XXX,XX @@ static void colo_compare_complete(UserCreatable *uc, Error **errp)
40
s->connection_track_table = g_hash_table_new_full(connection_key_hash,
41
connection_key_equal,
42
g_free,
43
- connection_destroy);
44
+ NULL);
45
46
colo_compare_iothread(s);
47
48
diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/net/filter-rewriter.c
51
+++ b/net/filter-rewriter.c
52
@@ -XXX,XX +XXX,XX @@ static void colo_rewriter_setup(NetFilterState *nf, Error **errp)
53
s->connection_track_table = g_hash_table_new_full(connection_key_hash,
54
connection_key_equal,
55
g_free,
56
- connection_destroy);
57
+ NULL);
58
s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
59
}
60
61
--
62
2.7.4
diff view generated by jsdifflib
Deleted patch
1
From: Zhang Chen <chen.zhang@intel.com>
2
1
3
Filter-rewriter no need to track connection in conn_list.
4
This patch fix the glib g_queue_is_empty assertion when COLO guest
5
keep a lot of network connection.
6
7
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
8
Reviewed-by: Li Zhijian <lizhijian@fujitsu.com>
9
Signed-off-by: Jason Wang <jasowang@redhat.com>
10
---
11
net/colo.c | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
14
diff --git a/net/colo.c b/net/colo.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/net/colo.c
17
+++ b/net/colo.c
18
@@ -XXX,XX +XXX,XX @@ Connection *connection_get(GHashTable *connection_track_table,
19
/*
20
* clear the conn_list
21
*/
22
- while (!g_queue_is_empty(conn_list)) {
23
+ while (conn_list && !g_queue_is_empty(conn_list)) {
24
connection_destroy(g_queue_pop_head(conn_list));
25
}
26
}
27
--
28
2.7.4
diff view generated by jsdifflib
Deleted patch
1
From: Zhang Chen <chen.zhang@intel.com>
2
1
3
When COLO use only one vnet_hdr_support parameter between
4
filter-redirector and filter-mirror(or colo-compare), COLO will crash
5
with segmentation fault. Back track as follow:
6
7
Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
8
0x0000555555cb200b in eth_get_l2_hdr_length (p=0x0)
9
at /home/tao/project/COLO/colo-qemu/include/net/eth.h:296
10
296 uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto);
11
(gdb) bt
12
0 0x0000555555cb200b in eth_get_l2_hdr_length (p=0x0)
13
at /home/tao/project/COLO/colo-qemu/include/net/eth.h:296
14
1 0x0000555555cb22b4 in parse_packet_early (pkt=0x555556a44840) at
15
net/colo.c:49
16
2 0x0000555555cb2b91 in is_tcp_packet (pkt=0x555556a44840) at
17
net/filter-rewriter.c:63
18
19
So wrong vnet_hdr_len will cause pkt->data become NULL. Add check to
20
raise error and add trace-events to track vnet_hdr_len.
21
22
Signed-off-by: Tao Xu <tao3.xu@intel.com>
23
Signed-off-by: Zhang Chen <chen.zhang@intel.com>
24
Reviewed-by: Li Zhijian <lizhijian@fujitsu.com>
25
Signed-off-by: Jason Wang <jasowang@redhat.com>
26
---
27
net/colo.c | 9 ++++++++-
28
net/trace-events | 1 +
29
2 files changed, 9 insertions(+), 1 deletion(-)
30
31
diff --git a/net/colo.c b/net/colo.c
32
index XXXXXXX..XXXXXXX 100644
33
--- a/net/colo.c
34
+++ b/net/colo.c
35
@@ -XXX,XX +XXX,XX @@ int parse_packet_early(Packet *pkt)
36
static const uint8_t vlan[] = {0x81, 0x00};
37
uint8_t *data = pkt->data + pkt->vnet_hdr_len;
38
uint16_t l3_proto;
39
- ssize_t l2hdr_len = eth_get_l2_hdr_length(data);
40
+ ssize_t l2hdr_len;
41
+
42
+ if (data == NULL) {
43
+ trace_colo_proxy_main_vnet_info("This packet is not parsed correctly, "
44
+ "pkt->vnet_hdr_len", pkt->vnet_hdr_len);
45
+ return 1;
46
+ }
47
+ l2hdr_len = eth_get_l2_hdr_length(data);
48
49
if (pkt->size < ETH_HLEN + pkt->vnet_hdr_len) {
50
trace_colo_proxy_main("pkt->size < ETH_HLEN");
51
diff --git a/net/trace-events b/net/trace-events
52
index XXXXXXX..XXXXXXX 100644
53
--- a/net/trace-events
54
+++ b/net/trace-events
55
@@ -XXX,XX +XXX,XX @@ vhost_user_event(const char *chr, int event) "chr: %s got event: %d"
56
57
# colo.c
58
colo_proxy_main(const char *chr) ": %s"
59
+colo_proxy_main_vnet_info(const char *sta, int size) ": %s = %d"
60
61
# colo-compare.c
62
colo_compare_main(const char *chr) ": %s"
63
--
64
2.7.4
diff view generated by jsdifflib