aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-07-29 11:57:01 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-07-29 11:57:01 +0100
commitcbe81c6331d875750720c021492ca74e312f9cb2 (patch)
treed0cb64e14d8b326b3057eec82efb4baffbed93e5
parentaa2aac51f0f888b9a4a4fd461d5816ad84a1c448 (diff)
parentf077f889121c89612eaf0f21e94b97aa110a6910 (diff)
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
pc, pci, virtio: cleanups, fixes a bunch of bugfixes and a couple of cleanups making these easier and/or making debugging easier Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Fri 29 Jul 2016 04:11:01 BST # gpg: using RSA key 0x281F0DB8D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * remotes/mst/tags/for_upstream: (41 commits) mptsas: Fix a migration compatible issue vhost: do not update last avail idx on get_vring_base() failure vhost: add vhost_net_set_backend() vhost-user: add error report in vhost_user_write() tests: fix vhost-user-test leak tests: plug some leaks in virtio-net-test vhost-user: wait until backend init is completed char: add and use tcp_chr_wait_connected char: add chr_wait_connected callback vhost: add assert() to check runtime behaviour vhost-net: vhost_migration_done is vhost-user specific Revert "vhost-net: do not crash if backend is not present" vhost-user: add get_vhost_net() assertions vhost-user: keep vhost_net after a disconnection vhost-user: check vhost_user_{read,write}() return value vhost-user: check qemu_chr_fe_set_msgfds() return value vhost-user: call set_msgfds unconditionally qemu-char: fix qemu_chr_fe_set_msgfds() crash when disconnected vhost: use error_report() instead of fprintf(stderr,...) vhost: add missing VHOST_OPS_DEBUG ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--hw/display/virtio-gpu-pci.c4
-rw-r--r--hw/display/virtio-vga.c4
-rw-r--r--hw/i386/acpi-build.c131
-rw-r--r--hw/net/vhost_net.c34
-rw-r--r--hw/pci-bridge/ioh3420.c1
-rw-r--r--hw/pci-bridge/pci_expander_bridge.c54
-rw-r--r--hw/pci-host/apb.c5
-rw-r--r--hw/pci/pci.c2
-rw-r--r--hw/pci/pcie.c24
-rw-r--r--hw/scsi/mptsas.c2
-rw-r--r--hw/virtio/vhost-user.c67
-rw-r--r--hw/virtio/vhost.c162
-rw-r--r--hw/virtio/virtio-pci.c34
-rw-r--r--hw/virtio/virtio-pci.h21
-rw-r--r--hw/virtio/virtio.c5
-rw-r--r--include/hw/compat.h12
-rw-r--r--include/hw/pci/pci.h3
-rw-r--r--include/hw/virtio/vhost.h4
-rw-r--r--include/sysemu/char.h8
-rw-r--r--net/tap.c1
-rw-r--r--net/vhost-user.c65
-rw-r--r--qemu-char.c82
-rw-r--r--tests/vhost-user-test.c1
-rw-r--r--tests/virtio-net-test.c12
24 files changed, 486 insertions, 252 deletions
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index a71b230d33..34a724c754 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -30,9 +30,7 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
int i;
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- /* force virtio-1.0 */
- vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
- vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ virtio_pci_force_virtio_1(vpci_dev);
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
for (i = 0; i < g->conf.max_outputs; i++) {
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 315b7fc58c..5b510a17fd 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -134,9 +134,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
/* init virtio bits */
qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus));
- /* force virtio-1.0 */
- vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
- vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ virtio_pci_force_virtio_1(vpci_dev);
object_property_set_bool(OBJECT(g), true, "realized", &err);
if (err) {
error_propagate(errp, err);
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 77c40d92e2..a26a4bb03f 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -600,6 +600,10 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
QLIST_FOREACH(sec, &bus->child, sibling) {
int32_t devfn = sec->parent_dev->devfn;
+ if (pci_bus_is_root(sec) || pci_bus_is_express(sec)) {
+ continue;
+ }
+
aml_append(method, aml_name("^S%.02X.PCNT", devfn));
}
}
@@ -747,6 +751,27 @@ static void crs_range_free(gpointer data)
g_free(entry);
}
+typedef struct CrsRangeSet {
+ GPtrArray *io_ranges;
+ GPtrArray *mem_ranges;
+ GPtrArray *mem_64bit_ranges;
+ } CrsRangeSet;
+
+static void crs_range_set_init(CrsRangeSet *range_set)
+{
+ range_set->io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ range_set->mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ range_set->mem_64bit_ranges =
+ g_ptr_array_new_with_free_func(crs_range_free);
+}
+
+static void crs_range_set_free(CrsRangeSet *range_set)
+{
+ g_ptr_array_free(range_set->io_ranges, true);
+ g_ptr_array_free(range_set->mem_ranges, true);
+ g_ptr_array_free(range_set->mem_64bit_ranges, true);
+}
+
static gint crs_range_compare(gconstpointer a, gconstpointer b)
{
CrsRangeEntry *entry_a = *(CrsRangeEntry **)a;
@@ -831,18 +856,17 @@ static void crs_range_merge(GPtrArray *range)
g_ptr_array_free(tmp, true);
}
-static Aml *build_crs(PCIHostState *host,
- GPtrArray *io_ranges, GPtrArray *mem_ranges)
+static Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set)
{
Aml *crs = aml_resource_template();
- GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
- GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ CrsRangeSet temp_range_set;
CrsRangeEntry *entry;
uint8_t max_bus = pci_bus_num(host->bus);
uint8_t type;
int devfn;
int i;
+ crs_range_set_init(&temp_range_set);
for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
uint64_t range_base, range_limit;
PCIDevice *dev = host->bus->devices[devfn];
@@ -866,9 +890,11 @@ static Aml *build_crs(PCIHostState *host,
}
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
- crs_range_insert(host_io_ranges, range_base, range_limit);
+ crs_range_insert(temp_range_set.io_ranges,
+ range_base, range_limit);
} else { /* "memory" */
- crs_range_insert(host_mem_ranges, range_base, range_limit);
+ crs_range_insert(temp_range_set.mem_ranges,
+ range_base, range_limit);
}
}
@@ -887,7 +913,8 @@ static Aml *build_crs(PCIHostState *host,
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
- crs_range_insert(host_io_ranges, range_base, range_limit);
+ crs_range_insert(temp_range_set.io_ranges,
+ range_base, range_limit);
}
range_base =
@@ -900,7 +927,14 @@ static Aml *build_crs(PCIHostState *host,
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
- crs_range_insert(host_mem_ranges, range_base, range_limit);
+ uint64_t length = range_limit - range_base + 1;
+ if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
+ crs_range_insert(temp_range_set.mem_ranges,
+ range_base, range_limit);
+ } else {
+ crs_range_insert(temp_range_set.mem_64bit_ranges,
+ range_base, range_limit);
+ }
}
range_base =
@@ -913,35 +947,55 @@ static Aml *build_crs(PCIHostState *host,
* that do not support multiple root buses
*/
if (range_base && range_base <= range_limit) {
- crs_range_insert(host_mem_ranges, range_base, range_limit);
+ uint64_t length = range_limit - range_base + 1;
+ if (range_limit <= UINT32_MAX && length <= UINT32_MAX) {
+ crs_range_insert(temp_range_set.mem_ranges,
+ range_base, range_limit);
+ } else {
+ crs_range_insert(temp_range_set.mem_64bit_ranges,
+ range_base, range_limit);
+ }
}
}
}
- crs_range_merge(host_io_ranges);
- for (i = 0; i < host_io_ranges->len; i++) {
- entry = g_ptr_array_index(host_io_ranges, i);
+ crs_range_merge(temp_range_set.io_ranges);
+ for (i = 0; i < temp_range_set.io_ranges->len; i++) {
+ entry = g_ptr_array_index(temp_range_set.io_ranges, i);
aml_append(crs,
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
AML_POS_DECODE, AML_ENTIRE_RANGE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
- crs_range_insert(io_ranges, entry->base, entry->limit);
+ crs_range_insert(range_set->io_ranges, entry->base, entry->limit);
}
- g_ptr_array_free(host_io_ranges, true);
- crs_range_merge(host_mem_ranges);
- for (i = 0; i < host_mem_ranges->len; i++) {
- entry = g_ptr_array_index(host_mem_ranges, i);
+ crs_range_merge(temp_range_set.mem_ranges);
+ for (i = 0; i < temp_range_set.mem_ranges->len; i++) {
+ entry = g_ptr_array_index(temp_range_set.mem_ranges, i);
aml_append(crs,
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
AML_MAX_FIXED, AML_NON_CACHEABLE,
AML_READ_WRITE,
0, entry->base, entry->limit, 0,
entry->limit - entry->base + 1));
- crs_range_insert(mem_ranges, entry->base, entry->limit);
+ crs_range_insert(range_set->mem_ranges, entry->base, entry->limit);
}
- g_ptr_array_free(host_mem_ranges, true);
+
+ crs_range_merge(temp_range_set.mem_64bit_ranges);
+ for (i = 0; i < temp_range_set.mem_64bit_ranges->len; i++) {
+ entry = g_ptr_array_index(temp_range_set.mem_64bit_ranges, i);
+ aml_append(crs,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED, AML_NON_CACHEABLE,
+ AML_READ_WRITE,
+ 0, entry->base, entry->limit, 0,
+ entry->limit - entry->base + 1));
+ crs_range_insert(range_set->mem_64bit_ranges,
+ entry->base, entry->limit);
+ }
+
+ crs_range_set_free(&temp_range_set);
aml_append(crs,
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
@@ -1898,8 +1952,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
{
CrsRangeEntry *entry;
Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs;
- GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
- GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
+ CrsRangeSet crs_range_set;
PCMachineState *pcms = PC_MACHINE(machine);
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine);
uint32_t nr_mem = machine->ram_slots;
@@ -1988,6 +2041,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
aml_append(dsdt, scope);
+ crs_range_set_init(&crs_range_set);
bus = PC_MACHINE(machine)->bus;
if (bus) {
QLIST_FOREACH(bus, &bus->child, sibling) {
@@ -2014,8 +2068,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
aml_append(dev, build_prt(false));
- crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent),
- io_ranges, mem_ranges);
+ crs = build_crs(PCI_HOST_BRIDGE(BUS(bus)->parent), &crs_range_set);
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
aml_append(dsdt, scope);
@@ -2036,9 +2089,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
AML_POS_DECODE, AML_ENTIRE_RANGE,
0x0000, 0x0000, 0x0CF7, 0x0000, 0x0CF8));
- crs_replace_with_free_ranges(io_ranges, 0x0D00, 0xFFFF);
- for (i = 0; i < io_ranges->len; i++) {
- entry = g_ptr_array_index(io_ranges, i);
+ crs_replace_with_free_ranges(crs_range_set.io_ranges, 0x0D00, 0xFFFF);
+ for (i = 0; i < crs_range_set.io_ranges->len; i++) {
+ entry = g_ptr_array_index(crs_range_set.io_ranges, i);
aml_append(crs,
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
AML_POS_DECODE, AML_ENTIRE_RANGE,
@@ -2051,11 +2104,11 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
AML_CACHEABLE, AML_READ_WRITE,
0, 0x000A0000, 0x000BFFFF, 0, 0x00020000));
- crs_replace_with_free_ranges(mem_ranges,
+ crs_replace_with_free_ranges(crs_range_set.mem_ranges,
range_lob(pci_hole),
range_upb(pci_hole));
- for (i = 0; i < mem_ranges->len; i++) {
- entry = g_ptr_array_index(mem_ranges, i);
+ for (i = 0; i < crs_range_set.mem_ranges->len; i++) {
+ entry = g_ptr_array_index(crs_range_set.mem_ranges, i);
aml_append(crs,
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
AML_NON_CACHEABLE, AML_READ_WRITE,
@@ -2064,11 +2117,18 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
if (!range_is_empty(pci_hole64)) {
- aml_append(crs,
- aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
- AML_CACHEABLE, AML_READ_WRITE,
- 0, range_lob(pci_hole64), range_upb(pci_hole64), 0,
- range_upb(pci_hole64) + 1 - range_lob(pci_hole64)));
+ crs_replace_with_free_ranges(crs_range_set.mem_64bit_ranges,
+ range_lob(pci_hole64),
+ range_upb(pci_hole64));
+ for (i = 0; i < crs_range_set.mem_64bit_ranges->len; i++) {
+ entry = g_ptr_array_index(crs_range_set.mem_64bit_ranges, i);
+ aml_append(crs,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+ AML_MAX_FIXED,
+ AML_CACHEABLE, AML_READ_WRITE,
+ 0, entry->base, entry->limit,
+ 0, entry->limit - entry->base + 1));
+ }
}
if (misc->tpm_version != TPM_VERSION_UNSPEC) {
@@ -2090,8 +2150,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
- g_ptr_array_free(io_ranges, true);
- g_ptr_array_free(mem_ranges, true);
+ crs_range_set_free(&crs_range_set);
/* reserve PCIHP resources */
if (pm->pcihp_io_len) {
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index f92d3f829e..dc61dc1c5f 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -140,7 +140,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
{
int r;
bool backend_kernel = options->backend_type == VHOST_BACKEND_TYPE_KERNEL;
- struct vhost_net *net = g_malloc(sizeof *net);
+ struct vhost_net *net = g_new0(struct vhost_net, 1);
uint64_t features = 0;
if (!options->net_backend) {
@@ -185,7 +185,6 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
fprintf(stderr, "vhost lacks feature mask %" PRIu64
" for backend\n",
(uint64_t)(~net->dev.features & net->dev.backend_features));
- vhost_dev_cleanup(&net->dev);
goto fail;
}
}
@@ -197,7 +196,6 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
fprintf(stderr, "vhost lacks feature mask %" PRIu64
" for backend\n",
(uint64_t)(~net->dev.features & features));
- vhost_dev_cleanup(&net->dev);
goto fail;
}
}
@@ -205,7 +203,9 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options)
vhost_net_ack_features(net, features);
return net;
+
fail:
+ vhost_dev_cleanup(&net->dev);
g_free(net);
return NULL;
}
@@ -242,8 +242,7 @@ static int vhost_net_start_one(struct vhost_net *net,
qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
file.fd = net->backend;
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
- const VhostOps *vhost_ops = net->dev.vhost_ops;
- r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
+ r = vhost_net_set_backend(&net->dev, &file);
if (r < 0) {
r = -errno;
goto fail;
@@ -255,8 +254,7 @@ fail:
file.fd = -1;
if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
while (file.index-- > 0) {
- const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
+ int r = vhost_net_set_backend(&net->dev, &file);
assert(r >= 0);
}
}
@@ -277,8 +275,7 @@ static void vhost_net_stop_one(struct vhost_net *net,
if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
- const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = vhost_ops->vhost_net_set_backend(&net->dev, &file);
+ int r = vhost_net_set_backend(&net->dev, &file);
assert(r >= 0);
}
}
@@ -313,7 +310,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
* properly.
*/
if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
- dev->use_guest_notifier_mask = false;
+ dev->use_guest_notifier_mask = false;
}
}
@@ -378,19 +375,16 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs,
void vhost_net_cleanup(struct vhost_net *net)
{
vhost_dev_cleanup(&net->dev);
- g_free(net);
}
int vhost_net_notify_migration_done(struct vhost_net *net, char* mac_addr)
{
const VhostOps *vhost_ops = net->dev.vhost_ops;
- int r = -1;
- if (vhost_ops->vhost_migration_done) {
- r = vhost_ops->vhost_migration_done(&net->dev, mac_addr);
- }
+ assert(vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
+ assert(vhost_ops->vhost_migration_done);
- return r;
+ return vhost_ops->vhost_migration_done(&net->dev, mac_addr);
}
bool vhost_net_virtqueue_pending(VHostNetState *net, int idx)
@@ -418,6 +412,7 @@ VHostNetState *get_vhost_net(NetClientState *nc)
break;
case NET_CLIENT_DRIVER_VHOST_USER:
vhost_net = vhost_user_get_vhost_net(nc);
+ assert(vhost_net);
break;
default:
break;
@@ -429,15 +424,10 @@ VHostNetState *get_vhost_net(NetClientState *nc)
int vhost_set_vring_enable(NetClientState *nc, int enable)
{
VHostNetState *net = get_vhost_net(nc);
- const VhostOps *vhost_ops;
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
nc->vring_enable = enable;
- if (!net) {
- return 0;
- }
-
- vhost_ops = net->dev.vhost_ops;
if (vhost_ops->vhost_set_vring_enable) {
return vhost_ops->vhost_set_vring_enable(&net->dev, enable);
}
diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c
index 0444b596c6..c8b5ac4207 100644
--- a/hw/pci-bridge/ioh3420.c
+++ b/hw/pci-bridge/ioh3420.c
@@ -100,6 +100,7 @@ static int ioh3420_initfn(PCIDevice *d)
int rc;
Error *err = NULL;
+ pci_config_set_interrupt_pin(d->config, 1);
pci_bridge_initfn(d, TYPE_PCIE_BUS);
pcie_port_init_reg(d);
diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c
index ab8612158d..1cc598f7e9 100644
--- a/hw/pci-bridge/pci_expander_bridge.c
+++ b/hw/pci-bridge/pci_expander_bridge.c
@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/pci_host.h"
@@ -162,30 +163,25 @@ static const TypeInfo pxb_host_info = {
};
/*
- * Registers the PXB bus as a child of the i440fx root bus.
- *
- * Returns 0 on successs, -1 if i440fx host was not
- * found or the bus number is already in use.
+ * Registers the PXB bus as a child of pci host root bus.
*/
-static int pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus)
+static void pxb_register_bus(PCIDevice *dev, PCIBus *pxb_bus, Error **errp)
{
PCIBus *bus = dev->bus;
int pxb_bus_num = pci_bus_num(pxb_bus);
if (bus->parent_dev) {
- error_report("PXB devices can be attached only to root bus.");
- return -1;
+ error_setg(errp, "PXB devices can be attached only to root bus");
+ return;
}
QLIST_FOREACH(bus, &bus->child, sibling) {
if (pci_bus_num(bus) == pxb_bus_num) {
- error_report("Bus %d is already in use.", pxb_bus_num);
- return -1;
+ error_setg(errp, "Bus %d is already in use", pxb_bus_num);
+ return;
}
}
QLIST_INSERT_HEAD(&dev->bus->child, pxb_bus, sibling);
-
- return 0;
}
static int pxb_map_irq_fn(PCIDevice *pci_dev, int pin)
@@ -215,17 +211,18 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
0;
}
-static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
+static void pxb_dev_realize_common(PCIDevice *dev, bool pcie, Error **errp)
{
PXBDev *pxb = convert_to_pxb(dev);
DeviceState *ds, *bds = NULL;
PCIBus *bus;
const char *dev_name = NULL;
+ Error *local_err = NULL;
if (pxb->numa_node != NUMA_NODE_UNASSIGNED &&
pxb->numa_node >= nb_numa_nodes) {
- error_report("Illegal numa node %d.", pxb->numa_node);
- return -EINVAL;
+ error_setg(errp, "Illegal numa node %d", pxb->numa_node);
+ return;
}
if (dev->qdev.id && *dev->qdev.id) {
@@ -250,7 +247,9 @@ static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
PCI_HOST_BRIDGE(ds)->bus = bus;
- if (pxb_register_bus(dev, bus)) {
+ pxb_register_bus(dev, bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
goto err_register_bus;
}
@@ -264,23 +263,22 @@ static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare);
- return 0;
+ return;
err_register_bus:
object_unref(OBJECT(bds));
object_unparent(OBJECT(bus));
object_unref(OBJECT(ds));
- return -EINVAL;
}
-static int pxb_dev_initfn(PCIDevice *dev)
+static void pxb_dev_realize(PCIDevice *dev, Error **errp)
{
if (pci_bus_is_express(dev->bus)) {
- error_report("pxb devices cannot reside on a PCIe bus!");
- return -EINVAL;
+ error_setg(errp, "pxb devices cannot reside on a PCIe bus");
+ return;
}
- return pxb_dev_init_common(dev, false);
+ pxb_dev_realize_common(dev, false, errp);
}
static void pxb_dev_exitfn(PCIDevice *pci_dev)
@@ -302,7 +300,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = pxb_dev_initfn;
+ k->realize = pxb_dev_realize;
k->exit = pxb_dev_exitfn;
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_PXB;
@@ -310,6 +308,7 @@ static void pxb_dev_class_init(ObjectClass *klass, void *data)
dc->desc = "PCI Expander Bridge";
dc->props = pxb_dev_properties;
+ dc->hotpluggable = false;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
@@ -320,14 +319,14 @@ static const TypeInfo pxb_dev_info = {
.class_init = pxb_dev_class_init,
};
-static int pxb_pcie_dev_initfn(PCIDevice *dev)
+static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp)
{
if (!pci_bus_is_express(dev->bus)) {
- error_report("pxb-pcie devices cannot reside on a PCI bus!");
- return -EINVAL;
+ error_setg(errp, "pxb-pcie devices cannot reside on a PCI bus");
+ return;
}
- return pxb_dev_init_common(dev, true);
+ pxb_dev_realize_common(dev, true, errp);
}
static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
@@ -335,7 +334,7 @@ static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = pxb_pcie_dev_initfn;
+ k->realize = pxb_pcie_dev_realize;
k->exit = pxb_dev_exitfn;
k->vendor_id = PCI_VENDOR_ID_REDHAT;
k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
@@ -343,6 +342,7 @@ static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
dc->desc = "PCI Express Expander Bridge";
dc->props = pxb_dev_properties;
+ dc->hotpluggable = false;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c
index 16587f8373..653e711121 100644
--- a/hw/pci-host/apb.c
+++ b/hw/pci-host/apb.c
@@ -634,7 +634,7 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level)
}
}
-static int apb_pci_bridge_initfn(PCIDevice *dev)
+static void apb_pci_bridge_realize(PCIDevice *dev, Error **errp)
{
pci_bridge_initfn(dev, TYPE_PCI_BUS);
@@ -652,7 +652,6 @@ static int apb_pci_bridge_initfn(PCIDevice *dev)
pci_set_word(dev->config + PCI_STATUS,
PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
PCI_STATUS_DEVSEL_MEDIUM);
- return 0;
}
PCIBus *pci_apb_init(hwaddr special_base,
@@ -843,7 +842,7 @@ static void pbm_pci_bridge_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = apb_pci_bridge_initfn;
+ k->realize = apb_pci_bridge_realize;
k->exit = pci_bridge_exitfn;
k->vendor_id = PCI_VENDOR_ID_SUN;
k->device_id = PCI_DEVICE_ID_SUN_SIMBA;
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 728c6d4b3b..24fae1689d 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -62,6 +62,8 @@ static Property pci_props[] = {
QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false),
DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present,
QEMU_PCI_CAP_SERR_BITNR, true),
+ DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present,
+ QEMU_PCIE_LNKSTA_DLLLA_BITNR, true),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 9599fdef57..99cfb4561b 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -45,8 +45,11 @@
*/
static void
-pcie_cap_v1_fill(uint8_t *exp_cap, uint8_t port, uint8_t type, uint8_t version)
+pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version)
{
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ uint8_t *cmask = dev->cmask + dev->exp.exp_cap;
+
/* capability register
interrupt message number defaults to 0 */
pci_set_word(exp_cap + PCI_EXP_FLAGS,
@@ -69,7 +72,18 @@ pcie_cap_v1_fill(uint8_t *exp_cap, uint8_t port, uint8_t type, uint8_t version)
PCI_EXP_LNK_LS_25);
pci_set_word(exp_cap + PCI_EXP_LNKSTA,
- PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25 |PCI_EXP_LNKSTA_DLLLA);
+ PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
+
+ if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNKSTA_DLLLA);
+ }
+
+ /* We changed link status bits over time, and changing them across
+ * migrations is generally fine as hardware changes them too.
+ * Let's not bother checking.
+ */
+ pci_set_word(cmask + PCI_EXP_LNKSTA, 0);
}
int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
@@ -88,7 +102,7 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
exp_cap = dev->config + pos;
/* Filling values common with v1 */
- pcie_cap_v1_fill(exp_cap, port, type, PCI_EXP_FLAGS_VER2);
+ pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER2);
/* Filling v2 specific values */
pci_set_long(exp_cap + PCI_EXP_DEVCAP2,
@@ -103,7 +117,6 @@ int pcie_cap_v1_init(PCIDevice *dev, uint8_t offset, uint8_t type,
{
/* PCIe cap v1 init */
int pos;
- uint8_t *exp_cap;
assert(pci_is_express(dev));
@@ -112,9 +125,8 @@ int pcie_cap_v1_init(PCIDevice *dev, uint8_t offset, uint8_t type,
return pos;
}
dev->exp.exp_cap = pos;
- exp_cap = dev->config + pos;
- pcie_cap_v1_fill(exp_cap, port, type, PCI_EXP_FLAGS_VER1);
+ pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER1);
return pos;
}
diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
index 1ae32fb7d3..bebe5130fe 100644
--- a/hw/scsi/mptsas.c
+++ b/hw/scsi/mptsas.c
@@ -1370,7 +1370,7 @@ static const VMStateDescription vmstate_mptsas = {
.post_load = mptsas_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, MPTSASState),
-
+ VMSTATE_UNUSED(sizeof(bool)), /* Was msi_in_use */
VMSTATE_UINT32(state, MPTSASState),
VMSTATE_UINT8(who_init, MPTSASState),
VMSTATE_UINT8(doorbell_state, MPTSASState),
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 495e09fd4e..1995fd20bd 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -176,7 +176,7 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
int *fds, int fd_num)
{
CharDriverState *chr = dev->opaque;
- int size = VHOST_USER_HDR_SIZE + msg->size;
+ int ret, size = VHOST_USER_HDR_SIZE + msg->size;
/*
* For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE,
@@ -187,12 +187,19 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg,
return 0;
}
- if (fd_num) {
- qemu_chr_fe_set_msgfds(chr, fds, fd_num);
+ if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) {
+ error_report("Failed to set msg fds.");
+ return -1;
+ }
+
+ ret = qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size);
+ if (ret != size) {
+ error_report("Failed to write msg."
+ " Wrote %d instead of %d.", ret, size);
+ return -1;
}
- return qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size) == size ?
- 0 : -1;
+ return 0;
}
static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
@@ -214,12 +221,14 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
fds[fd_num++] = log->fd;
}
- vhost_user_write(dev, &msg, fds, fd_num);
+ if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ return -1;
+ }
if (shmfd) {
msg.size = 0;
if (vhost_user_read(dev, &msg) < 0) {
- return 0;
+ return -1;
}
if (msg.request != VHOST_USER_SET_LOG_BASE) {
@@ -275,7 +284,9 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
msg.size += sizeof(msg.payload.memory.padding);
msg.size += fd_num * sizeof(VhostUserMemoryRegion);
- vhost_user_write(dev, &msg, fds, fd_num);
+ if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ return -1;
+ }
return 0;
}
@@ -290,7 +301,9 @@ static int vhost_user_set_vring_addr(struct vhost_dev *dev,
.size = sizeof(msg.payload.addr),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
@@ -313,7 +326,9 @@ static int vhost_set_vring(struct vhost_dev *dev,
.size = sizeof(msg.payload.state),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
@@ -360,10 +375,12 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev,
.size = sizeof(msg.payload.state),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
if (vhost_user_read(dev, &msg) < 0) {
- return 0;
+ return -1;
}
if (msg.request != VHOST_USER_GET_VRING_BASE) {
@@ -401,7 +418,9 @@ static int vhost_set_vring_file(struct vhost_dev *dev,
msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK;
}
- vhost_user_write(dev, &msg, fds, fd_num);
+ if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
+ return -1;
+ }
return 0;
}
@@ -427,7 +446,9 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64)
.size = sizeof(msg.payload.u64),
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
@@ -455,10 +476,12 @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64)
return 0;
}
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
if (vhost_user_read(dev, &msg) < 0) {
- return 0;
+ return -1;
}
if (msg.request != request) {
@@ -489,7 +512,9 @@ static int vhost_user_set_owner(struct vhost_dev *dev)
.flags = VHOST_USER_VERSION,
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
@@ -501,7 +526,9 @@ static int vhost_user_reset_device(struct vhost_dev *dev)
.flags = VHOST_USER_VERSION,
};
- vhost_user_write(dev, &msg, NULL, 0);
+ if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
+ return -1;
+ }
return 0;
}
@@ -588,7 +615,6 @@ static bool vhost_user_requires_shm_log(struct vhost_dev *dev)
static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
{
VhostUserMsg msg = { 0 };
- int err;
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
@@ -605,8 +631,7 @@ static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
memcpy((char *)&msg.payload.u64, mac_addr, 6);
msg.size = sizeof(msg.payload.u64);
- err = vhost_user_write(dev, &msg, NULL, 0);
- return err;
+ return vhost_user_write(dev, &msg, NULL, 0);
}
return -1;
}
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index ec3abda9d5..3d0c807d0e 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -27,6 +27,18 @@
#include "hw/virtio/virtio-access.h"
#include "migration/migration.h"
+/* enabled until disconnected backend stabilizes */
+#define _VHOST_DEBUG 1
+
+#ifdef _VHOST_DEBUG
+#define VHOST_OPS_DEBUG(fmt, ...) \
+ do { error_report(fmt ": %s (%d)", ## __VA_ARGS__, \
+ strerror(errno), errno); } while (0)
+#else
+#define VHOST_OPS_DEBUG(fmt, ...) \
+ do { } while (0)
+#endif
+
static struct vhost_log *vhost_log;
static struct vhost_log *vhost_log_shm;
@@ -362,6 +374,8 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync)
if (!log) {
return;
}
+ dev->log = NULL;
+ dev->log_size = 0;
--log->refcnt;
if (log->refcnt == 0) {
@@ -398,7 +412,10 @@ static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size)
/* inform backend of log switching, this must be done before
releasing the current log, to ensure no logging is lost */
r = dev->vhost_ops->vhost_set_log_base(dev, log_base, log);
- assert(r >= 0);
+ if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_log_base failed");
+ }
+
vhost_log_put(dev, true);
dev->log = log;
dev->log_size = size;
@@ -422,11 +439,11 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev,
l = vq->ring_size;
p = cpu_physical_memory_map(vq->ring_phys, &l, 1);
if (!p || l != vq->ring_size) {
- fprintf(stderr, "Unable to map ring buffer for ring %d\n", i);
+ error_report("Unable to map ring buffer for ring %d", i);
r = -ENOMEM;
}
if (p != vq->ring) {
- fprintf(stderr, "Ring buffer relocated for ring %d\n", i);
+ error_report("Ring buffer relocated for ring %d", i);
r = -EBUSY;
}
cpu_physical_memory_unmap(p, l, 0, 0);
@@ -565,7 +582,9 @@ static void vhost_commit(MemoryListener *listener)
if (!dev->log_enabled) {
r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
- assert(r >= 0);
+ if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_mem_table failed");
+ }
dev->memory_changed = false;
return;
}
@@ -578,7 +597,9 @@ static void vhost_commit(MemoryListener *listener)
vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER);
}
r = dev->vhost_ops->vhost_set_mem_table(dev, dev->mem);
- assert(r >= 0);
+ if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_mem_table failed");
+ }
/* To log less, can only decrease log size after table update. */
if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
vhost_dev_log_resize(dev, log_size);
@@ -647,6 +668,7 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
};
int r = dev->vhost_ops->vhost_set_vring_addr(dev, &addr);
if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_vring_addr failed");
return -errno;
}
return 0;
@@ -660,12 +682,15 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log)
features |= 0x1ULL << VHOST_F_LOG_ALL;
}
r = dev->vhost_ops->vhost_set_features(dev, features);
+ if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_features failed");
+ }
return r < 0 ? -errno : 0;
}
static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
{
- int r, t, i, idx;
+ int r, i, idx;
r = vhost_dev_set_features(dev, enable_log);
if (r < 0) {
goto err_features;
@@ -682,12 +707,10 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
err_vq:
for (; i >= 0; --i) {
idx = dev->vhost_ops->vhost_get_vq_index(dev, dev->vq_index + i);
- t = vhost_virtqueue_set_addr(dev, dev->vqs + i, idx,
- dev->log_enabled);
- assert(t >= 0);
+ vhost_virtqueue_set_addr(dev, dev->vqs + i, idx,
+ dev->log_enabled);
}
- t = vhost_dev_set_features(dev, dev->log_enabled);
- assert(t >= 0);
+ vhost_dev_set_features(dev, dev->log_enabled);
err_features:
return r;
}
@@ -710,8 +733,6 @@ static int vhost_migration_log(MemoryListener *listener, int enable)
return r;
}
vhost_log_put(dev, false);
- dev->log = NULL;
- dev->log_size = 0;
} else {
vhost_dev_log_resize(dev, vhost_get_log_size(dev));
r = vhost_dev_set_log(dev, true);
@@ -787,6 +808,7 @@ static int vhost_virtqueue_set_vring_endian_legacy(struct vhost_dev *dev,
return 0;
}
+ VHOST_OPS_DEBUG("vhost_set_vring_endian failed");
if (errno == ENOTTY) {
error_report("vhost does not support cross-endian");
return -ENOSYS;
@@ -815,12 +837,14 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
vq->num = state.num = virtio_queue_get_num(vdev, idx);
r = dev->vhost_ops->vhost_set_vring_num(dev, &state);
if (r) {
+ VHOST_OPS_DEBUG("vhost_set_vring_num failed");
return -errno;
}
state.num = virtio_queue_get_last_avail_idx(vdev, idx);
r = dev->vhost_ops->vhost_set_vring_base(dev, &state);
if (r) {
+ VHOST_OPS_DEBUG("vhost_set_vring_base failed");
return -errno;
}
@@ -872,6 +896,7 @@ static int vhost_virtqueue_start(struct vhost_dev *dev,
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
r = dev->vhost_ops->vhost_set_vring_kick(dev, &file);
if (r) {
+ VHOST_OPS_DEBUG("vhost_set_vring_kick failed");
r = -errno;
goto fail_kick;
}
@@ -919,25 +944,21 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev,
r = dev->vhost_ops->vhost_get_vring_base(dev, &state);
if (r < 0) {
- fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
- fflush(stderr);
+ VHOST_OPS_DEBUG("vhost VQ %d ring restore failed: %d", idx, r);
+ } else {
+ virtio_queue_set_last_avail_idx(vdev, idx, state.num);
}
- virtio_queue_set_last_avail_idx(vdev, idx, state.num);
virtio_queue_invalidate_signalled_used(vdev, idx);
/* In the cross-endian case, we need to reset the vring endianness to
* native as legacy devices expect so by default.
*/
if (vhost_needs_vring_endian(vdev)) {
- r = vhost_virtqueue_set_vring_endian_legacy(dev,
- !virtio_is_big_endian(vdev),
- vhost_vq_index);
- if (r < 0) {
- error_report("failed to reset vring endianness");
- }
+ vhost_virtqueue_set_vring_endian_legacy(dev,
+ !virtio_is_big_endian(vdev),
+ vhost_vq_index);
}
- assert (r >= 0);
cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
0, virtio_queue_get_ring_size(vdev, idx));
cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx),
@@ -976,6 +997,7 @@ static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev,
r = dev->vhost_ops->vhost_set_vring_busyloop_timeout(dev, &state);
if (r) {
+ VHOST_OPS_DEBUG("vhost_set_vring_busyloop_timeout failed");
return r;
}
@@ -997,6 +1019,7 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
file.fd = event_notifier_get_fd(&vq->masked_notifier);
r = dev->vhost_ops->vhost_set_vring_call(dev, &file);
if (r) {
+ VHOST_OPS_DEBUG("vhost_set_vring_call failed");
r = -errno;
goto fail_call;
}
@@ -1015,42 +1038,41 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
VhostBackendType backend_type, uint32_t busyloop_timeout)
{
uint64_t features;
- int i, r;
+ int i, r, n_initialized_vqs = 0;
hdev->migration_blocker = NULL;
- if (vhost_set_backend_type(hdev, backend_type) < 0) {
- close((uintptr_t)opaque);
- return -1;
- }
+ r = vhost_set_backend_type(hdev, backend_type);
+ assert(r >= 0);
- if (hdev->vhost_ops->vhost_backend_init(hdev, opaque) < 0) {
- close((uintptr_t)opaque);
- return -errno;
+ r = hdev->vhost_ops->vhost_backend_init(hdev, opaque);
+ if (r < 0) {
+ goto fail;
}
if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) {
- fprintf(stderr, "vhost backend memory slots limit is less"
- " than current number of present memory slots\n");
- close((uintptr_t)opaque);
- return -1;
+ error_report("vhost backend memory slots limit is less"
+ " than current number of present memory slots");
+ r = -1;
+ goto fail;
}
- QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
r = hdev->vhost_ops->vhost_set_owner(hdev);
if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_owner failed");
goto fail;
}
r = hdev->vhost_ops->vhost_get_features(hdev, &features);
if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_get_features failed");
goto fail;
}
- for (i = 0; i < hdev->nvqs; ++i) {
+ for (i = 0; i < hdev->nvqs; ++i, ++n_initialized_vqs) {
r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i);
if (r < 0) {
- goto fail_vq;
+ goto fail;
}
}
@@ -1105,38 +1127,43 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
hdev->started = false;
hdev->memory_changed = false;
memory_listener_register(&hdev->memory_listener, &address_space_memory);
+ QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
return 0;
+
fail_busyloop:
while (--i >= 0) {
vhost_virtqueue_set_busyloop_timeout(hdev, hdev->vq_index + i, 0);
}
- i = hdev->nvqs;
-fail_vq:
- while (--i >= 0) {
- vhost_virtqueue_cleanup(hdev->vqs + i);
- }
fail:
- r = -errno;
- hdev->vhost_ops->vhost_backend_cleanup(hdev);
- QLIST_REMOVE(hdev, entry);
+ hdev->nvqs = n_initialized_vqs;
+ vhost_dev_cleanup(hdev);
return r;
}
void vhost_dev_cleanup(struct vhost_dev *hdev)
{
int i;
+
for (i = 0; i < hdev->nvqs; ++i) {
vhost_virtqueue_cleanup(hdev->vqs + i);
}
- memory_listener_unregister(&hdev->memory_listener);
+ if (hdev->mem) {
+ /* those are only safe after successful init */
+ memory_listener_unregister(&hdev->memory_listener);
+ QLIST_REMOVE(hdev, entry);
+ }
if (hdev->migration_blocker) {
migrate_del_blocker(hdev->migration_blocker);
error_free(hdev->migration_blocker);
}
g_free(hdev->mem);
g_free(hdev->mem_sections);
- hdev->vhost_ops->vhost_backend_cleanup(hdev);
- QLIST_REMOVE(hdev, entry);
+ if (hdev->vhost_ops) {
+ hdev->vhost_ops->vhost_backend_cleanup(hdev);
+ }
+ assert(!hdev->log);
+
+ memset(hdev, 0, sizeof(struct vhost_dev));
}
/* Stop processing guest IO notifications in qemu.
@@ -1148,8 +1175,9 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
int i, r, e;
+
if (!k->ioeventfd_started) {
- fprintf(stderr, "binding does not support host notifiers\n");
+ error_report("binding does not support host notifiers");
r = -ENOSYS;
goto fail;
}
@@ -1158,7 +1186,7 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
true);
if (r < 0) {
- fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r);
+ error_report("vhost VQ %d notifier binding failed: %d", i, -r);
goto fail_vq;
}
}
@@ -1169,8 +1197,7 @@ fail_vq:
e = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
false);
if (e < 0) {
- fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r);
- fflush(stderr);
+ error_report("vhost VQ %d notifier cleanup error: %d", i, -r);
}
assert (e >= 0);
}
@@ -1192,8 +1219,7 @@ void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i,
false);
if (r < 0) {
- fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r);
- fflush(stderr);
+ error_report("vhost VQ %d notifier cleanup failed: %d", i, -r);
}
assert (r >= 0);
}
@@ -1217,6 +1243,9 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
int r, index = n - hdev->vq_index;
struct vhost_vring_file file;
+ /* should only be called after backend is connected */
+ assert(hdev->vhost_ops);
+
if (mask) {
assert(vdev->use_guest_notifier_mask);
file.fd = event_notifier_get_fd(&hdev->vqs[index].masked_notifier);
@@ -1226,7 +1255,9 @@ void vhost_virtqueue_mask(struct vhost_dev *hdev, VirtIODevice *vdev, int n,
file.index = hdev->vhost_ops->vhost_get_vq_index(hdev, n);
r = hdev->vhost_ops->vhost_set_vring_call(hdev, &file);
- assert(r >= 0);
+ if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_vring_call failed");
+ }
}
uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
@@ -1261,6 +1292,9 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
{
int i, r;
+ /* should only be called after backend is connected */
+ assert(hdev->vhost_ops);
+
hdev->started = true;
r = vhost_dev_set_features(hdev, hdev->log_enabled);
@@ -1269,6 +1303,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
}
r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem);
if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_mem_table failed");
r = -errno;
goto fail_mem;
}
@@ -1293,6 +1328,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
hdev->log_size ? log_base : 0,
hdev->log);
if (r < 0) {
+ VHOST_OPS_DEBUG("vhost_set_log_base failed");
r = -errno;
goto fail_log;
}
@@ -1321,6 +1357,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
{
int i;
+ /* should only be called after backend is connected */
+ assert(hdev->vhost_ops);
+
for (i = 0; i < hdev->nvqs; ++i) {
vhost_virtqueue_stop(hdev,
vdev,
@@ -1330,7 +1369,14 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
vhost_log_put(hdev, true);
hdev->started = false;
- hdev->log = NULL;
- hdev->log_size = 0;
}
+int vhost_net_set_backend(struct vhost_dev *hdev,
+ struct vhost_vring_file *file)
+{
+ if (hdev->vhost_ops->vhost_net_set_backend) {
+ return hdev->vhost_ops->vhost_net_set_backend(hdev, file);
+ }
+
+ return -1;
+}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index f0677b73d8..755f9218b7 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -161,7 +161,7 @@ static bool virtio_pci_modern_state_needed(void *opaque)
{
VirtIOPCIProxy *proxy = opaque;
- return !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ return virtio_pci_modern(proxy);
}
static const VMStateDescription vmstate_virtio_pci_modern_state = {
@@ -300,8 +300,8 @@ static int virtio_pci_ioeventfd_assign(DeviceState *d, EventNotifier *notifier,
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
VirtQueue *vq = virtio_get_queue(vdev, n);
- bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
- bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool legacy = virtio_pci_legacy(proxy);
+ bool modern = virtio_pci_modern(proxy);
bool fast_mmio = kvm_ioeventfd_any_length_enabled();
bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
MemoryRegion *modern_mr = &proxy->notify.mr;
@@ -1574,8 +1574,8 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
VirtioBusState *bus = &proxy->bus;
- bool legacy = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_LEGACY);
- bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool legacy = virtio_pci_legacy(proxy);
+ bool modern = virtio_pci_modern(proxy);
bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
uint8_t *config;
uint32_t size;
@@ -1694,7 +1694,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
static void virtio_pci_device_unplugged(DeviceState *d)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
- bool modern = !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN);
+ bool modern = virtio_pci_modern(proxy);
bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
virtio_pci_stop_ioeventfd(proxy);
@@ -1714,6 +1714,8 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
{
VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
+ bool pcie_port = pci_bus_is_express(pci_dev->bus) &&
+ !pci_bus_is_root(pci_dev->bus);
/*
* virtio pci bar layout used by default.
@@ -1764,8 +1766,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as");
- if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus) &&
- !pci_bus_is_root(pci_dev->bus)) {
+ if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) {
+ proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
+ }
+
+ if (pcie_port && pci_is_express(pci_dev)) {
int pos;
pos = pcie_endpoint_cap_init(pci_dev, 0);
@@ -1819,10 +1824,9 @@ static void virtio_pci_reset(DeviceState *qdev)
static Property virtio_pci_properties[] = {
DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false),
- DEFINE_PROP_BIT("disable-legacy", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT, false),
- DEFINE_PROP_BIT("disable-modern", VirtIOPCIProxy, flags,
- VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT, true),
+ DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy,
+ ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false),
DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags,
VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true),
DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags,
@@ -1839,7 +1843,7 @@ static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
PCIDevice *pci_dev = &proxy->pci_dev;
if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&
- !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)) {
+ virtio_pci_modern(proxy)) {
pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
}
@@ -2301,9 +2305,7 @@ static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
DeviceState *vdev = DEVICE(&vinput->vdev);
qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
- /* force virtio-1.0 */
- vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
- vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ virtio_pci_force_virtio_1(vpci_dev);
object_property_set_bool(OBJECT(vdev), true, "realized", errp);
}
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index e4548c2f97..25fbf8a375 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -61,8 +61,6 @@ typedef struct VirtioBusClass VirtioPCIBusClass;
enum {
VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT,
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT,
- VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT,
- VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT,
VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT,
VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT,
VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT,
@@ -77,8 +75,6 @@ enum {
#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT)
/* virtio version flags */
-#define VIRTIO_PCI_FLAG_DISABLE_LEGACY (1 << VIRTIO_PCI_FLAG_DISABLE_LEGACY_BIT)
-#define VIRTIO_PCI_FLAG_DISABLE_MODERN (1 << VIRTIO_PCI_FLAG_DISABLE_MODERN_BIT)
#define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT)
/* migrate extra state */
@@ -144,6 +140,8 @@ struct VirtIOPCIProxy {
uint32_t modern_mem_bar;
int config_cap;
uint32_t flags;
+ bool disable_modern;
+ OnOffAuto disable_legacy;
uint32_t class_code;
uint32_t nvectors;
uint32_t dfselect;
@@ -158,6 +156,21 @@ struct VirtIOPCIProxy {
VirtioBusState bus;
};
+static inline bool virtio_pci_modern(VirtIOPCIProxy *proxy)
+{
+ return !proxy->disable_modern;
+}
+
+static inline bool virtio_pci_legacy(VirtIOPCIProxy *proxy)
+{
+ return proxy->disable_legacy == ON_OFF_AUTO_OFF;
+}
+
+static inline void virtio_pci_force_virtio_1(VirtIOPCIProxy *proxy)
+{
+ proxy->disable_modern = false;
+ proxy->disable_legacy = ON_OFF_AUTO_ON;
+}
/*
* virtio-scsi-pci: This extends VirtioPCIProxy.
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 28cf504d6a..15ee3a71fa 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -458,6 +458,11 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove
unsigned num_sg = *p_num_sg;
assert(num_sg <= max_num_sg);
+ if (!sz) {
+ error_report("virtio: zero sized buffers are not allowed");
+ exit(1);
+ }
+
while (sz) {
hwaddr len = sz;
diff --git a/include/hw/compat.h b/include/hw/compat.h
index 9914e7a59e..7ee7299c36 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -6,6 +6,14 @@
.driver = "virtio-mmio",\
.property = "format_transport_address",\
.value = "off",\
+ },{\
+ .driver = "virtio-pci",\
+ .property = "disable-modern",\
+ .value = "on",\
+ },{\
+ .driver = "virtio-pci",\
+ .property = "disable-legacy",\
+ .value = "off",\
},
#define HW_COMPAT_2_5 \
@@ -80,6 +88,10 @@
.driver = "virtio-rng-pci",\
.property = "any_layout",\
.value = "off",\
+ },{\
+ .driver = TYPE_PCI_DEVICE,\
+ .property = "x-pcie-lnksta-dllla",\
+ .value = "off",\
},
#define HW_COMPAT_2_2 \
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 74d797d1cf..929ec2fb07 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -174,6 +174,9 @@ enum {
/* PCI Express capability - Power Controller Present */
#define QEMU_PCIE_SLTCAP_PCP_BITNR 7
QEMU_PCIE_SLTCAP_PCP = (1 << QEMU_PCIE_SLTCAP_PCP_BITNR),
+ /* Link active status in endpoint capability is always set */
+#define QEMU_PCIE_LNKSTA_DLLLA_BITNR 8
+ QEMU_PCIE_LNKSTA_DLLLA = (1 << QEMU_PCIE_LNKSTA_DLLLA_BITNR),
};
#define TYPE_PCI_DEVICE "pci-device"
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 2106ed8c0d..e433089ea9 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -86,4 +86,8 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
uint64_t features);
bool vhost_has_free_slot(void);
+
+int vhost_net_set_backend(struct vhost_dev *hdev,
+ struct vhost_vring_file *file);
+
#endif
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0ea9eacc40..ee7e55468f 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -70,6 +70,7 @@ struct CharDriverState {
int (*get_msgfds)(struct CharDriverState *s, int* fds, int num);
int (*set_msgfds)(struct CharDriverState *s, int *fds, int num);
int (*chr_add_client)(struct CharDriverState *chr, int fd);
+ int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp);
IOEventHandler *chr_event;
IOCanReadHandler *chr_can_read;
IOReadHandler *chr_read;
@@ -159,6 +160,13 @@ void qemu_chr_disconnect(CharDriverState *chr);
void qemu_chr_cleanup(void);
/**
+ * @qemu_chr_wait_connected:
+ *
+ * Wait for characted backend to be connected.
+ */
+int qemu_chr_wait_connected(CharDriverState *chr, Error **errp);
+
+/**
* @qemu_chr_new_noreplay:
*
* Create a new character backend from a URI.
diff --git a/net/tap.c b/net/tap.c
index 40a8c741fc..6abb962efd 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -312,6 +312,7 @@ static void tap_cleanup(NetClientState *nc)
if (s->vhost_net) {
vhost_net_cleanup(s->vhost_net);
+ g_free(s->vhost_net);
s->vhost_net = NULL;
}
diff --git a/net/vhost-user.c b/net/vhost-user.c
index c4d63e05eb..b0595f8781 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -24,6 +24,7 @@ typedef struct VhostUserState {
VHostNetState *vhost_net;
guint watch;
uint64_t acked_features;
+ bool started;
} VhostUserState;
typedef struct VhostUserChardevProps {
@@ -45,11 +46,6 @@ uint64_t vhost_user_get_acked_features(NetClientState *nc)
return s->acked_features;
}
-static int vhost_user_running(VhostUserState *s)
-{
- return (s->vhost_net) ? 1 : 0;
-}
-
static void vhost_user_stop(int queues, NetClientState *ncs[])
{
VhostUserState *s;
@@ -59,15 +55,14 @@ static void vhost_user_stop(int queues, NetClientState *ncs[])
assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
s = DO_UPCAST(VhostUserState, nc, ncs[i]);
- if (!vhost_user_running(s)) {
- continue;
- }
if (s->vhost_net) {
/* save acked features */
- s->acked_features = vhost_net_get_acked_features(s->vhost_net);
+ uint64_t features = vhost_net_get_acked_features(s->vhost_net);
+ if (features) {
+ s->acked_features = features;
+ }
vhost_net_cleanup(s->vhost_net);
- s->vhost_net = NULL;
}
}
}
@@ -75,6 +70,7 @@ static void vhost_user_stop(int queues, NetClientState *ncs[])
static int vhost_user_start(int queues, NetClientState *ncs[])
{
VhostNetOptions options;
+ struct vhost_net *net = NULL;
VhostUserState *s;
int max_queues;
int i;
@@ -85,33 +81,39 @@ static int vhost_user_start(int queues, NetClientState *ncs[])
assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER);
s = DO_UPCAST(VhostUserState, nc, ncs[i]);
- if (vhost_user_running(s)) {
- continue;
- }
options.net_backend = ncs[i];
options.opaque = s->chr;
options.busyloop_timeout = 0;
- s->vhost_net = vhost_net_init(&options);
- if (!s->vhost_net) {
+ net = vhost_net_init(&options);
+ if (!net) {
error_report("failed to init vhost_net for queue %d", i);
goto err;
}
if (i == 0) {
- max_queues = vhost_net_get_max_queues(s->vhost_net);
+ max_queues = vhost_net_get_max_queues(net);
if (queues > max_queues) {
error_report("you are asking more queues than supported: %d",
max_queues);
goto err;
}
}
+
+ if (s->vhost_net) {
+ vhost_net_cleanup(s->vhost_net);
+ g_free(s->vhost_net);
+ }
+ s->vhost_net = net;
}
return 0;
err:
- vhost_user_stop(i + 1, ncs);
+ if (net) {
+ vhost_net_cleanup(net);
+ }
+ vhost_user_stop(i, ncs);
return -1;
}
@@ -150,6 +152,7 @@ static void vhost_user_cleanup(NetClientState *nc)
if (s->vhost_net) {
vhost_net_cleanup(s->vhost_net);
+ g_free(s->vhost_net);
s->vhost_net = NULL;
}
if (s->chr) {
@@ -188,12 +191,8 @@ static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond,
void *opaque)
{
VhostUserState *s = opaque;
- uint8_t buf[1];
- /* We don't actually want to read anything, but CHR_EVENT_CLOSED will be
- * raised as a side-effect of the read.
- */
- qemu_chr_fe_read_all(s->chr, buf, sizeof(buf));
+ qemu_chr_disconnect(s->chr);
return FALSE;
}
@@ -222,6 +221,7 @@ static void net_vhost_user_event(void *opaque, int event)
return;
}
qmp_set_link(name, true, &err);
+ s->started = true;
break;
case CHR_EVENT_CLOSED:
qmp_set_link(name, false, &err);
@@ -240,7 +240,7 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
const char *name, CharDriverState *chr,
int queues)
{
- NetClientState *nc;
+ NetClientState *nc, *nc0 = NULL;
VhostUserState *s;
int i;
@@ -249,6 +249,9 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
for (i = 0; i < queues; i++) {
nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name);
+ if (!nc0) {
+ nc0 = nc;
+ }
snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s",
i, chr->label);
@@ -259,7 +262,18 @@ static int net_vhost_user_init(NetClientState *peer, const char *device,
s->chr = chr;
}
- qemu_chr_add_handlers(chr, NULL, NULL, net_vhost_user_event, nc[0].name);
+ s = DO_UPCAST(VhostUserState, nc, nc0);
+ do {
+ Error *err = NULL;
+ if (qemu_chr_wait_connected(chr, &err) < 0) {
+ error_report_err(err);
+ return -1;
+ }
+ qemu_chr_add_handlers(chr, NULL, NULL,
+ net_vhost_user_event, nc0->name);
+ } while (!s->started);
+
+ assert(s->vhost_net);
return 0;
}
@@ -316,7 +330,6 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
{
const char *name = opaque;
const char *driver, *netdev;
- const char virtio_name[] = "virtio-net-";
driver = qemu_opt_get(opts, "driver");
netdev = qemu_opt_get(opts, "netdev");
@@ -326,7 +339,7 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
}
if (strcmp(netdev, name) == 0 &&
- strncmp(driver, virtio_name, strlen(virtio_name)) != 0) {
+ !g_str_has_prefix(driver, "virtio-net-")) {
error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
return -1;
}
diff --git a/qemu-char.c b/qemu-char.c
index e4b8448422..a50b8fb3a3 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2760,14 +2760,16 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
{
TCPCharDriver *s = chr->opaque;
- if (!qio_channel_has_feature(s->ioc,
- QIO_CHANNEL_FEATURE_FD_PASS)) {
- return -1;
- }
/* clear old pending fd array */
g_free(s->write_msgfds);
s->write_msgfds = NULL;
+ if (!s->connected ||
+ !qio_channel_has_feature(s->ioc,
+ QIO_CHANNEL_FEATURE_FD_PASS)) {
+ return -1;
+ }
+
if (num) {
s->write_msgfds = g_new(int, num);
memcpy(s->write_msgfds, fds, num * sizeof(int));
@@ -3137,6 +3139,41 @@ static gboolean tcp_chr_accept(QIOChannel *channel,
return TRUE;
}
+static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
+{
+ TCPCharDriver *s = chr->opaque;
+ QIOChannelSocket *sioc;
+
+ while (!s->connected) {
+ if (s->is_listen) {
+ fprintf(stderr, "QEMU waiting for connection on: %s\n",
+ chr->filename);
+ qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL);
+ tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
+ qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
+ } else {
+ sioc = qio_channel_socket_new();
+ if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
+ object_unref(OBJECT(sioc));
+ return -1;
+ }
+ tcp_chr_new_client(chr, sioc);
+ object_unref(OBJECT(sioc));
+ }
+ }
+
+ return 0;
+}
+
+int qemu_chr_wait_connected(CharDriverState *chr, Error **errp)
+{
+ if (chr->chr_wait_connected) {
+ return chr->chr_wait_connected(chr, errp);
+ }
+
+ return 0;
+}
+
static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
@@ -4391,6 +4428,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
s->addr = QAPI_CLONE(SocketAddress, sock->addr);
chr->opaque = s;
+ chr->chr_wait_connected = tcp_chr_wait_connected;
chr->chr_write = tcp_chr_write;
chr->chr_sync_read = tcp_chr_sync_read;
chr->chr_close = tcp_chr_close;
@@ -4414,32 +4452,30 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
s->reconnect_time = reconnect;
}
- sioc = qio_channel_socket_new();
if (s->reconnect_time) {
+ sioc = qio_channel_socket_new();
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL);
- } else if (s->is_listen) {
- if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
- goto error;
- }
- s->listen_ioc = sioc;
- if (is_waitconnect) {
- fprintf(stderr, "QEMU waiting for connection on: %s\n",
- chr->filename);
- tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
- }
- qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
- if (!s->ioc) {
- s->listen_tag = qio_channel_add_watch(
- QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
- }
} else {
- if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
+ if (s->is_listen) {
+ sioc = qio_channel_socket_new();
+ if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
+ goto error;
+ }
+ s->listen_ioc = sioc;
+ if (is_waitconnect &&
+ qemu_chr_wait_connected(chr, errp) < 0) {
+ goto error;
+ }
+ if (!s->ioc) {
+ s->listen_tag = qio_channel_add_watch(
+ QIO_CHANNEL(s->listen_ioc), G_IO_IN,
+ tcp_chr_accept, chr, NULL);
+ }
+ } else if (qemu_chr_wait_connected(chr, errp) < 0) {
goto error;
}
- tcp_chr_new_client(chr, sioc);
- object_unref(OBJECT(sioc));
}
return chr;
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 46d05881b6..27b10c19b8 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -683,6 +683,7 @@ static void test_reconnect(void)
qtest_get_arch());
g_test_trap_subprocess(path, 0, 0);
g_test_trap_assert_passed();
+ g_free(path);
}
#endif
diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c
index a34a9392e3..361506faf5 100644
--- a/tests/virtio-net-test.c
+++ b/tests/virtio-net-test.c
@@ -149,6 +149,7 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
char test[] = "TEST";
char buffer[64];
int len = htonl(sizeof(test));
+ QDict *rsp;
struct iovec iov[] = {
{
.iov_base = &len,
@@ -165,7 +166,8 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
free_head = qvirtqueue_add(vq, req_addr, 64, true, false);
qvirtqueue_kick(bus, dev, vq, free_head);
- qmp("{ 'execute' : 'stop'}");
+ rsp = qmp("{ 'execute' : 'stop'}");
+ QDECREF(rsp);
ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
@@ -173,8 +175,10 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
/* We could check the status, but this command is more importantly to
* ensure the packet data gets queued in QEMU, before we do 'cont'.
*/
- qmp("{ 'execute' : 'query-status'}");
- qmp("{ 'execute' : 'cont'}");
+ rsp = qmp("{ 'execute' : 'query-status'}");
+ QDECREF(rsp);
+ rsp = qmp("{ 'execute' : 'cont'}");
+ QDECREF(rsp);
qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US);
memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
@@ -230,8 +234,10 @@ static void pci_basic(gconstpointer data)
/* End test */
close(sv[0]);
qvirtqueue_cleanup(&qvirtio_pci, &tx->vq, alloc);
+ qvirtqueue_cleanup(&qvirtio_pci, &rx->vq, alloc);
pc_alloc_uninit(alloc);
qvirtio_pci_device_disable(dev);
+ g_free(dev->pdev);
g_free(dev);
qpci_free_pc(bus);
test_end();