aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/net/vhost_net-stub.c4
-rw-r--r--hw/net/vhost_net.c55
-rw-r--r--hw/net/virtio-net.c201
-rw-r--r--hw/pci/pci_bridge.c10
-rw-r--r--hw/virtio/Kconfig5
-rw-r--r--hw/virtio/meson.build2
-rw-r--r--hw/virtio/trace-events1
-rw-r--r--hw/virtio/vhost-user-rng-pci.c79
-rw-r--r--hw/virtio/vhost-user-rng.c289
-rw-r--r--hw/virtio/vhost-user.c5
-rw-r--r--hw/virtio/vhost-vdpa.c140
-rw-r--r--hw/virtio/virtio-iommu-pci.c4
12 files changed, 658 insertions, 137 deletions
diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c
index a7f4252630..89d71cfb8e 100644
--- a/hw/net/vhost_net-stub.c
+++ b/hw/net/vhost_net-stub.c
@@ -33,13 +33,13 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
int vhost_net_start(VirtIODevice *dev,
NetClientState *ncs,
- int total_queues)
+ int data_queue_pairs, int cvq)
{
return -ENOSYS;
}
void vhost_net_stop(VirtIODevice *dev,
NetClientState *ncs,
- int total_queues)
+ int data_queue_pairs, int cvq)
{
}
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 386ec2eaa2..0d888f29a6 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -231,9 +231,11 @@ fail:
return NULL;
}
-static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index)
+static void vhost_net_set_vq_index(struct vhost_net *net, int vq_index,
+ int last_index)
{
net->dev.vq_index = vq_index;
+ net->dev.last_index = last_index;
}
static int vhost_net_start_one(struct vhost_net *net,
@@ -315,25 +317,37 @@ static void vhost_net_stop_one(struct vhost_net *net,
}
int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
- int total_queues)
+ int data_queue_pairs, int cvq)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
+ int total_notifiers = data_queue_pairs * 2 + cvq;
+ VirtIONet *n = VIRTIO_NET(dev);
+ int nvhosts = data_queue_pairs + cvq;
struct vhost_net *net;
- int r, e, i;
+ int r, e, i, last_index = data_queue_pairs * 2;
NetClientState *peer;
+ if (!cvq) {
+ last_index -= 1;
+ }
+
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
return -ENOSYS;
}
- for (i = 0; i < total_queues; i++) {
+ for (i = 0; i < nvhosts; i++) {
+
+ if (i < data_queue_pairs) {
+ peer = qemu_get_peer(ncs, i);
+ } else { /* Control Virtqueue */
+ peer = qemu_get_peer(ncs, n->max_queue_pairs);
+ }
- peer = qemu_get_peer(ncs, i);
net = get_vhost_net(peer);
- vhost_net_set_vq_index(net, i * 2);
+ vhost_net_set_vq_index(net, i * 2, last_index);
/* Suppress the masking guest notifiers on vhost user
* because vhost user doesn't interrupt masking/unmasking
@@ -344,14 +358,18 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
}
}
- r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true);
+ r = k->set_guest_notifiers(qbus->parent, total_notifiers, true);
if (r < 0) {
error_report("Error binding guest notifier: %d", -r);
goto err;
}
- for (i = 0; i < total_queues; i++) {
- peer = qemu_get_peer(ncs, i);
+ for (i = 0; i < nvhosts; i++) {
+ if (i < data_queue_pairs) {
+ peer = qemu_get_peer(ncs, i);
+ } else {
+ peer = qemu_get_peer(ncs, n->max_queue_pairs);
+ }
r = vhost_net_start_one(get_vhost_net(peer), dev);
if (r < 0) {
@@ -375,7 +393,7 @@ err_start:
peer = qemu_get_peer(ncs , i);
vhost_net_stop_one(get_vhost_net(peer), dev);
}
- e = k->set_guest_notifiers(qbus->parent, total_queues * 2, false);
+ e = k->set_guest_notifiers(qbus->parent, total_notifiers, false);
if (e < 0) {
fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e);
fflush(stderr);
@@ -385,18 +403,27 @@ err:
}
void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
- int total_queues)
+ int data_queue_pairs, int cvq)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
+ VirtIONet *n = VIRTIO_NET(dev);
+ NetClientState *peer;
+ int total_notifiers = data_queue_pairs * 2 + cvq;
+ int nvhosts = data_queue_pairs + cvq;
int i, r;
- for (i = 0; i < total_queues; i++) {
- vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev);
+ for (i = 0; i < nvhosts; i++) {
+ if (i < data_queue_pairs) {
+ peer = qemu_get_peer(ncs, i);
+ } else {
+ peer = qemu_get_peer(ncs, n->max_queue_pairs);
+ }
+ vhost_net_stop_one(get_vhost_net(peer), dev);
}
- r = k->set_guest_notifiers(qbus->parent, total_queues * 2, false);
+ r = k->set_guest_notifiers(qbus->parent, total_notifiers, false);
if (r < 0) {
fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
fflush(stderr);
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 09e173a558..f2014d5ea0 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -54,7 +54,7 @@
#define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256
#define VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE 256
-/* for now, only allow larger queues; with virtio-1, guest can downsize */
+/* for now, only allow larger queue_pairs; with virtio-1, guest can downsize */
#define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE
#define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE
@@ -131,7 +131,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
int ret = 0;
memset(&netcfg, 0 , sizeof(struct virtio_net_config));
virtio_stw_p(vdev, &netcfg.status, n->status);
- virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
+ virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queue_pairs);
virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
memcpy(netcfg.mac, n->mac, ETH_ALEN);
virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
@@ -243,7 +243,8 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
{
VirtIODevice *vdev = VIRTIO_DEVICE(n);
NetClientState *nc = qemu_get_queue(n->nic);
- int queues = n->multiqueue ? n->max_queues : 1;
+ int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1;
+ int cvq = n->max_ncs - n->max_queue_pairs;
if (!get_vhost_net(nc->peer)) {
return;
@@ -266,7 +267,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
/* Any packets outstanding? Purge them to avoid touching rings
* when vhost is running.
*/
- for (i = 0; i < queues; i++) {
+ for (i = 0; i < queue_pairs; i++) {
NetClientState *qnc = qemu_get_subqueue(n->nic, i);
/* Purge both directions: TX and RX. */
@@ -285,14 +286,14 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
}
n->vhost_started = 1;
- r = vhost_net_start(vdev, n->nic->ncs, queues);
+ r = vhost_net_start(vdev, n->nic->ncs, queue_pairs, cvq);
if (r < 0) {
error_report("unable to start vhost net: %d: "
"falling back on userspace virtio", -r);
n->vhost_started = 0;
}
} else {
- vhost_net_stop(vdev, n->nic->ncs, queues);
+ vhost_net_stop(vdev, n->nic->ncs, queue_pairs, cvq);
n->vhost_started = 0;
}
}
@@ -309,11 +310,11 @@ static int virtio_net_set_vnet_endian_one(VirtIODevice *vdev,
}
static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
- int queues, bool enable)
+ int queue_pairs, bool enable)
{
int i;
- for (i = 0; i < queues; i++) {
+ for (i = 0; i < queue_pairs; i++) {
if (virtio_net_set_vnet_endian_one(vdev, ncs[i].peer, enable) < 0 &&
enable) {
while (--i >= 0) {
@@ -330,7 +331,7 @@ static bool virtio_net_set_vnet_endian(VirtIODevice *vdev, NetClientState *ncs,
static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
{
VirtIODevice *vdev = VIRTIO_DEVICE(n);
- int queues = n->multiqueue ? n->max_queues : 1;
+ int queue_pairs = n->multiqueue ? n->max_queue_pairs : 1;
if (virtio_net_started(n, status)) {
/* Before using the device, we tell the network backend about the
@@ -339,14 +340,14 @@ static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status)
* virtio-net code.
*/
n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs,
- queues, true);
+ queue_pairs, true);
} else if (virtio_net_started(n, vdev->status)) {
/* After using the device, we need to reset the network backend to
* the default (guest native endianness), otherwise the guest may
* lose network connectivity if it is rebooted into a different
* endianness.
*/
- virtio_net_set_vnet_endian(vdev, n->nic->ncs, queues, false);
+ virtio_net_set_vnet_endian(vdev, n->nic->ncs, queue_pairs, false);
}
}
@@ -368,12 +369,12 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
virtio_net_vnet_endian_status(n, status);
virtio_net_vhost_status(n, status);
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
NetClientState *ncs = qemu_get_subqueue(n->nic, i);
bool queue_started;
q = &n->vqs[i];
- if ((!n->multiqueue && i != 0) || i >= n->curr_queues) {
+ if ((!n->multiqueue && i != 0) || i >= n->curr_queue_pairs) {
queue_status = 0;
} else {
queue_status = status;
@@ -540,7 +541,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
n->nouni = 0;
n->nobcast = 0;
/* multiqueue is disabled by default */
- n->curr_queues = 1;
+ n->curr_queue_pairs = 1;
timer_del(n->announce_timer.tm);
n->announce_timer.round = 0;
n->status &= ~VIRTIO_NET_S_ANNOUNCE;
@@ -556,7 +557,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
memset(n->vlans, 0, MAX_VLAN >> 3);
/* Flush any async TX */
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
NetClientState *nc = qemu_get_subqueue(n->nic, i);
if (nc->peer) {
@@ -610,7 +611,7 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
sizeof(struct virtio_net_hdr);
}
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
nc = qemu_get_subqueue(n->nic, i);
if (peer_has_vnet_hdr(n) &&
@@ -655,7 +656,7 @@ static int peer_attach(VirtIONet *n, int index)
return 0;
}
- if (n->max_queues == 1) {
+ if (n->max_queue_pairs == 1) {
return 0;
}
@@ -681,7 +682,7 @@ static int peer_detach(VirtIONet *n, int index)
return tap_disable(nc->peer);
}
-static void virtio_net_set_queues(VirtIONet *n)
+static void virtio_net_set_queue_pairs(VirtIONet *n)
{
int i;
int r;
@@ -690,8 +691,8 @@ static void virtio_net_set_queues(VirtIONet *n)
return;
}
- for (i = 0; i < n->max_queues; i++) {
- if (i < n->curr_queues) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
+ if (i < n->curr_queue_pairs) {
r = peer_attach(n, i);
assert(!r);
} else {
@@ -905,7 +906,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features)
virtio_net_apply_guest_offloads(n);
}
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
NetClientState *nc = qemu_get_subqueue(n->nic, i);
if (!get_vhost_net(nc->peer)) {
@@ -1232,7 +1233,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
VirtIODevice *vdev = VIRTIO_DEVICE(n);
struct virtio_net_rss_config cfg;
size_t s, offset = 0, size_get;
- uint16_t queues, i;
+ uint16_t queue_pairs, i;
struct {
uint16_t us;
uint8_t b;
@@ -1274,7 +1275,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
}
n->rss_data.default_queue = do_rss ?
virtio_lduw_p(vdev, &cfg.unclassified_queue) : 0;
- if (n->rss_data.default_queue >= n->max_queues) {
+ if (n->rss_data.default_queue >= n->max_queue_pairs) {
err_msg = "Invalid default queue";
err_value = n->rss_data.default_queue;
goto error;
@@ -1303,14 +1304,14 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
size_get = sizeof(temp);
s = iov_to_buf(iov, iov_cnt, offset, &temp, size_get);
if (s != size_get) {
- err_msg = "Can't get queues";
+ err_msg = "Can't get queue_pairs";
err_value = (uint32_t)s;
goto error;
}
- queues = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queues;
- if (queues == 0 || queues > n->max_queues) {
- err_msg = "Invalid number of queues";
- err_value = queues;
+ queue_pairs = do_rss ? virtio_lduw_p(vdev, &temp.us) : n->curr_queue_pairs;
+ if (queue_pairs == 0 || queue_pairs > n->max_queue_pairs) {
+ err_msg = "Invalid number of queue_pairs";
+ err_value = queue_pairs;
goto error;
}
if (temp.b > VIRTIO_NET_RSS_MAX_KEY_SIZE) {
@@ -1325,7 +1326,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
}
if (!temp.b && !n->rss_data.hash_types) {
virtio_net_disable_rss(n);
- return queues;
+ return queue_pairs;
}
offset += size_get;
size_get = temp.b;
@@ -1358,7 +1359,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n,
trace_virtio_net_rss_enable(n->rss_data.hash_types,
n->rss_data.indirections_len,
temp.b);
- return queues;
+ return queue_pairs;
error:
trace_virtio_net_rss_error(err_msg, err_value);
virtio_net_disable_rss(n);
@@ -1369,15 +1370,15 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
struct iovec *iov, unsigned int iov_cnt)
{
VirtIODevice *vdev = VIRTIO_DEVICE(n);
- uint16_t queues;
+ uint16_t queue_pairs;
virtio_net_disable_rss(n);
if (cmd == VIRTIO_NET_CTRL_MQ_HASH_CONFIG) {
- queues = virtio_net_handle_rss(n, iov, iov_cnt, false);
- return queues ? VIRTIO_NET_OK : VIRTIO_NET_ERR;
+ queue_pairs = virtio_net_handle_rss(n, iov, iov_cnt, false);
+ return queue_pairs ? VIRTIO_NET_OK : VIRTIO_NET_ERR;
}
if (cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) {
- queues = virtio_net_handle_rss(n, iov, iov_cnt, true);
+ queue_pairs = virtio_net_handle_rss(n, iov, iov_cnt, true);
} else if (cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
struct virtio_net_ctrl_mq mq;
size_t s;
@@ -1388,24 +1389,24 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd,
if (s != sizeof(mq)) {
return VIRTIO_NET_ERR;
}
- queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
+ queue_pairs = virtio_lduw_p(vdev, &mq.virtqueue_pairs);
} else {
return VIRTIO_NET_ERR;
}
- if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
- queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
- queues > n->max_queues ||
+ if (queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN ||
+ queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX ||
+ queue_pairs > n->max_queue_pairs ||
!n->multiqueue) {
return VIRTIO_NET_ERR;
}
- n->curr_queues = queues;
- /* stop the backend before changing the number of queues to avoid handling a
+ n->curr_queue_pairs = queue_pairs;
+ /* stop the backend before changing the number of queue_pairs to avoid handling a
* disabled queue */
virtio_net_set_status(vdev, vdev->status);
- virtio_net_set_queues(n);
+ virtio_net_set_queue_pairs(n);
return VIRTIO_NET_OK;
}
@@ -1483,7 +1484,7 @@ static bool virtio_net_can_receive(NetClientState *nc)
return false;
}
- if (nc->queue_index >= n->curr_queues) {
+ if (nc->queue_index >= n->curr_queue_pairs) {
return false;
}
@@ -2763,11 +2764,11 @@ static void virtio_net_del_queue(VirtIONet *n, int index)
virtio_del_queue(vdev, index * 2 + 1);
}
-static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
+static void virtio_net_change_num_queue_pairs(VirtIONet *n, int new_max_queue_pairs)
{
VirtIODevice *vdev = VIRTIO_DEVICE(n);
int old_num_queues = virtio_get_num_queues(vdev);
- int new_num_queues = new_max_queues * 2 + 1;
+ int new_num_queues = new_max_queue_pairs * 2 + 1;
int i;
assert(old_num_queues >= 3);
@@ -2800,12 +2801,12 @@ static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues)
static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue)
{
- int max = multiqueue ? n->max_queues : 1;
+ int max = multiqueue ? n->max_queue_pairs : 1;
n->multiqueue = multiqueue;
- virtio_net_change_num_queues(n, max);
+ virtio_net_change_num_queue_pairs(n, max);
- virtio_net_set_queues(n);
+ virtio_net_set_queue_pairs(n);
}
static int virtio_net_post_load_device(void *opaque, int version_id)
@@ -2838,7 +2839,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
*/
n->saved_guest_offloads = n->curr_guest_offloads;
- virtio_net_set_queues(n);
+ virtio_net_set_queue_pairs(n);
/* Find the first multicast entry in the saved MAC filter */
for (i = 0; i < n->mac_table.in_use; i++) {
@@ -2851,7 +2852,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id)
/* nc.link_down can't be migrated, so infer link_down according
* to link status bit in n->status */
link_down = (n->status & VIRTIO_NET_S_LINK_UP) == 0;
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
qemu_get_subqueue(n->nic, i)->link_down = link_down;
}
@@ -2916,9 +2917,9 @@ static const VMStateDescription vmstate_virtio_net_queue_tx_waiting = {
},
};
-static bool max_queues_gt_1(void *opaque, int version_id)
+static bool max_queue_pairs_gt_1(void *opaque, int version_id)
{
- return VIRTIO_NET(opaque)->max_queues > 1;
+ return VIRTIO_NET(opaque)->max_queue_pairs > 1;
}
static bool has_ctrl_guest_offloads(void *opaque, int version_id)
@@ -2943,13 +2944,13 @@ static bool mac_table_doesnt_fit(void *opaque, int version_id)
struct VirtIONetMigTmp {
VirtIONet *parent;
VirtIONetQueue *vqs_1;
- uint16_t curr_queues_1;
+ uint16_t curr_queue_pairs_1;
uint8_t has_ufo;
uint32_t has_vnet_hdr;
};
/* The 2nd and subsequent tx_waiting flags are loaded later than
- * the 1st entry in the queues and only if there's more than one
+ * the 1st entry in the queue_pairs and only if there's more than one
* entry. We use the tmp mechanism to calculate a temporary
* pointer and count and also validate the count.
*/
@@ -2959,9 +2960,9 @@ static int virtio_net_tx_waiting_pre_save(void *opaque)
struct VirtIONetMigTmp *tmp = opaque;
tmp->vqs_1 = tmp->parent->vqs + 1;
- tmp->curr_queues_1 = tmp->parent->curr_queues - 1;
- if (tmp->parent->curr_queues == 0) {
- tmp->curr_queues_1 = 0;
+ tmp->curr_queue_pairs_1 = tmp->parent->curr_queue_pairs - 1;
+ if (tmp->parent->curr_queue_pairs == 0) {
+ tmp->curr_queue_pairs_1 = 0;
}
return 0;
@@ -2974,9 +2975,9 @@ static int virtio_net_tx_waiting_pre_load(void *opaque)
/* Reuse the pointer setup from save */
virtio_net_tx_waiting_pre_save(opaque);
- if (tmp->parent->curr_queues > tmp->parent->max_queues) {
- error_report("virtio-net: curr_queues %x > max_queues %x",
- tmp->parent->curr_queues, tmp->parent->max_queues);
+ if (tmp->parent->curr_queue_pairs > tmp->parent->max_queue_pairs) {
+ error_report("virtio-net: curr_queue_pairs %x > max_queue_pairs %x",
+ tmp->parent->curr_queue_pairs, tmp->parent->max_queue_pairs);
return -EINVAL;
}
@@ -2990,7 +2991,7 @@ static const VMStateDescription vmstate_virtio_net_tx_waiting = {
.pre_save = virtio_net_tx_waiting_pre_save,
.fields = (VMStateField[]) {
VMSTATE_STRUCT_VARRAY_POINTER_UINT16(vqs_1, struct VirtIONetMigTmp,
- curr_queues_1,
+ curr_queue_pairs_1,
vmstate_virtio_net_queue_tx_waiting,
struct VirtIONetQueue),
VMSTATE_END_OF_LIST()
@@ -3132,9 +3133,9 @@ static const VMStateDescription vmstate_virtio_net_device = {
VMSTATE_UINT8(nobcast, VirtIONet),
VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
vmstate_virtio_net_has_ufo),
- VMSTATE_SINGLE_TEST(max_queues, VirtIONet, max_queues_gt_1, 0,
+ VMSTATE_SINGLE_TEST(max_queue_pairs, VirtIONet, max_queue_pairs_gt_1, 0,
vmstate_info_uint16_equal, uint16_t),
- VMSTATE_UINT16_TEST(curr_queues, VirtIONet, max_queues_gt_1),
+ VMSTATE_UINT16_TEST(curr_queue_pairs, VirtIONet, max_queue_pairs_gt_1),
VMSTATE_WITH_TMP(VirtIONet, struct VirtIONetMigTmp,
vmstate_virtio_net_tx_waiting),
VMSTATE_UINT64_TEST(curr_guest_offloads, VirtIONet,
@@ -3299,19 +3300,41 @@ static bool failover_hide_primary_device(DeviceListener *listener,
if (!device_opts) {
return false;
}
- standby_id = qdict_get_try_str(device_opts, "failover_pair_id");
- if (g_strcmp0(standby_id, n->netclient_name) != 0) {
+
+ if (!qdict_haskey(device_opts, "failover_pair_id")) {
return false;
}
- if (n->primary_opts) {
- error_setg(errp, "Cannot attach more than one primary device to '%s'",
- n->netclient_name);
+ if (!qdict_haskey(device_opts, "id")) {
+ error_setg(errp, "Device with failover_pair_id needs to have id");
return false;
}
- n->primary_opts = qdict_clone_shallow(device_opts);
- n->primary_opts_from_json = from_json;
+ standby_id = qdict_get_str(device_opts, "failover_pair_id");
+ if (g_strcmp0(standby_id, n->netclient_name) != 0) {
+ return false;
+ }
+
+ /*
+ * The hide helper can be called several times for a given device.
+ * Check there is only one primary for a virtio-net device but
+ * don't duplicate the qdict several times if it's called for the same
+ * device.
+ */
+ if (n->primary_opts) {
+ const char *old, *new;
+ /* devices with failover_pair_id always have an id */
+ old = qdict_get_str(n->primary_opts, "id");
+ new = qdict_get_str(device_opts, "id");
+ if (strcmp(old, new) != 0) {
+ error_setg(errp, "Cannot attach more than one primary device to "
+ "'%s': '%s' and '%s'", n->netclient_name, old, new);
+ return false;
+ }
+ } else {
+ n->primary_opts = qdict_clone_shallow(device_opts);
+ n->primary_opts_from_json = from_json;
+ }
/* failover_primary_hidden is set during feature negotiation */
return qatomic_read(&n->failover_primary_hidden);
@@ -3389,16 +3412,30 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
return;
}
- n->max_queues = MAX(n->nic_conf.peers.queues, 1);
- if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) {
- error_setg(errp, "Invalid number of queues (= %" PRIu32 "), "
+ n->max_ncs = MAX(n->nic_conf.peers.queues, 1);
+
+ /*
+ * Figure out the datapath queue pairs since the backend could
+ * provide control queue via peers as well.
+ */
+ if (n->nic_conf.peers.queues) {
+ for (i = 0; i < n->max_ncs; i++) {
+ if (n->nic_conf.peers.ncs[i]->is_datapath) {
+ ++n->max_queue_pairs;
+ }
+ }
+ }
+ n->max_queue_pairs = MAX(n->max_queue_pairs, 1);
+
+ if (n->max_queue_pairs * 2 + 1 > VIRTIO_QUEUE_MAX) {
+ error_setg(errp, "Invalid number of queue pairs (= %" PRIu32 "), "
"must be a positive integer less than %d.",
- n->max_queues, (VIRTIO_QUEUE_MAX - 1) / 2);
+ n->max_queue_pairs, (VIRTIO_QUEUE_MAX - 1) / 2);
virtio_cleanup(vdev);
return;
}
- n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queues);
- n->curr_queues = 1;
+ n->vqs = g_malloc0(sizeof(VirtIONetQueue) * n->max_queue_pairs);
+ n->curr_queue_pairs = 1;
n->tx_timeout = n->net_conf.txtimer;
if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer")
@@ -3412,7 +3449,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n),
n->net_conf.tx_queue_size);
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
virtio_net_add_queue(n, i);
}
@@ -3436,13 +3473,13 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
object_get_typename(OBJECT(dev)), dev->id, n);
}
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
n->nic->ncs[i].do_not_pad = true;
}
peer_test_vnet_hdr(n);
if (peer_has_vnet_hdr(n)) {
- for (i = 0; i < n->max_queues; i++) {
+ for (i = 0; i < n->max_queue_pairs; i++) {
qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true);
}
n->host_hdr_len = sizeof(struct virtio_net_hdr);
@@ -3484,7 +3521,7 @@ static void virtio_net_device_unrealize(DeviceState *dev)
{
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
VirtIONet *n = VIRTIO_NET(dev);
- int i, max_queues;
+ int i, max_queue_pairs;
if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) {
virtio_net_unload_ebpf(n);
@@ -3509,12 +3546,12 @@ static void virtio_net_device_unrealize(DeviceState *dev)
assert(n->primary_opts == NULL);
}
- max_queues = n->multiqueue ? n->max_queues : 1;
- for (i = 0; i < max_queues; i++) {
+ max_queue_pairs = n->multiqueue ? n->max_queue_pairs : 1;
+ for (i = 0; i < max_queue_pairs; i++) {
virtio_net_del_queue(n, i);
}
/* delete also control vq */
- virtio_del_queue(vdev, max_queues * 2);
+ virtio_del_queue(vdev, max_queue_pairs * 2);
qemu_announce_timer_del(&n->announce_timer, false);
g_free(n->vqs);
qemu_del_nic(n->nic);
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index d1f902ee86..da34c8ebcd 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -448,11 +448,11 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset,
PCIBridgeQemuCap cap = {
.len = cap_len,
.type = REDHAT_PCI_CAP_RESOURCE_RESERVE,
- .bus_res = res_reserve.bus,
- .io = res_reserve.io,
- .mem = res_reserve.mem_non_pref,
- .mem_pref_32 = res_reserve.mem_pref_32,
- .mem_pref_64 = res_reserve.mem_pref_64
+ .bus_res = cpu_to_le32(res_reserve.bus),
+ .io = cpu_to_le64(res_reserve.io),
+ .mem = cpu_to_le32(res_reserve.mem_non_pref),
+ .mem_pref_32 = cpu_to_le32(res_reserve.mem_pref_32),
+ .mem_pref_64 = cpu_to_le64(res_reserve.mem_pref_64)
};
int offset = pci_add_capability(dev, PCI_CAP_ID_VNDR,
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
index 35ab45e209..c144d42f9b 100644
--- a/hw/virtio/Kconfig
+++ b/hw/virtio/Kconfig
@@ -63,3 +63,8 @@ config VHOST_USER_I2C
bool
default y
depends on VIRTIO && VHOST_USER
+
+config VHOST_USER_RNG
+ bool
+ default y
+ depends on VIRTIO && VHOST_USER
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
index bc352a6009..521f7d64a8 100644
--- a/hw/virtio/meson.build
+++ b/hw/virtio/meson.build
@@ -27,6 +27,8 @@ virtio_ss.add(when: 'CONFIG_VIRTIO_IOMMU', if_true: files('virtio-iommu.c'))
virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c'))
virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], if_true: files('vhost-user-i2c-pci.c'))
+virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
+virtio_ss.add(when: ['CONFIG_VHOST_USER_RNG', 'CONFIG_VIRTIO_PCI'], if_true: files('vhost-user-rng-pci.c'))
virtio_pci_ss = ss.source_set()
virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c'))
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 8ed19e9d0c..650e521e35 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -52,6 +52,7 @@ vhost_vdpa_set_vring_call(void *dev, unsigned int index, int fd) "dev: %p index:
vhost_vdpa_get_features(void *dev, uint64_t features) "dev: %p features: 0x%"PRIx64
vhost_vdpa_set_owner(void *dev) "dev: %p"
vhost_vdpa_vq_get_addr(void *dev, void *vq, uint64_t desc_user_addr, uint64_t avail_user_addr, uint64_t used_user_addr) "dev: %p vq: %p desc_user_addr: 0x%"PRIx64" avail_user_addr: 0x%"PRIx64" used_user_addr: 0x%"PRIx64
+vhost_vdpa_get_iova_range(void *dev, uint64_t first, uint64_t last) "dev: %p first: 0x%"PRIx64" last: 0x%"PRIx64
# virtio.c
virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u"
diff --git a/hw/virtio/vhost-user-rng-pci.c b/hw/virtio/vhost-user-rng-pci.c
new file mode 100644
index 0000000000..c83dc86813
--- /dev/null
+++ b/hw/virtio/vhost-user-rng-pci.c
@@ -0,0 +1,79 @@
+/*
+ * Vhost-user RNG virtio device PCI glue
+ *
+ * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/vhost-user-rng.h"
+#include "virtio-pci.h"
+
+struct VHostUserRNGPCI {
+ VirtIOPCIProxy parent_obj;
+ VHostUserRNG vdev;
+};
+
+typedef struct VHostUserRNGPCI VHostUserRNGPCI;
+
+#define TYPE_VHOST_USER_RNG_PCI "vhost-user-rng-pci-base"
+
+DECLARE_INSTANCE_CHECKER(VHostUserRNGPCI, VHOST_USER_RNG_PCI,
+ TYPE_VHOST_USER_RNG_PCI)
+
+static Property vhost_user_rng_pci_properties[] = {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VHostUserRNGPCI *dev = VHOST_USER_RNG_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&dev->vdev);
+
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = 1;
+ }
+
+ qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
+}
+
+static void vhost_user_rng_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+ k->realize = vhost_user_rng_pci_realize;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ device_class_set_props(dc, vhost_user_rng_pci_properties);
+ pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ pcidev_k->device_id = 0; /* Set by virtio-pci based on virtio id */
+ pcidev_k->revision = 0x00;
+ pcidev_k->class_id = PCI_CLASS_OTHERS;
+}
+
+static void vhost_user_rng_pci_instance_init(Object *obj)
+{
+ VHostUserRNGPCI *dev = VHOST_USER_RNG_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VHOST_USER_RNG);
+}
+
+static const VirtioPCIDeviceTypeInfo vhost_user_rng_pci_info = {
+ .base_name = TYPE_VHOST_USER_RNG_PCI,
+ .non_transitional_name = "vhost-user-rng-pci",
+ .instance_size = sizeof(VHostUserRNGPCI),
+ .instance_init = vhost_user_rng_pci_instance_init,
+ .class_init = vhost_user_rng_pci_class_init,
+};
+
+static void vhost_user_rng_pci_register(void)
+{
+ virtio_pci_types_register(&vhost_user_rng_pci_info);
+}
+
+type_init(vhost_user_rng_pci_register);
diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c
new file mode 100644
index 0000000000..209ee5bf9a
--- /dev/null
+++ b/hw/virtio/vhost-user-rng.c
@@ -0,0 +1,289 @@
+/*
+ * Vhost-user RNG virtio device
+ *
+ * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org>
+ *
+ * Implementation seriously tailored on vhost-user-i2c.c
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/vhost-user-rng.h"
+#include "qemu/error-report.h"
+#include "standard-headers/linux/virtio_ids.h"
+
+static void vu_rng_start(VirtIODevice *vdev)
+{
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+ int i;
+
+ if (!k->set_guest_notifiers) {
+ error_report("binding does not support guest notifiers");
+ return;
+ }
+
+ ret = vhost_dev_enable_notifiers(&rng->vhost_dev, vdev);
+ if (ret < 0) {
+ error_report("Error enabling host notifiers: %d", -ret);
+ return;
+ }
+
+ ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, true);
+ if (ret < 0) {
+ error_report("Error binding guest notifier: %d", -ret);
+ goto err_host_notifiers;
+ }
+
+ rng->vhost_dev.acked_features = vdev->guest_features;
+ ret = vhost_dev_start(&rng->vhost_dev, vdev);
+ if (ret < 0) {
+ error_report("Error starting vhost-user-rng: %d", -ret);
+ goto err_guest_notifiers;
+ }
+
+ /*
+ * guest_notifier_mask/pending not used yet, so just unmask
+ * everything here. virtio-pci will do the right thing by
+ * enabling/disabling irqfd.
+ */
+ for (i = 0; i < rng->vhost_dev.nvqs; i++) {
+ vhost_virtqueue_mask(&rng->vhost_dev, vdev, i, false);
+ }
+
+ return;
+
+err_guest_notifiers:
+ k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false);
+err_host_notifiers:
+ vhost_dev_disable_notifiers(&rng->vhost_dev, vdev);
+}
+
+static void vu_rng_stop(VirtIODevice *vdev)
+{
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ int ret;
+
+ if (!k->set_guest_notifiers) {
+ return;
+ }
+
+ vhost_dev_stop(&rng->vhost_dev, vdev);
+
+ ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false);
+ if (ret < 0) {
+ error_report("vhost guest notifier cleanup failed: %d", ret);
+ return;
+ }
+
+ vhost_dev_disable_notifiers(&rng->vhost_dev, vdev);
+}
+
+static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status)
+{
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+ bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
+
+ if (!vdev->vm_running) {
+ should_start = false;
+ }
+
+ if (rng->vhost_dev.started == should_start) {
+ return;
+ }
+
+ if (should_start) {
+ vu_rng_start(vdev);
+ } else {
+ vu_rng_stop(vdev);
+ }
+}
+
+static uint64_t vu_rng_get_features(VirtIODevice *vdev,
+ uint64_t requested_features, Error **errp)
+{
+ /* No feature bits used yet */
+ return requested_features;
+}
+
+static void vu_rng_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /*
+ * Not normally called; it's the daemon that handles the queue;
+ * however virtio's cleanup path can call this.
+ */
+}
+
+static void vu_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
+{
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+
+ vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask);
+}
+
+static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx)
+{
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+
+ return vhost_virtqueue_pending(&rng->vhost_dev, idx);
+}
+
+static void vu_rng_connect(DeviceState *dev)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+
+ if (rng->connected) {
+ return;
+ }
+
+ rng->connected = true;
+
+ /* restore vhost state */
+ if (virtio_device_started(vdev, vdev->status)) {
+ vu_rng_start(vdev);
+ }
+}
+
+static void vu_rng_disconnect(DeviceState *dev)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserRNG *rng = VHOST_USER_RNG(vdev);
+
+ if (!rng->connected) {
+ return;
+ }
+
+ rng->connected = false;
+
+ if (rng->vhost_dev.started) {
+ vu_rng_stop(vdev);
+ }
+}
+
+static void vu_rng_event(void *opaque, QEMUChrEvent event)
+{
+ DeviceState *dev = opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ vu_rng_connect(dev);
+ break;
+ case CHR_EVENT_CLOSED:
+ vu_rng_disconnect(dev);
+ break;
+ case CHR_EVENT_BREAK:
+ case CHR_EVENT_MUX_IN:
+ case CHR_EVENT_MUX_OUT:
+ /* Ignore */
+ break;
+ }
+}
+
+static void vu_rng_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserRNG *rng = VHOST_USER_RNG(dev);
+ int ret;
+
+ if (!rng->chardev.chr) {
+ error_setg(errp, "missing chardev");
+ return;
+ }
+
+ if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) {
+ return;
+ }
+
+ virtio_init(vdev, "vhost-user-rng", VIRTIO_ID_RNG, 0);
+
+ rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output);
+ if (!rng->req_vq) {
+ error_setg_errno(errp, -1, "virtio_add_queue() failed");
+ goto virtio_add_queue_failed;
+ }
+
+ rng->vhost_dev.nvqs = 1;
+ rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs);
+ ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user,
+ VHOST_BACKEND_TYPE_USER, 0, errp);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "vhost_dev_init() failed");
+ goto vhost_dev_init_failed;
+ }
+
+ qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL,
+ dev, NULL, true);
+
+ return;
+
+vhost_dev_init_failed:
+ virtio_delete_queue(rng->req_vq);
+virtio_add_queue_failed:
+ virtio_cleanup(vdev);
+ vhost_user_cleanup(&rng->vhost_user);
+}
+
+static void vu_rng_device_unrealize(DeviceState *dev)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VHostUserRNG *rng = VHOST_USER_RNG(dev);
+
+ vu_rng_set_status(vdev, 0);
+
+ vhost_dev_cleanup(&rng->vhost_dev);
+ g_free(rng->vhost_dev.vqs);
+ rng->vhost_dev.vqs = NULL;
+ virtio_delete_queue(rng->req_vq);
+ virtio_cleanup(vdev);
+ vhost_user_cleanup(&rng->vhost_user);
+}
+
+static const VMStateDescription vu_rng_vmstate = {
+ .name = "vhost-user-rng",
+ .unmigratable = 1,
+};
+
+static Property vu_rng_properties[] = {
+ DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vu_rng_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ device_class_set_props(dc, vu_rng_properties);
+ dc->vmsd = &vu_rng_vmstate;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+
+ vdc->realize = vu_rng_device_realize;
+ vdc->unrealize = vu_rng_device_unrealize;
+ vdc->get_features = vu_rng_get_features;
+ vdc->set_status = vu_rng_set_status;
+ vdc->guest_notifier_mask = vu_rng_guest_notifier_mask;
+ vdc->guest_notifier_pending = vu_rng_guest_notifier_pending;
+}
+
+static const TypeInfo vu_rng_info = {
+ .name = TYPE_VHOST_USER_RNG,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VHostUserRNG),
+ .class_init = vu_rng_class_init,
+};
+
+static void vu_rng_register_types(void)
+{
+ type_register_static(&vu_rng_info);
+}
+
+type_init(vu_rng_register_types)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 2c8556237f..bf6e50223c 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1526,8 +1526,9 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev,
name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]",
user, queue_idx);
- memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name,
- page_size, addr);
+ if (!n->mr.ram) /* Don't init again after suspend. */
+ memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name,
+ page_size, addr);
g_free(name);
if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) {
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index 47d7a5a23d..12661fd5b1 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -24,19 +24,49 @@
#include "trace.h"
#include "qemu-common.h"
-static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section)
+/*
+ * Return one past the end of the end of section. Be careful with uint64_t
+ * conversions!
+ */
+static Int128 vhost_vdpa_section_end(const MemoryRegionSection *section)
+{
+ Int128 llend = int128_make64(section->offset_within_address_space);
+ llend = int128_add(llend, section->size);
+ llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
+
+ return llend;
+}
+
+static bool vhost_vdpa_listener_skipped_section(MemoryRegionSection *section,
+ uint64_t iova_min,
+ uint64_t iova_max)
{
- return (!memory_region_is_ram(section->mr) &&
- !memory_region_is_iommu(section->mr)) ||
- /* vhost-vDPA doesn't allow MMIO to be mapped */
- memory_region_is_ram_device(section->mr) ||
- /*
- * Sizing an enabled 64-bit BAR can cause spurious mappings to
- * addresses in the upper part of the 64-bit address space. These
- * are never accessed by the CPU and beyond the address width of
- * some IOMMU hardware. TODO: VDPA should tell us the IOMMU width.
- */
- section->offset_within_address_space & (1ULL << 63);
+ Int128 llend;
+
+ if ((!memory_region_is_ram(section->mr) &&
+ !memory_region_is_iommu(section->mr)) ||
+ memory_region_is_protected(section->mr) ||
+ /* vhost-vDPA doesn't allow MMIO to be mapped */
+ memory_region_is_ram_device(section->mr)) {
+ return true;
+ }
+
+ if (section->offset_within_address_space < iova_min) {
+ error_report("RAM section out of device range (min=0x%" PRIx64
+ ", addr=0x%" HWADDR_PRIx ")",
+ iova_min, section->offset_within_address_space);
+ return true;
+ }
+
+ llend = vhost_vdpa_section_end(section);
+ if (int128_gt(llend, int128_make64(iova_max))) {
+ error_report("RAM section out of device range (max=0x%" PRIx64
+ ", end addr=0x%" PRIx64 ")",
+ iova_max, int128_get64(llend));
+ return true;
+ }
+
+ return false;
}
static int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size,
@@ -148,7 +178,8 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
void *vaddr;
int ret;
- if (vhost_vdpa_listener_skipped_section(section)) {
+ if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first,
+ v->iova_range.last)) {
return;
}
@@ -159,10 +190,7 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener,
}
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
- llend = int128_make64(section->offset_within_address_space);
- llend = int128_add(llend, section->size);
- llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
-
+ llend = vhost_vdpa_section_end(section);
if (int128_ge(int128_make64(iova), llend)) {
return;
}
@@ -209,7 +237,8 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener,
Int128 llend, llsize;
int ret;
- if (vhost_vdpa_listener_skipped_section(section)) {
+ if (vhost_vdpa_listener_skipped_section(section, v->iova_range.first,
+ v->iova_range.last)) {
return;
}
@@ -220,9 +249,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener,
}
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
- llend = int128_make64(section->offset_within_address_space);
- llend = int128_add(llend, section->size);
- llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
+ llend = vhost_vdpa_section_end(section);
trace_vhost_vdpa_listener_region_del(v, iova, int128_get64(llend));
@@ -279,6 +306,26 @@ static void vhost_vdpa_add_status(struct vhost_dev *dev, uint8_t status)
vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &s);
}
+static void vhost_vdpa_get_iova_range(struct vhost_vdpa *v)
+{
+ int ret = vhost_vdpa_call(v->dev, VHOST_VDPA_GET_IOVA_RANGE,
+ &v->iova_range);
+ if (ret != 0) {
+ v->iova_range.first = 0;
+ v->iova_range.last = UINT64_MAX;
+ }
+
+ trace_vhost_vdpa_get_iova_range(v->dev, v->iova_range.first,
+ v->iova_range.last);
+}
+
+static bool vhost_vdpa_one_time_request(struct vhost_dev *dev)
+{
+ struct vhost_vdpa *v = dev->opaque;
+
+ return v->index != 0;
+}
+
static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
{
struct vhost_vdpa *v;
@@ -291,6 +338,12 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp)
v->listener = vhost_vdpa_memory_listener;
v->msg_type = VHOST_IOTLB_MSG_V2;
+ vhost_vdpa_get_iova_range(v);
+
+ if (vhost_vdpa_one_time_request(dev)) {
+ return 0;
+ }
+
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);
@@ -401,6 +454,10 @@ static int vhost_vdpa_memslots_limit(struct vhost_dev *dev)
static int vhost_vdpa_set_mem_table(struct vhost_dev *dev,
struct vhost_memory *mem)
{
+ if (vhost_vdpa_one_time_request(dev)) {
+ return 0;
+ }
+
trace_vhost_vdpa_set_mem_table(dev, mem->nregions, mem->padding);
if (trace_event_get_state_backends(TRACE_VHOST_VDPA_SET_MEM_TABLE) &&
trace_event_get_state_backends(TRACE_VHOST_VDPA_DUMP_REGIONS)) {
@@ -424,6 +481,11 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev,
uint64_t features)
{
int ret;
+
+ if (vhost_vdpa_one_time_request(dev)) {
+ return 0;
+ }
+
trace_vhost_vdpa_set_features(dev, features);
ret = vhost_vdpa_call(dev, VHOST_SET_FEATURES, &features);
uint8_t status = 0;
@@ -448,9 +510,12 @@ static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev)
}
features &= f;
- r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features);
- if (r) {
- return -EFAULT;
+
+ if (vhost_vdpa_one_time_request(dev)) {
+ r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features);
+ if (r) {
+ return -EFAULT;
+ }
}
dev->backend_cap = features;
@@ -481,8 +546,8 @@ static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx)
{
assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs);
- trace_vhost_vdpa_get_vq_index(dev, idx, idx - dev->vq_index);
- return idx - dev->vq_index;
+ trace_vhost_vdpa_get_vq_index(dev, idx, idx);
+ return idx;
}
static int vhost_vdpa_set_vring_ready(struct vhost_dev *dev)
@@ -559,11 +624,21 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
{
struct vhost_vdpa *v = dev->opaque;
trace_vhost_vdpa_dev_start(dev, started);
+
if (started) {
- uint8_t status = 0;
- memory_listener_register(&v->listener, &address_space_memory);
vhost_vdpa_host_notifiers_init(dev);
vhost_vdpa_set_vring_ready(dev);
+ } else {
+ vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs);
+ }
+
+ if (dev->vq_index + dev->nvqs != dev->last_index) {
+ return 0;
+ }
+
+ if (started) {
+ uint8_t status = 0;
+ memory_listener_register(&v->listener, &address_space_memory);
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
vhost_vdpa_call(dev, VHOST_VDPA_GET_STATUS, &status);
@@ -572,7 +647,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
vhost_vdpa_reset_device(dev);
vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);
- vhost_vdpa_host_notifiers_uninit(dev, dev->nvqs);
memory_listener_unregister(&v->listener);
return 0;
@@ -582,6 +656,10 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started)
static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base,
struct vhost_log *log)
{
+ if (vhost_vdpa_one_time_request(dev)) {
+ return 0;
+ }
+
trace_vhost_vdpa_set_log_base(dev, base, log->size, log->refcnt, log->fd,
log->log);
return vhost_vdpa_call(dev, VHOST_SET_LOG_BASE, &base);
@@ -647,6 +725,10 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev,
static int vhost_vdpa_set_owner(struct vhost_dev *dev)
{
+ if (vhost_vdpa_one_time_request(dev)) {
+ return 0;
+ }
+
trace_vhost_vdpa_set_owner(dev);
return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL);
}
diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c
index 770c286be7..a160ae6b41 100644
--- a/hw/virtio/virtio-iommu-pci.c
+++ b/hw/virtio/virtio-iommu-pci.c
@@ -98,9 +98,7 @@ static void virtio_iommu_pci_instance_init(Object *obj)
}
static const VirtioPCIDeviceTypeInfo virtio_iommu_pci_info = {
- .base_name = TYPE_VIRTIO_IOMMU_PCI,
- .generic_name = "virtio-iommu-pci",
- .non_transitional_name = "virtio-iommu-pci-non-transitional",
+ .generic_name = TYPE_VIRTIO_IOMMU_PCI,
.instance_size = sizeof(VirtIOIOMMUPCI),
.instance_init = virtio_iommu_pci_instance_init,
.class_init = virtio_iommu_pci_class_init,