From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MrGhM-0006bj-VA for qemu-devel@nongnu.org; Fri, 25 Sep 2009 15:43:21 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MrGhG-0006Uz-VS for qemu-devel@nongnu.org; Fri, 25 Sep 2009 15:43:20 -0400 Received: from [199.232.76.173] (port=56062 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MrGhF-0006Uh-J4 for qemu-devel@nongnu.org; Fri, 25 Sep 2009 15:43:13 -0400 Received: from mx1.redhat.com ([209.132.183.28]:9627) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MrGhE-0004g4-Rw for qemu-devel@nongnu.org; Fri, 25 Sep 2009 15:43:13 -0400 Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id n8PJhBuS024396 for ; Fri, 25 Sep 2009 15:43:12 -0400 From: Gerd Hoffmann Date: Fri, 25 Sep 2009 21:42:41 +0200 Message-Id: <1253907769-1067-17-git-send-email-kraxel@redhat.com> In-Reply-To: <1253907769-1067-1-git-send-email-kraxel@redhat.com> References: <1253907769-1067-1-git-send-email-kraxel@redhat.com> Subject: [Qemu-devel] [PATCH 16/24] qdev hotplug: infrastructure and monitor commands. List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Gerd Hoffmann 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 --- hw/qdev.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/qdev.h | 12 +++++++- qemu-monitor.hx | 16 +++++++++++ vl.c | 2 + 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/hw/qdev.c b/hw/qdev.c index a25245a..00cb849 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -31,6 +31,8 @@ #include "monitor.h" /* 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 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_compat(dev); QLIST_INSERT_HEAD(&bus->children, dev, sibling); + if (qdev_hotplug) { + assert(bus->allow_hotplug); + dev->hotplugged = 1; + } dev->state = DEV_STATE_CREATED; return dev; } @@ -192,6 +198,11 @@ DeviceState *qdev_device_add(QemuOpts *opts) path ? path : info->bus_info->name, info->name); 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 */ qdev = qdev_create(bus, driver); @@ -229,6 +240,24 @@ int qdev_init(DeviceState *dev) 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. */ void qdev_free(DeviceState *dev) { @@ -252,6 +281,15 @@ void qdev_free(DeviceState *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. */ CharDriverState *qdev_init_chardev(DeviceState *dev) { @@ -370,6 +408,24 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name, 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) { BusState *child; @@ -647,3 +703,26 @@ void do_info_qdm(Monitor *mon) 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); +} diff --git a/hw/qdev.h b/hw/qdev.h index 0db2d32..8b6e4fe 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -29,6 +29,7 @@ enum DevState { struct DeviceState { const char *id; enum DevState state; + int hotplugged; DeviceInfo *info; BusState *parent_bus; int num_gpio_out; @@ -53,6 +54,7 @@ struct BusState { DeviceState *parent; BusInfo *info; const char *name; + int allow_hotplug; int qdev_allocated; QLIST_HEAD(, DeviceState) children; QLIST_ENTRY(BusState) sibling; @@ -97,7 +99,10 @@ struct CompatProperty { DeviceState *qdev_create(BusState *bus, const char *name); DeviceState *qdev_device_add(QemuOpts *opts); int qdev_init(DeviceState *dev); +int qdev_unplug(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); void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); @@ -107,7 +112,7 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name); /*** Device API. ***/ typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info); -typedef int (*qdev_exitfn)(DeviceState *dev); +typedef int (*qdev_event)(DeviceState *dev); struct DeviceInfo { const char *name; @@ -125,7 +130,8 @@ struct DeviceInfo { /* Private to qdev / bus. */ qdev_initfn init; - qdev_exitfn exit; + qdev_event unplug; + qdev_event exit; BusInfo *bus_info; struct DeviceInfo *next; }; @@ -164,6 +170,8 @@ void qbus_free(BusState *bus); void do_info_qtree(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 ***/ diff --git a/qemu-monitor.hx b/qemu-monitor.hx index 9f91873..e4afe62 100644 --- a/qemu-monitor.hx +++ b/qemu-monitor.hx @@ -369,6 +369,22 @@ hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor command @code{info usb} to see the devices you can remove. ETEXI + { "device_add", "config:s", do_device_add, + "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 diff --git a/vl.c b/vl.c index eb01da7..7df9328 100644 --- a/vl.c +++ b/vl.c @@ -5869,6 +5869,8 @@ int main(int argc, char **argv, char **envp) exit(1); } + qdev_machine_creation_done(); + if (loadvm) { if (load_vmstate(cur_mon, loadvm) < 0) { autostart = 0; -- 1.6.2.5