From 402a4ff190f92d6a691e9d53a6420d9e305e6e1e Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Tue, 11 Mar 2008 17:12:07 -0300 Subject: [PATCH] kvm: qemu: device and disk hot-add Add monitor command to hot-add PCI devices (nic and storage). Syntax is: pci_add pcibus nic|storage params It returns the bus slot and function for the newly added device on success. It is possible to attach a disk to a device after PCI initialization via the drive_add command. If so, a manual scan of the SCSI bus on the guest is necessary. Save QEMUMachine necessary for drive_init. Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- qemu/Makefile.target | 2 + qemu/hw/boards.h | 2 + qemu/hw/device-hotplug.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++ qemu/monitor.c | 6 ++ qemu/sysemu.h | 5 ++ qemu/vl.c | 3 + 6 files changed, 175 insertions(+) create mode 100644 qemu/hw/device-hotplug.c diff --git a/qemu/Makefile.target b/qemu/Makefile.target index 67a433e4..cec2b80b 100644 --- a/qemu/Makefile.target +++ b/qemu/Makefile.target @@ -579,6 +579,8 @@ OBJS+= hypercall.o # virtio devices OBJS += virtio.o virtio-net.o virtio-blk.o +OBJS += device-hotplug.o + ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o diff --git a/qemu/hw/boards.h b/qemu/hw/boards.h index b3776066..67e57dda 100644 --- a/qemu/hw/boards.h +++ b/qemu/hw/boards.h @@ -19,6 +19,8 @@ typedef struct QEMUMachine { int qemu_register_machine(QEMUMachine *m); +extern QEMUMachine *current_machine; + /* Axis ETRAX. */ extern QEMUMachine bareetraxfs_machine; diff --git a/qemu/hw/device-hotplug.c b/qemu/hw/device-hotplug.c new file mode 100644 index 00000000..02e3e172 --- /dev/null +++ b/qemu/hw/device-hotplug.c @@ -0,0 +1,157 @@ +#include "hw.h" +#include "boards.h" +#include "pci.h" +#include "net.h" +#include "sysemu.h" +#include "pc.h" +#include "console.h" + +static PCIDevice *qemu_system_hot_add_nic(const char *opts, int bus_nr) +{ + int ret; + char buf[4096]; + PCIBus *pci_bus; + + pci_bus = pci_find_bus (bus_nr); + if (!pci_bus) { + term_printf ("Can't find pci_bus %d\n", bus_nr); + return NULL; + } + + memset (buf, 0, sizeof (buf)); + + strcpy (buf, "nic,"); + strncat (buf, opts, sizeof (buf) - strlen (buf) - 1); + + ret = net_client_init (buf); + if (ret < 0 || !nd_table[ret].model) + return NULL; + return pci_nic_init (pci_bus, &nd_table[ret], -1); +} + +static int add_init_drive(const char *opts) +{ + int drive_opt_idx, drive_idx; + int ret = -1; + + drive_opt_idx = drive_add(NULL, "%s", opts); + if (!drive_opt_idx) + return ret; + + drive_idx = drive_init(&drives_opt[drive_opt_idx], 0, current_machine); + if (drive_idx == -1) { + drive_remove(drive_opt_idx); + return ret; + } + + return drive_idx; +} + +void drive_hot_add(int pcibus, const char *devfn_string, const char *opts) +{ + int drive_idx, type, bus; + int devfn; + int success = 0; + PCIDevice *dev; + + devfn = strtoul(devfn_string, NULL, 0); + + dev = pci_find_device(pcibus, PCI_SLOT(devfn)); + if (!dev) { + term_printf("no pci device with devfn %d (slot %d)\n", devfn, + PCI_SLOT(devfn)); + return; + } + + drive_idx = add_init_drive(opts); + if (drive_idx < 0) + return; + type = drives_table[drive_idx].type; + bus = drive_get_max_bus (type); + + switch (type) { + case IF_SCSI: + success = 1; + lsi_scsi_attach (dev, drives_table[drive_idx].bdrv, + drives_table[drive_idx].unit); + break; + default: + term_printf("Can't hot-add drive to type %d\n", type); + } + + if (success) + term_printf("OK bus %d, unit %d\n", drives_table[drive_idx].bus, + drives_table[drive_idx].unit); + return; +} + +static PCIDevice *qemu_system_hot_add_storage(const char *opts, int bus_nr) +{ + void *opaque = NULL; + PCIBus *pci_bus; + int type = -1, drive_idx = -1; + char buf[128]; + + pci_bus = pci_find_bus(bus_nr); + if (!pci_bus) { + term_printf("Can't find pci_bus %d\n", bus_nr); + return NULL; + } + + if (get_param_value(buf, sizeof(buf), "if", opts)) { + if (!strcmp(buf, "scsi")) + type = IF_SCSI; + else if (!strcmp(buf, "virtio")) { + type = IF_VIRTIO; + } + } else { + term_printf("no if= specified\n"); + return NULL; + } + + if (get_param_value(buf, sizeof(buf), "file", opts)) { + drive_idx = add_init_drive(opts); + if (drive_idx < 0) + return NULL; + } else if (type == IF_VIRTIO) { + term_printf("virtio requires a backing file/device.\n"); + return NULL; + } + + switch (type) { + case IF_SCSI: + opaque = lsi_scsi_init (pci_bus, -1); + if (drive_idx >= 0) + lsi_scsi_attach (opaque, drives_table[drive_idx].bdrv, + drives_table[drive_idx].unit); + break; + case IF_VIRTIO: + opaque = virtio_blk_init (pci_bus, 0x1AF4, 0x1001, + drives_table[drive_idx].bdrv); + break; + default: + term_printf ("type %s not a hotpluggable PCI device.\n", buf); + } + + return opaque; +} + +void device_hot_add(int pcibus, const char *type, const char *opts) +{ + PCIDevice *dev = NULL; + + if (strcmp(type, "nic") == 0) + dev = qemu_system_hot_add_nic(opts, pcibus); + else if (strcmp(type, "storage") == 0) + dev = qemu_system_hot_add_storage(opts, pcibus); + else + term_printf("invalid type: %s\n", type); + + if (dev) { + qemu_system_device_hot_add(PCI_SLOT(dev->devfn), 1); + term_printf("OK bus %d, slot %d, function %d (devfn %d)\n", + pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), dev->devfn); + } else + term_printf("failed to add %s\n", opts); +} diff --git a/qemu/monitor.c b/qemu/monitor.c index aaff280c..66af8da5 100644 --- a/qemu/monitor.c +++ b/qemu/monitor.c @@ -1359,6 +1359,12 @@ static term_cmd_t term_cmds[] = { { "migrate_set_speed", "s", do_migrate_set_speed, "value", "set maximum speed (in bytes) for migrations" }, { "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" }, + { "drive_add", "iss", drive_hot_add, "pcibus pcidevfn [file=file][,if=type][,bus=n]\n" + "[,unit=m][,media=d][index=i]\n" + "[,cyls=c,heads=h,secs=s[,trans=t]]\n" + "[snapshot=on|off][,cache=on|off]", + "add drive to PCI storage controller" }, + { "pci_add", "iss", device_hot_add, "bus nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" }, { NULL, NULL, }, }; diff --git a/qemu/sysemu.h b/qemu/sysemu.h index e5242765..7bd492c6 100644 --- a/qemu/sysemu.h +++ b/qemu/sysemu.h @@ -176,6 +176,11 @@ extern int drive_init(struct drive_opt *arg, int snapshot, void *machine); /* acpi */ void qemu_system_cpu_hot_add(int cpu, int state); void qemu_system_hot_add_init(char *cpu_model); +void qemu_system_device_hot_add(int slot, int state); + +/* device-hotplug */ +void device_hot_add(int pcibus, const char *type, const char *opts); +void drive_hot_add(int pcibus, const char *devfn_string, const char *opts); /* vmchannel devices */ diff --git a/qemu/vl.c b/qemu/vl.c index 16be2c61..74183627 100644 --- a/qemu/vl.c +++ b/qemu/vl.c @@ -7610,6 +7610,7 @@ void qemu_bh_delete(QEMUBH *bh) /* machine registration */ QEMUMachine *first_machine = NULL; +QEMUMachine *current_machine = NULL; int qemu_register_machine(QEMUMachine *m) { @@ -9767,6 +9768,8 @@ int main(int argc, char **argv) machine->init(ram_size, vga_ram_size, boot_devices, ds, kernel_filename, kernel_cmdline, initrd_filename, cpu_model); + current_machine = machine; + /* init USB devices */ if (usb_enabled) { for(i = 0; i < usb_devices_index; i++) { -- 2.11.4.GIT