mirror of https://github.com/proxmox/mirror_qemu
qdev hotplug: infrastructure and monitor commands.
Adds device_add and device_del commands. device_add accepts accepts the same syntax like the -device command line switch. device_del expects a device id. So you should tag your devices with ids if you want to remove them later on, like this: device_add pci-ohci,id=ohci device_del ohci Unplugging via pci_del or usb_del works too. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>master
parent
a861c453e7
commit
3418bd25e1
79
hw/qdev.c
79
hw/qdev.c
|
@ -31,6 +31,8 @@
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
|
|
||||||
/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
|
/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
|
||||||
|
static int qdev_hotplug = 0;
|
||||||
|
|
||||||
static BusState *main_system_bus;
|
static BusState *main_system_bus;
|
||||||
|
|
||||||
static DeviceInfo *device_info_list;
|
static DeviceInfo *device_info_list;
|
||||||
|
@ -102,6 +104,10 @@ DeviceState *qdev_create(BusState *bus, const char *name)
|
||||||
qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
|
qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
|
||||||
qdev_prop_set_compat(dev);
|
qdev_prop_set_compat(dev);
|
||||||
QLIST_INSERT_HEAD(&bus->children, dev, sibling);
|
QLIST_INSERT_HEAD(&bus->children, dev, sibling);
|
||||||
|
if (qdev_hotplug) {
|
||||||
|
assert(bus->allow_hotplug);
|
||||||
|
dev->hotplugged = 1;
|
||||||
|
}
|
||||||
dev->state = DEV_STATE_CREATED;
|
dev->state = DEV_STATE_CREATED;
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
@ -192,6 +198,11 @@ DeviceState *qdev_device_add(QemuOpts *opts)
|
||||||
path ? path : info->bus_info->name, info->name);
|
path ? path : info->bus_info->name, info->name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (qdev_hotplug && !bus->allow_hotplug) {
|
||||||
|
qemu_error("Bus %s does not support hotplugging\n",
|
||||||
|
bus->name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* create device, set properties */
|
/* create device, set properties */
|
||||||
qdev = qdev_create(bus, driver);
|
qdev = qdev_create(bus, driver);
|
||||||
|
@ -229,6 +240,24 @@ int qdev_init(DeviceState *dev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int qdev_unplug(DeviceState *dev)
|
||||||
|
{
|
||||||
|
if (!dev->parent_bus->allow_hotplug) {
|
||||||
|
qemu_error("Bus %s does not support hotplugging\n",
|
||||||
|
dev->parent_bus->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return dev->info->unplug(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* can be used as ->unplug() callback for the simple cases */
|
||||||
|
int qdev_simple_unplug_cb(DeviceState *dev)
|
||||||
|
{
|
||||||
|
/* just zap it */
|
||||||
|
qdev_free(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Unlink device from bus and free the structure. */
|
/* Unlink device from bus and free the structure. */
|
||||||
void qdev_free(DeviceState *dev)
|
void qdev_free(DeviceState *dev)
|
||||||
{
|
{
|
||||||
|
@ -252,6 +281,15 @@ void qdev_free(DeviceState *dev)
|
||||||
qemu_free(dev);
|
qemu_free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qdev_machine_creation_done(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* ok, initial machine setup is done, starting from now we can
|
||||||
|
* only create hotpluggable devices
|
||||||
|
*/
|
||||||
|
qdev_hotplug = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get a character (serial) device interface. */
|
/* Get a character (serial) device interface. */
|
||||||
CharDriverState *qdev_init_chardev(DeviceState *dev)
|
CharDriverState *qdev_init_chardev(DeviceState *dev)
|
||||||
{
|
{
|
||||||
|
@ -370,6 +408,24 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
||||||
|
{
|
||||||
|
DeviceState *dev, *ret;
|
||||||
|
BusState *child;
|
||||||
|
|
||||||
|
QLIST_FOREACH(dev, &bus->children, sibling) {
|
||||||
|
if (dev->id && strcmp(dev->id, id) == 0)
|
||||||
|
return dev;
|
||||||
|
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
||||||
|
ret = qdev_find_recursive(child, id);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void qbus_list_bus(DeviceState *dev, char *dest, int len)
|
static void qbus_list_bus(DeviceState *dev, char *dest, int len)
|
||||||
{
|
{
|
||||||
BusState *child;
|
BusState *child;
|
||||||
|
@ -647,3 +703,26 @@ void do_info_qdm(Monitor *mon)
|
||||||
monitor_printf(mon, "%s\n", msg);
|
monitor_printf(mon, "%s\n", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_device_add(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
QemuOpts *opts;
|
||||||
|
|
||||||
|
opts = qemu_opts_parse(&qemu_device_opts,
|
||||||
|
qdict_get_str(qdict, "config"), "driver");
|
||||||
|
if (opts)
|
||||||
|
qdev_device_add(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_device_del(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
const char *id = qdict_get_str(qdict, "id");
|
||||||
|
DeviceState *dev;
|
||||||
|
|
||||||
|
dev = qdev_find_recursive(main_system_bus, id);
|
||||||
|
if (NULL == dev) {
|
||||||
|
qemu_error("Device '%s' not found\n", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qdev_unplug(dev);
|
||||||
|
}
|
||||||
|
|
12
hw/qdev.h
12
hw/qdev.h
|
@ -29,6 +29,7 @@ enum DevState {
|
||||||
struct DeviceState {
|
struct DeviceState {
|
||||||
const char *id;
|
const char *id;
|
||||||
enum DevState state;
|
enum DevState state;
|
||||||
|
int hotplugged;
|
||||||
DeviceInfo *info;
|
DeviceInfo *info;
|
||||||
BusState *parent_bus;
|
BusState *parent_bus;
|
||||||
int num_gpio_out;
|
int num_gpio_out;
|
||||||
|
@ -53,6 +54,7 @@ struct BusState {
|
||||||
DeviceState *parent;
|
DeviceState *parent;
|
||||||
BusInfo *info;
|
BusInfo *info;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
int allow_hotplug;
|
||||||
int qdev_allocated;
|
int qdev_allocated;
|
||||||
QLIST_HEAD(, DeviceState) children;
|
QLIST_HEAD(, DeviceState) children;
|
||||||
QLIST_ENTRY(BusState) sibling;
|
QLIST_ENTRY(BusState) sibling;
|
||||||
|
@ -98,7 +100,10 @@ struct CompatProperty {
|
||||||
DeviceState *qdev_create(BusState *bus, const char *name);
|
DeviceState *qdev_create(BusState *bus, const char *name);
|
||||||
DeviceState *qdev_device_add(QemuOpts *opts);
|
DeviceState *qdev_device_add(QemuOpts *opts);
|
||||||
int qdev_init(DeviceState *dev);
|
int qdev_init(DeviceState *dev);
|
||||||
|
int qdev_unplug(DeviceState *dev);
|
||||||
void qdev_free(DeviceState *dev);
|
void qdev_free(DeviceState *dev);
|
||||||
|
int qdev_simple_unplug_cb(DeviceState *dev);
|
||||||
|
void qdev_machine_creation_done(void);
|
||||||
|
|
||||||
qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
|
qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
|
||||||
void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
|
void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
|
||||||
|
@ -108,7 +113,7 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
|
||||||
/*** Device API. ***/
|
/*** Device API. ***/
|
||||||
|
|
||||||
typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info);
|
typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info);
|
||||||
typedef int (*qdev_exitfn)(DeviceState *dev);
|
typedef int (*qdev_event)(DeviceState *dev);
|
||||||
|
|
||||||
struct DeviceInfo {
|
struct DeviceInfo {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
@ -126,7 +131,8 @@ struct DeviceInfo {
|
||||||
|
|
||||||
/* Private to qdev / bus. */
|
/* Private to qdev / bus. */
|
||||||
qdev_initfn init;
|
qdev_initfn init;
|
||||||
qdev_exitfn exit;
|
qdev_event unplug;
|
||||||
|
qdev_event exit;
|
||||||
BusInfo *bus_info;
|
BusInfo *bus_info;
|
||||||
struct DeviceInfo *next;
|
struct DeviceInfo *next;
|
||||||
};
|
};
|
||||||
|
@ -165,6 +171,8 @@ void qbus_free(BusState *bus);
|
||||||
|
|
||||||
void do_info_qtree(Monitor *mon);
|
void do_info_qtree(Monitor *mon);
|
||||||
void do_info_qdm(Monitor *mon);
|
void do_info_qdm(Monitor *mon);
|
||||||
|
void do_device_add(Monitor *mon, const QDict *qdict);
|
||||||
|
void do_device_del(Monitor *mon, const QDict *qdict);
|
||||||
|
|
||||||
/*** qdev-properties.c ***/
|
/*** qdev-properties.c ***/
|
||||||
|
|
||||||
|
|
|
@ -369,8 +369,24 @@ hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor
|
||||||
command @code{info usb} to see the devices you can remove.
|
command @code{info usb} to see the devices you can remove.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{ "cpu", "index:i", do_cpu_set,
|
{ "device_add", "config:s", do_device_add,
|
||||||
"index", "set the default CPU" },
|
"device", "add device, like -device on the command line" },
|
||||||
|
STEXI
|
||||||
|
@item device_add @var{config}
|
||||||
|
|
||||||
|
Add device.
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{ "device_del", "id:s", do_device_del,
|
||||||
|
"device", "remove device" },
|
||||||
|
STEXI
|
||||||
|
@item device_del @var{id}
|
||||||
|
|
||||||
|
Remove device @var{id}.
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{ "cpu", "index:i", do_cpu_set, "index", "set the default CPU" },
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
Set the default CPU.
|
Set the default CPU.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
Loading…
Reference in New Issue