qdev: Add JSON -device
- Add a JSON mode to the -device command line option - net/vhost-{user,vdpa}: Fix device compatibility check - Minor iotests fixes -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmFpkxIRHGt3b2xmQHJl ZGhhdC5jb20ACgkQfwmycsiPL9awKA//YWdkh/81lTczk+l7zwRI5YS4efbOvMxc LoS6tUV4gh0mAEBxw/ZmSwMMnGWcZkjIGnhROSeLPDtzi0YN2gwaVYJ2RMcICTyD 9LAIMRfR5tUu63qwXXH9VpfhkS2hJt/uq9+oQnD1okRRxBTIcRBi8vIj6X1EPii/ w0cDWsMlq4m8R6+cVcgapmXCej7zSXaHPyyat0JiNstnREW/QQkR8rZRbJKYQc03 6nfsznB2wdAWCT02dchhtaZK/0IN3EZ5kin8kn/luxjoAPV6UimvtJmaqZ0zl86j dwXGJbAbnUnXhkBludGYYKiJDtKh2w1Zfltsoac5diigaSnKik2bavmnOn8N95NE U8+kxXFXBxJiHGn7PgWYtkmiRqdY46/t6r0U/bQAa3QpCLRtdqpddC5dBw92iPml aX0q7LVIGmesbDgMEVjgdwCYGxUOefEL05THeJSmZ82cZS0YvAzdQhN0nL7L2yX/ t9jscqLiUnXS6ierqvLMH+Y5P7o5x07mR1DRgQr2JYaOz5lBMTASX/DmehfJcLkO SmsgBtlzg4Y1BMCC07q2LkUvPHMA4PillAWxeW4TPWAr19geEafzhjoWJyIktZ3h 9ND6A4W6OoOL/fjr/v0J2z6svBcXwya/W8rCpyeMndEatRKINPzbNE2iTi1zE8Ki fMhJHwsjus0= =1WQc -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kwolf/tags/for-upstream' into staging qdev: Add JSON -device - Add a JSON mode to the -device command line option - net/vhost-{user,vdpa}: Fix device compatibility check - Minor iotests fixes # gpg: Signature made Fri 15 Oct 2021 07:41:22 AM PDT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] * remotes/kwolf/tags/for-upstream: vl: Enable JSON syntax for -device qdev: Base object creation on QDict rather than QemuOpts virtio-net: Avoid QemuOpts in failover_find_primary_device() virtio-net: Store failover primary opts pointer locally qdev: Add Error parameter to hide_device() callbacks qemu-option: Allow deleting opts during qemu_opts_foreach() softmmu/qdev-monitor: add error handling in qdev_set_id qdev: Make DeviceState.id independent of QemuOpts qdev: Avoid using string visitor for properties iotests/051: Fix typo iotests/245: Fix type for iothread property qom: Reduce use of error_propagate() net/vhost-vdpa: Fix device compatibility check net/vhost-user: Fix device compatibility check net: Introduce NetClientInfo.check_peer_type() Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
253e399bab
@ -1459,7 +1459,7 @@ static void create_platform_bus(VirtMachineState *vms)
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
|
||||
dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE);
|
||||
dev->id = TYPE_PLATFORM_BUS_DEVICE;
|
||||
dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE);
|
||||
qdev_prop_set_uint32(dev, "num_irqs", PLATFORM_BUS_NUM_IRQS);
|
||||
qdev_prop_set_uint32(dev, "mmio_size", vms->memmap[VIRT_PLATFORM_BUS].size);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
@ -431,6 +431,12 @@ static void set_netdev(Object *obj, Visitor *v, const char *name,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (peers[i]->info->check_peer_type) {
|
||||
if (!peers[i]->info->check_peer_type(peers[i], obj->class, errp)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ncs[i] = peers[i];
|
||||
ncs[i]->queue_index = i;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-qdev.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
@ -211,14 +212,17 @@ void device_listener_unregister(DeviceListener *listener)
|
||||
QTAILQ_REMOVE(&device_listeners, listener, link);
|
||||
}
|
||||
|
||||
bool qdev_should_hide_device(QemuOpts *opts)
|
||||
bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
DeviceListener *listener;
|
||||
|
||||
QTAILQ_FOREACH(listener, &device_listeners, link) {
|
||||
if (listener->hide_device) {
|
||||
if (listener->hide_device(listener, opts)) {
|
||||
if (listener->hide_device(listener, opts, from_json, errp)) {
|
||||
return true;
|
||||
} else if (*errp) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -955,7 +959,8 @@ static void device_finalize(Object *obj)
|
||||
dev->canonical_path = NULL;
|
||||
}
|
||||
|
||||
qemu_opts_del(dev->opts);
|
||||
qobject_unref(dev->opts);
|
||||
g_free(dev->id);
|
||||
}
|
||||
|
||||
static void device_class_base_init(ObjectClass *class, void *data)
|
||||
|
@ -796,48 +796,34 @@ static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n)
|
||||
|
||||
typedef struct {
|
||||
VirtIONet *n;
|
||||
char *id;
|
||||
} FailoverId;
|
||||
DeviceState *dev;
|
||||
} FailoverDevice;
|
||||
|
||||
/**
|
||||
* Set the id of the failover primary device
|
||||
* Set the failover primary device
|
||||
*
|
||||
* @opaque: FailoverId to setup
|
||||
* @opts: opts for device we are handling
|
||||
* @errp: returns an error if this function fails
|
||||
*/
|
||||
static int failover_set_primary(void *opaque, QemuOpts *opts, Error **errp)
|
||||
static int failover_set_primary(DeviceState *dev, void *opaque)
|
||||
{
|
||||
FailoverId *fid = opaque;
|
||||
const char *standby_id = qemu_opt_get(opts, "failover_pair_id");
|
||||
FailoverDevice *fdev = opaque;
|
||||
PCIDevice *pci_dev = (PCIDevice *)
|
||||
object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE);
|
||||
|
||||
if (g_strcmp0(standby_id, fid->n->netclient_name) == 0) {
|
||||
fid->id = g_strdup(opts->id);
|
||||
if (!pci_dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_strcmp0(pci_dev->failover_pair_id, fdev->n->netclient_name)) {
|
||||
fdev->dev = dev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the primary device id for this failover virtio-net
|
||||
*
|
||||
* @n: VirtIONet device
|
||||
* @errp: returns an error if this function fails
|
||||
*/
|
||||
static char *failover_find_primary_device_id(VirtIONet *n)
|
||||
{
|
||||
Error *err = NULL;
|
||||
FailoverId fid;
|
||||
|
||||
fid.n = n;
|
||||
if (!qemu_opts_foreach(qemu_find_opts("device"),
|
||||
failover_set_primary, &fid, &err)) {
|
||||
return NULL;
|
||||
}
|
||||
return fid.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the primary device for this failover virtio-net
|
||||
*
|
||||
@ -846,39 +832,38 @@ static char *failover_find_primary_device_id(VirtIONet *n)
|
||||
*/
|
||||
static DeviceState *failover_find_primary_device(VirtIONet *n)
|
||||
{
|
||||
char *id = failover_find_primary_device_id(n);
|
||||
FailoverDevice fdev = {
|
||||
.n = n,
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return qdev_find_recursive(sysbus_get_default(), id);
|
||||
qbus_walk_children(sysbus_get_default(), failover_set_primary, NULL,
|
||||
NULL, NULL, &fdev);
|
||||
return fdev.dev;
|
||||
}
|
||||
|
||||
static void failover_add_primary(VirtIONet *n, Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
QemuOpts *opts;
|
||||
char *id;
|
||||
DeviceState *dev = failover_find_primary_device(n);
|
||||
|
||||
if (dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
id = failover_find_primary_device_id(n);
|
||||
if (!id) {
|
||||
if (!n->primary_opts) {
|
||||
error_setg(errp, "Primary device not found");
|
||||
error_append_hint(errp, "Virtio-net failover will not work. Make "
|
||||
"sure primary device has parameter"
|
||||
" failover_pair_id=%s\n", n->netclient_name);
|
||||
return;
|
||||
}
|
||||
opts = qemu_opts_find(qemu_find_opts("device"), id);
|
||||
g_assert(opts); /* cannot be NULL because id was found using opts list */
|
||||
dev = qdev_device_add(opts, &err);
|
||||
|
||||
dev = qdev_device_add_from_qdict(n->primary_opts,
|
||||
n->primary_opts_from_json,
|
||||
&err);
|
||||
if (err) {
|
||||
qemu_opts_del(opts);
|
||||
qobject_unref(n->primary_opts);
|
||||
n->primary_opts = NULL;
|
||||
} else {
|
||||
object_unref(OBJECT(dev));
|
||||
}
|
||||
@ -3304,7 +3289,9 @@ static void virtio_net_migration_state_notifier(Notifier *notifier, void *data)
|
||||
}
|
||||
|
||||
static bool failover_hide_primary_device(DeviceListener *listener,
|
||||
QemuOpts *device_opts)
|
||||
const QDict *device_opts,
|
||||
bool from_json,
|
||||
Error **errp)
|
||||
{
|
||||
VirtIONet *n = container_of(listener, VirtIONet, primary_listener);
|
||||
const char *standby_id;
|
||||
@ -3312,11 +3299,20 @@ static bool failover_hide_primary_device(DeviceListener *listener,
|
||||
if (!device_opts) {
|
||||
return false;
|
||||
}
|
||||
standby_id = qemu_opt_get(device_opts, "failover_pair_id");
|
||||
standby_id = qdict_get_try_str(device_opts, "failover_pair_id");
|
||||
if (g_strcmp0(standby_id, n->netclient_name) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n->primary_opts) {
|
||||
error_setg(errp, "Cannot attach more than one primary device to '%s'",
|
||||
n->netclient_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -3506,8 +3502,11 @@ static void virtio_net_device_unrealize(DeviceState *dev)
|
||||
g_free(n->vlans);
|
||||
|
||||
if (n->failover) {
|
||||
qobject_unref(n->primary_opts);
|
||||
device_listener_unregister(&n->primary_listener);
|
||||
remove_migration_state_change_notifier(&n->migration_state);
|
||||
} else {
|
||||
assert(n->primary_opts == NULL);
|
||||
}
|
||||
|
||||
max_queues = n->multiqueue ? n->max_queues : 1;
|
||||
|
@ -245,7 +245,7 @@ static void pxb_dev_realize_common(PCIDevice *dev, bool pcie, Error **errp)
|
||||
} else {
|
||||
bus = pci_root_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
||||
bds = qdev_new("pci-bridge");
|
||||
bds->id = dev_name;
|
||||
bds->id = g_strdup(dev_name);
|
||||
qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
|
||||
qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
|
||||
}
|
||||
|
@ -1006,7 +1006,7 @@ void ppce500_init(MachineState *machine)
|
||||
/* Platform Bus Device */
|
||||
if (pmc->has_platform_bus) {
|
||||
dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE);
|
||||
dev->id = TYPE_PLATFORM_BUS_DEVICE;
|
||||
dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE);
|
||||
qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs);
|
||||
qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
@ -29,10 +29,10 @@
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/units.h"
|
||||
#include "sysemu/kvm.h"
|
||||
@ -941,7 +941,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev)
|
||||
}
|
||||
|
||||
if (vfio_opt_rom_in_denylist(vdev)) {
|
||||
if (dev->opts && qemu_opt_get(dev->opts, "rombar")) {
|
||||
if (dev->opts && qdict_haskey(dev->opts, "rombar")) {
|
||||
warn_report("Device at %s is known to cause system instability"
|
||||
" issues during option rom execution",
|
||||
vdev->vbasedev.name);
|
||||
|
@ -276,7 +276,8 @@ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom,
|
||||
xendev = g_malloc0(ops->size);
|
||||
object_initialize(&xendev->qdev, ops->size, TYPE_XENBACKEND);
|
||||
OBJECT(xendev)->free = g_free;
|
||||
qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev));
|
||||
qdev_set_id(DEVICE(xendev), g_strdup_printf("xen-%s-%d", type, dev),
|
||||
&error_fatal);
|
||||
qdev_realize(DEVICE(xendev), xen_sysbus, &error_fatal);
|
||||
object_unref(OBJECT(xendev));
|
||||
|
||||
|
@ -176,11 +176,11 @@ struct DeviceState {
|
||||
Object parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
const char *id;
|
||||
char *id;
|
||||
char *canonical_path;
|
||||
bool realized;
|
||||
bool pending_deleted_event;
|
||||
QemuOpts *opts;
|
||||
QDict *opts;
|
||||
int hotplugged;
|
||||
bool allow_unplug_during_migration;
|
||||
BusState *parent_bus;
|
||||
@ -201,8 +201,12 @@ struct DeviceListener {
|
||||
* informs qdev if a device should be visible or hidden. We can
|
||||
* hide a failover device depending for example on the device
|
||||
* opts.
|
||||
*
|
||||
* On errors, it returns false and errp is set. Device creation
|
||||
* should fail in this case.
|
||||
*/
|
||||
bool (*hide_device)(DeviceListener *listener, QemuOpts *device_opts);
|
||||
bool (*hide_device)(DeviceListener *listener, const QDict *device_opts,
|
||||
bool from_json, Error **errp);
|
||||
QTAILQ_ENTRY(DeviceListener) link;
|
||||
};
|
||||
|
||||
@ -831,13 +835,15 @@ void device_listener_unregister(DeviceListener *listener);
|
||||
|
||||
/**
|
||||
* @qdev_should_hide_device:
|
||||
* @opts: QemuOpts as passed on cmdline.
|
||||
* @opts: options QDict
|
||||
* @from_json: true if @opts entries are typed, false for all strings
|
||||
* @errp: pointer to error object
|
||||
*
|
||||
* Check if a device should be added.
|
||||
* When a device is added via qdev_device_add() this will be called,
|
||||
* and return if the device should be added now or not.
|
||||
*/
|
||||
bool qdev_should_hide_device(QemuOpts *opts);
|
||||
bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp);
|
||||
|
||||
typedef enum MachineInitPhase {
|
||||
/* current_machine is NULL. */
|
||||
|
@ -209,6 +209,8 @@ struct VirtIONet {
|
||||
bool failover_primary_hidden;
|
||||
bool failover;
|
||||
DeviceListener primary_listener;
|
||||
QDict *primary_opts;
|
||||
bool primary_opts_from_json;
|
||||
Notifier migration_state;
|
||||
VirtioNetRssData rss_data;
|
||||
struct NetRxPkt *rx_pkt;
|
||||
|
@ -9,6 +9,31 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp);
|
||||
|
||||
int qdev_device_help(QemuOpts *opts);
|
||||
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp);
|
||||
void qdev_set_id(DeviceState *dev, const char *id);
|
||||
DeviceState *qdev_device_add_from_qdict(const QDict *opts,
|
||||
bool from_json, Error **errp);
|
||||
|
||||
/**
|
||||
* qdev_set_id: parent the device and set its id if provided.
|
||||
* @dev: device to handle
|
||||
* @id: id to be given to the device, or NULL.
|
||||
*
|
||||
* Returns: the id of the device in case of success; otherwise NULL.
|
||||
*
|
||||
* @dev must be unrealized, unparented and must not have an id.
|
||||
*
|
||||
* If @id is non-NULL, this function tries to setup @dev qom path as
|
||||
* "/peripheral/id". If @id is already taken, it fails. If it succeeds,
|
||||
* the id field of @dev is set to @id (@dev now owns the given @id
|
||||
* parameter).
|
||||
*
|
||||
* If @id is NULL, this function generates a unique name and setups @dev
|
||||
* qom path as "/peripheral-anon/name". This name is not set as the id
|
||||
* of @dev.
|
||||
*
|
||||
* Upon success, it returns the id/name (generated or provided). The
|
||||
* returned string is owned by the corresponding child property and must
|
||||
* not be freed by the caller.
|
||||
*/
|
||||
const char *qdev_set_id(DeviceState *dev, char *id, Error **errp);
|
||||
|
||||
#endif
|
||||
|
@ -62,6 +62,7 @@ typedef struct SocketReadState SocketReadState;
|
||||
typedef void (SocketReadStateFinalize)(SocketReadState *rs);
|
||||
typedef void (NetAnnounce)(NetClientState *);
|
||||
typedef bool (SetSteeringEBPF)(NetClientState *, int);
|
||||
typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **);
|
||||
|
||||
typedef struct NetClientInfo {
|
||||
NetClientDriver type;
|
||||
@ -84,6 +85,7 @@ typedef struct NetClientInfo {
|
||||
SetVnetBE *set_vnet_be;
|
||||
NetAnnounce *announce;
|
||||
SetSteeringEBPF *set_steering_ebpf;
|
||||
NetCheckPeerType *check_peer_type;
|
||||
} NetClientInfo;
|
||||
|
||||
struct NetClientState {
|
||||
|
@ -198,6 +198,19 @@ static bool vhost_user_has_ufo(NetClientState *nc)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vhost_user_check_peer_type(NetClientState *nc, ObjectClass *oc,
|
||||
Error **errp)
|
||||
{
|
||||
const char *driver = object_class_get_name(oc);
|
||||
|
||||
if (!g_str_has_prefix(driver, "virtio-net-")) {
|
||||
error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static NetClientInfo net_vhost_user_info = {
|
||||
.type = NET_CLIENT_DRIVER_VHOST_USER,
|
||||
.size = sizeof(NetVhostUserState),
|
||||
@ -207,6 +220,7 @@ static NetClientInfo net_vhost_user_info = {
|
||||
.has_ufo = vhost_user_has_ufo,
|
||||
.set_vnet_be = vhost_user_set_vnet_endianness,
|
||||
.set_vnet_le = vhost_user_set_vnet_endianness,
|
||||
.check_peer_type = vhost_user_check_peer_type,
|
||||
};
|
||||
|
||||
static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond,
|
||||
@ -397,27 +411,6 @@ static Chardev *net_vhost_claim_chardev(
|
||||
return chr;
|
||||
}
|
||||
|
||||
static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
const char *name = opaque;
|
||||
const char *driver, *netdev;
|
||||
|
||||
driver = qemu_opt_get(opts, "driver");
|
||||
netdev = qemu_opt_get(opts, "netdev");
|
||||
|
||||
if (!driver || !netdev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(netdev, name) == 0 &&
|
||||
!g_str_has_prefix(driver, "virtio-net-")) {
|
||||
error_setg(errp, "vhost-user requires frontend driver virtio-net-*");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int net_init_vhost_user(const Netdev *netdev, const char *name,
|
||||
NetClientState *peer, Error **errp)
|
||||
{
|
||||
@ -433,12 +426,6 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* verify net frontend */
|
||||
if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
|
||||
(char *)name, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
queues = vhost_user_opts->has_queues ? vhost_user_opts->queues : 1;
|
||||
if (queues < 1 || queues > MAX_QUEUE_NUM) {
|
||||
error_setg(errp,
|
||||
|
@ -147,12 +147,26 @@ static bool vhost_vdpa_has_ufo(NetClientState *nc)
|
||||
|
||||
}
|
||||
|
||||
static bool vhost_vdpa_check_peer_type(NetClientState *nc, ObjectClass *oc,
|
||||
Error **errp)
|
||||
{
|
||||
const char *driver = object_class_get_name(oc);
|
||||
|
||||
if (!g_str_has_prefix(driver, "virtio-net-")) {
|
||||
error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static NetClientInfo net_vhost_vdpa_info = {
|
||||
.type = NET_CLIENT_DRIVER_VHOST_VDPA,
|
||||
.size = sizeof(VhostVDPAState),
|
||||
.cleanup = vhost_vdpa_cleanup,
|
||||
.has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
|
||||
.has_ufo = vhost_vdpa_has_ufo,
|
||||
.check_peer_type = vhost_vdpa_check_peer_type,
|
||||
};
|
||||
|
||||
static int net_vhost_vdpa_init(NetClientState *peer, const char *device,
|
||||
@ -179,24 +193,6 @@ static int net_vhost_vdpa_init(NetClientState *peer, const char *device,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
const char *name = opaque;
|
||||
const char *driver, *netdev;
|
||||
|
||||
driver = qemu_opt_get(opts, "driver");
|
||||
netdev = qemu_opt_get(opts, "netdev");
|
||||
if (!driver || !netdev) {
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(netdev, name) == 0 &&
|
||||
!g_str_has_prefix(driver, "virtio-net-")) {
|
||||
error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
|
||||
NetClientState *peer, Error **errp)
|
||||
{
|
||||
@ -204,10 +200,5 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
|
||||
|
||||
assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA);
|
||||
opts = &netdev->u.vhost_vdpa;
|
||||
/* verify net frontend */
|
||||
if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
|
||||
(char *)name, errp)) {
|
||||
return -1;
|
||||
}
|
||||
return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev);
|
||||
}
|
||||
|
@ -32,17 +32,23 @@
|
||||
##
|
||||
# @device_add:
|
||||
#
|
||||
# Add a device.
|
||||
#
|
||||
# @driver: the name of the new device's driver
|
||||
#
|
||||
# @bus: the device's parent bus (device tree path)
|
||||
#
|
||||
# @id: the device's ID, must be unique
|
||||
#
|
||||
# Additional arguments depend on the type.
|
||||
#
|
||||
# Add a device.
|
||||
# Features:
|
||||
# @json-cli: If present, the "-device" command line option supports JSON
|
||||
# syntax with a structure identical to the arguments of this
|
||||
# command.
|
||||
#
|
||||
# Notes:
|
||||
#
|
||||
# Additional arguments depend on the type.
|
||||
#
|
||||
# 1. For detailed information about this command, please refer to the
|
||||
# 'docs/qdev-device-use.txt' file.
|
||||
#
|
||||
@ -67,7 +73,8 @@
|
||||
##
|
||||
{ 'command': 'device_add',
|
||||
'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
|
||||
'gen': false } # so we can get the additional arguments
|
||||
'gen': false, # so we can get the additional arguments
|
||||
'features': ['json-cli'] }
|
||||
|
||||
##
|
||||
# @device_del:
|
||||
|
@ -1389,7 +1389,7 @@ bool object_property_get(Object *obj, const char *name, Visitor *v,
|
||||
bool object_property_set(Object *obj, const char *name, Visitor *v,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
ERRP_GUARD();
|
||||
ObjectProperty *prop = object_property_find_err(obj, name, errp);
|
||||
|
||||
if (prop == NULL) {
|
||||
@ -1400,9 +1400,8 @@ bool object_property_set(Object *obj, const char *name, Visitor *v,
|
||||
error_setg(errp, QERR_PERMISSION_DENIED);
|
||||
return false;
|
||||
}
|
||||
prop->set(obj, v, name, prop->opaque, &err);
|
||||
error_propagate(errp, err);
|
||||
return !err;
|
||||
prop->set(obj, v, name, prop->opaque, errp);
|
||||
return !*errp;
|
||||
}
|
||||
|
||||
bool object_property_set_str(Object *obj, const char *name,
|
||||
|
@ -46,25 +46,18 @@ static void object_set_properties_from_qdict(Object *obj, const QDict *qdict,
|
||||
Visitor *v, Error **errp)
|
||||
{
|
||||
const QDictEntry *e;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) {
|
||||
goto out;
|
||||
if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
|
||||
return;
|
||||
}
|
||||
for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
|
||||
if (!object_property_set(obj, e->key, v, &local_err)) {
|
||||
break;
|
||||
if (!object_property_set(obj, e->key, v, errp)) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!local_err) {
|
||||
visit_check_struct(v, &local_err);
|
||||
}
|
||||
visit_end_struct(v, NULL);
|
||||
|
||||
visit_check_struct(v, errp);
|
||||
out:
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
visit_end_struct(v, NULL);
|
||||
}
|
||||
|
||||
void object_set_properties_from_keyval(Object *obj, const QDict *qdict,
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "qapi/qmp/dispatch.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/help_option.h"
|
||||
@ -194,22 +196,6 @@ static void qdev_print_devinfos(bool show_no_user)
|
||||
g_slist_free(list);
|
||||
}
|
||||
|
||||
static int set_property(void *opaque, const char *name, const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
Object *obj = opaque;
|
||||
|
||||
if (strcmp(name, "driver") == 0)
|
||||
return 0;
|
||||
if (strcmp(name, "bus") == 0)
|
||||
return 0;
|
||||
|
||||
if (!object_property_parse(obj, name, value, errp)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *find_typename_by_alias(const char *alias)
|
||||
{
|
||||
int i;
|
||||
@ -578,32 +564,49 @@ static BusState *qbus_find(const char *path, Error **errp)
|
||||
return bus;
|
||||
}
|
||||
|
||||
void qdev_set_id(DeviceState *dev, const char *id)
|
||||
/* Takes ownership of @id, will be freed when deleting the device */
|
||||
const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
|
||||
{
|
||||
if (id) {
|
||||
dev->id = id;
|
||||
}
|
||||
ObjectProperty *prop;
|
||||
|
||||
if (dev->id) {
|
||||
object_property_add_child(qdev_get_peripheral(), dev->id,
|
||||
OBJECT(dev));
|
||||
assert(!dev->id && !dev->realized);
|
||||
|
||||
/*
|
||||
* object_property_[try_]add_child() below will assert the device
|
||||
* has no parent
|
||||
*/
|
||||
if (id) {
|
||||
prop = object_property_try_add_child(qdev_get_peripheral(), id,
|
||||
OBJECT(dev), NULL);
|
||||
if (prop) {
|
||||
dev->id = id;
|
||||
} else {
|
||||
g_free(id);
|
||||
error_setg(errp, "Duplicate device ID '%s'", id);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
static int anon_count;
|
||||
gchar *name = g_strdup_printf("device[%d]", anon_count++);
|
||||
object_property_add_child(qdev_get_peripheral_anon(), name,
|
||||
OBJECT(dev));
|
||||
prop = object_property_add_child(qdev_get_peripheral_anon(), name,
|
||||
OBJECT(dev));
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
return prop->name;
|
||||
}
|
||||
|
||||
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
||||
DeviceState *qdev_device_add_from_qdict(const QDict *opts,
|
||||
bool from_json, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
DeviceClass *dc;
|
||||
const char *driver, *path;
|
||||
char *id;
|
||||
DeviceState *dev = NULL;
|
||||
BusState *bus = NULL;
|
||||
|
||||
driver = qemu_opt_get(opts, "driver");
|
||||
driver = qdict_get_try_str(opts, "driver");
|
||||
if (!driver) {
|
||||
error_setg(errp, QERR_MISSING_PARAMETER, "driver");
|
||||
return NULL;
|
||||
@ -616,7 +619,7 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
||||
}
|
||||
|
||||
/* find bus */
|
||||
path = qemu_opt_get(opts, "bus");
|
||||
path = qdict_get_try_str(opts, "bus");
|
||||
if (path != NULL) {
|
||||
bus = qbus_find(path, errp);
|
||||
if (!bus) {
|
||||
@ -636,16 +639,18 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_opt_get(opts, "failover_pair_id")) {
|
||||
if (!opts->id) {
|
||||
if (qdict_haskey(opts, "failover_pair_id")) {
|
||||
if (!qdict_haskey(opts, "id")) {
|
||||
error_setg(errp, "Device with failover_pair_id don't have id");
|
||||
return NULL;
|
||||
}
|
||||
if (qdev_should_hide_device(opts)) {
|
||||
if (qdev_should_hide_device(opts, from_json, errp)) {
|
||||
if (bus && !qbus_is_hotpluggable(bus)) {
|
||||
error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
|
||||
}
|
||||
return NULL;
|
||||
} else if (*errp) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,16 +681,28 @@ DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
qdev_set_id(dev, qemu_opts_id(opts));
|
||||
|
||||
/* set properties */
|
||||
if (qemu_opt_foreach(opts, set_property, dev, errp)) {
|
||||
/*
|
||||
* set dev's parent and register its id.
|
||||
* If it fails it means the id is already taken.
|
||||
*/
|
||||
id = g_strdup(qdict_get_try_str(opts, "id"));
|
||||
if (!qdev_set_id(dev, id, errp)) {
|
||||
goto err_del_dev;
|
||||
}
|
||||
|
||||
/* set properties */
|
||||
dev->opts = qdict_clone_shallow(opts);
|
||||
qdict_del(dev->opts, "driver");
|
||||
qdict_del(dev->opts, "bus");
|
||||
qdict_del(dev->opts, "id");
|
||||
|
||||
object_set_properties_from_keyval(&dev->parent_obj, dev->opts, from_json,
|
||||
errp);
|
||||
if (*errp) {
|
||||
goto err_del_dev;
|
||||
}
|
||||
|
||||
dev->opts = opts;
|
||||
if (!qdev_realize(DEVICE(dev), bus, errp)) {
|
||||
dev->opts = NULL;
|
||||
goto err_del_dev;
|
||||
}
|
||||
return dev;
|
||||
@ -698,6 +715,19 @@ err_del_dev:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Takes ownership of @opts on success */
|
||||
DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
|
||||
{
|
||||
QDict *qdict = qemu_opts_to_qdict(opts, NULL);
|
||||
DeviceState *ret;
|
||||
|
||||
ret = qdev_device_add_from_qdict(qdict, false, errp);
|
||||
if (ret) {
|
||||
qemu_opts_del(opts);
|
||||
}
|
||||
qobject_unref(qdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
|
||||
static void qbus_print(Monitor *mon, BusState *bus, int indent);
|
||||
|
63
softmmu/vl.c
63
softmmu/vl.c
@ -144,6 +144,12 @@ typedef struct ObjectOption {
|
||||
QTAILQ_ENTRY(ObjectOption) next;
|
||||
} ObjectOption;
|
||||
|
||||
typedef struct DeviceOption {
|
||||
QDict *opts;
|
||||
Location loc;
|
||||
QTAILQ_ENTRY(DeviceOption) next;
|
||||
} DeviceOption;
|
||||
|
||||
static const char *cpu_option;
|
||||
static const char *mem_path;
|
||||
static const char *incoming;
|
||||
@ -151,6 +157,7 @@ static const char *loadvm;
|
||||
static const char *accelerators;
|
||||
static QDict *machine_opts_dict;
|
||||
static QTAILQ_HEAD(, ObjectOption) object_opts = QTAILQ_HEAD_INITIALIZER(object_opts);
|
||||
static QTAILQ_HEAD(, DeviceOption) device_opts = QTAILQ_HEAD_INITIALIZER(device_opts);
|
||||
static ram_addr_t maxram_size;
|
||||
static uint64_t ram_slots;
|
||||
static int display_remote;
|
||||
@ -494,21 +501,39 @@ const char *qemu_get_vm_name(void)
|
||||
return qemu_name;
|
||||
}
|
||||
|
||||
static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp)
|
||||
static void default_driver_disable(const char *driver)
|
||||
{
|
||||
const char *driver = qemu_opt_get(opts, "driver");
|
||||
int i;
|
||||
|
||||
if (!driver)
|
||||
return 0;
|
||||
if (!driver) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(default_list); i++) {
|
||||
if (strcmp(default_list[i].driver, driver) != 0)
|
||||
continue;
|
||||
*(default_list[i].flag) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
const char *driver = qemu_opt_get(opts, "driver");
|
||||
|
||||
default_driver_disable(driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void default_driver_check_json(void)
|
||||
{
|
||||
DeviceOption *opt;
|
||||
|
||||
QTAILQ_FOREACH(opt, &device_opts, next) {
|
||||
const char *driver = qdict_get_try_str(opt->opts, "driver");
|
||||
default_driver_disable(driver);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_name(void *opaque, QemuOpts *opts, Error **errp)
|
||||
{
|
||||
const char *proc_name;
|
||||
@ -1311,6 +1336,7 @@ static void qemu_disable_default_devices(void)
|
||||
{
|
||||
MachineClass *machine_class = MACHINE_GET_CLASS(current_machine);
|
||||
|
||||
default_driver_check_json();
|
||||
qemu_opts_foreach(qemu_find_opts("device"),
|
||||
default_driver_check, NULL, NULL);
|
||||
qemu_opts_foreach(qemu_find_opts("global"),
|
||||
@ -2637,6 +2663,8 @@ static void qemu_init_board(void)
|
||||
|
||||
static void qemu_create_cli_devices(void)
|
||||
{
|
||||
DeviceOption *opt;
|
||||
|
||||
soundhw_init();
|
||||
|
||||
qemu_opts_foreach(qemu_find_opts("fw_cfg"),
|
||||
@ -2652,6 +2680,18 @@ static void qemu_create_cli_devices(void)
|
||||
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
|
||||
qemu_opts_foreach(qemu_find_opts("device"),
|
||||
device_init_func, NULL, &error_fatal);
|
||||
QTAILQ_FOREACH(opt, &device_opts, next) {
|
||||
loc_push_restore(&opt->loc);
|
||||
/*
|
||||
* TODO Eventually we should call qmp_device_add() here to make sure it
|
||||
* behaves the same, but QMP still has to accept incorrectly typed
|
||||
* options until libvirt is fixed and we want to be strict on the CLI
|
||||
* from the start, so call qdev_device_add_from_qdict() directly for
|
||||
* now.
|
||||
*/
|
||||
qdev_device_add_from_qdict(opt->opts, true, &error_fatal);
|
||||
loc_pop(&opt->loc);
|
||||
}
|
||||
rom_reset_order_override();
|
||||
}
|
||||
|
||||
@ -3352,9 +3392,18 @@ void qemu_init(int argc, char **argv, char **envp)
|
||||
add_device_config(DEV_USB, optarg);
|
||||
break;
|
||||
case QEMU_OPTION_device:
|
||||
if (!qemu_opts_parse_noisily(qemu_find_opts("device"),
|
||||
optarg, true)) {
|
||||
exit(1);
|
||||
if (optarg[0] == '{') {
|
||||
QObject *obj = qobject_from_json(optarg, &error_fatal);
|
||||
DeviceOption *opt = g_new0(DeviceOption, 1);
|
||||
opt->opts = qobject_to(QDict, obj);
|
||||
loc_save(&opt->loc);
|
||||
assert(opt->opts != NULL);
|
||||
QTAILQ_INSERT_TAIL(&device_opts, opt, next);
|
||||
} else {
|
||||
if (!qemu_opts_parse_noisily(qemu_find_opts("device"),
|
||||
optarg, true)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QEMU_OPTION_smp:
|
||||
|
@ -199,7 +199,7 @@ case "$QEMU_DEFAULT_MACHINE" in
|
||||
# virtio-blk enables the iothread only when the driver initialises the
|
||||
# device, so a second virtio-blk device can't be added even with the
|
||||
# same iothread. virtio-scsi allows this.
|
||||
run_qemu $iothread -device virtio-blk-pci,drive=disk,iothread=iothread0,share-rw=on
|
||||
run_qemu $iothread -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on
|
||||
run_qemu $iothread -device virtio-scsi,id=virtio-scsi1,iothread=thread0 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on
|
||||
;;
|
||||
*)
|
||||
|
@ -183,9 +183,9 @@ Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on: Cannot change iothread of active block backend
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=iothread0,share-rw=on
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=iothread0,share-rw=on: Cannot change iothread of active block backend
|
||||
(qemu) QEMU_PROG: -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on: Cannot change iothread of active block backend
|
||||
|
||||
Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1,iothread=thread0 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on
|
||||
QEMU X.Y.Z monitor - type 'help' for more information
|
||||
|
@ -1189,10 +1189,10 @@ class TestBlockdevReopen(iotests.QMPTestCase):
|
||||
self.run_test_iothreads('iothread0', 'iothread0')
|
||||
|
||||
def test_iothreads_switch_backing(self):
|
||||
self.run_test_iothreads('iothread0', None)
|
||||
self.run_test_iothreads('iothread0', '')
|
||||
|
||||
def test_iothreads_switch_overlay(self):
|
||||
self.run_test_iothreads(None, 'iothread0')
|
||||
self.run_test_iothreads('', 'iothread0')
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.activate_logging()
|
||||
|
@ -1126,11 +1126,11 @@ int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
Location loc;
|
||||
QemuOpts *opts;
|
||||
QemuOpts *opts, *next;
|
||||
int rc = 0;
|
||||
|
||||
loc_push_none(&loc);
|
||||
QTAILQ_FOREACH(opts, &list->head, next) {
|
||||
QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) {
|
||||
loc_restore(&opts->loc);
|
||||
rc = func(opaque, opts, errp);
|
||||
if (rc) {
|
||||
|
Loading…
Reference in New Issue
Block a user