Commit 6f338c3469297d748be33aea864161e36e068436

Authored by aliguori
1 parent 880345c4

qemu: PCI device, disk and host network hot-add / hot-remove (Marcelo Tosatti)

Add monitor command to hot-add PCI devices (nic and storage).
    
Syntax is:
    
pci_add pci_addr=[[<domain>:]<bus>:]<slot> nic|storage params
    
It returns the domain, bus and slot 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.
    
Add monitor command to hot-remove devices, remove device data on _EJ0 notification.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6610 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile.target
... ... @@ -583,6 +583,7 @@ OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
583 583 OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
584 584 OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
585 585 OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
  586 +OBJS += device-hotplug.o pci-hotplug.o
586 587 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
587 588 endif
588 589 ifeq ($(TARGET_BASE_ARCH), ppc)
... ...
hw/acpi.c
... ... @@ -679,8 +679,12 @@ static uint32_t pciej_read(void *opaque, uint32_t addr)
679 679  
680 680 static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
681 681 {
  682 +#if defined (TARGET_I386)
682 683 int slot = ffs(val) - 1;
683 684  
  685 + pci_device_hot_remove_success(0, slot);
  686 +#endif
  687 +
684 688 #if defined(DEBUG)
685 689 printf("pciej write %lx <== %d\n", addr, val);
686 690 #endif
... ...
hw/boards.h
... ... @@ -25,6 +25,8 @@ typedef struct QEMUMachine {
25 25 int qemu_register_machine(QEMUMachine *m);
26 26 void register_machines(void);
27 27  
  28 +extern QEMUMachine *current_machine;
  29 +
28 30 /* Axis ETRAX. */
29 31 extern QEMUMachine bareetraxfs_machine;
30 32 extern QEMUMachine axisdev88_machine;
... ...
hw/device-hotplug.c 0 → 100644
  1 +/*
  2 + * QEMU device hotplug helpers
  3 + *
  4 + * Copyright (c) 2004 Fabrice Bellard
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include "hw.h"
  26 +#include "boards.h"
  27 +#include "net.h"
  28 +#include "block_int.h"
  29 +#include "sysemu.h"
  30 +
  31 +int add_init_drive(const char *opts)
  32 +{
  33 + int drive_opt_idx, drive_idx;
  34 + int ret = -1;
  35 +
  36 + drive_opt_idx = drive_add(NULL, "%s", opts);
  37 + if (!drive_opt_idx)
  38 + return ret;
  39 +
  40 + drive_idx = drive_init(&drives_opt[drive_opt_idx], 0, current_machine);
  41 + if (drive_idx == -1) {
  42 + drive_remove(drive_opt_idx);
  43 + return ret;
  44 + }
  45 +
  46 + return drive_idx;
  47 +}
  48 +
  49 +void destroy_nic(dev_match_fn *match_fn, void *arg)
  50 +{
  51 + int i;
  52 + NICInfo *nic;
  53 +
  54 + for (i = 0; i < MAX_NICS; i++)
  55 + nic = &nd_table[i];
  56 + if (nic->used) {
  57 + if (nic->private && match_fn(nic->private, arg)) {
  58 + if (nic->vlan) {
  59 + VLANClientState *vc;
  60 + vc = qemu_find_vlan_client(nic->vlan, nic->private);
  61 + if (vc)
  62 + qemu_del_vlan_client(vc);
  63 + }
  64 + net_client_uninit(nic);
  65 + }
  66 + }
  67 +}
  68 +
  69 +void destroy_bdrvs(dev_match_fn *match_fn, void *arg)
  70 +{
  71 + int i;
  72 + struct BlockDriverState *bs;
  73 +
  74 + for (i = 0; i <= MAX_DRIVES; i++) {
  75 + bs = drives_table[i].bdrv;
  76 + if (bs) {
  77 + if (bs->private && match_fn(bs->private, arg)) {
  78 + drive_uninit(bs);
  79 + bdrv_delete(bs);
  80 + }
  81 + }
  82 + }
  83 +}
  84 +
  85 +
... ...
hw/pci-hotplug.c 0 → 100644
  1 +/*
  2 + * QEMU PCI hotplug support
  3 + *
  4 + * Copyright (c) 2004 Fabrice Bellard
  5 + *
  6 + * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 + * of this software and associated documentation files (the "Software"), to deal
  8 + * in the Software without restriction, including without limitation the rights
  9 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 + * copies of the Software, and to permit persons to whom the Software is
  11 + * furnished to do so, subject to the following conditions:
  12 + *
  13 + * The above copyright notice and this permission notice shall be included in
  14 + * all copies or substantial portions of the Software.
  15 + *
  16 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 + * THE SOFTWARE.
  23 + */
  24 +
  25 +#include "hw.h"
  26 +#include "boards.h"
  27 +#include "pci.h"
  28 +#include "net.h"
  29 +#include "sysemu.h"
  30 +#include "pc.h"
  31 +#include "console.h"
  32 +#include "block_int.h"
  33 +#include "virtio-blk.h"
  34 +
  35 +#if defined(TARGET_I386) || defined(TARGET_X86_64)
  36 +static PCIDevice *qemu_pci_hot_add_nic(PCIBus *pci_bus, const char *opts)
  37 +{
  38 + int ret;
  39 +
  40 + ret = net_client_init ("nic", opts);
  41 + if (ret < 0 || !nd_table[ret].model)
  42 + return NULL;
  43 + return pci_nic_init (pci_bus, &nd_table[ret], -1, "rtl8139");
  44 +}
  45 +
  46 +void drive_hot_add(const char *pci_addr, const char *opts)
  47 +{
  48 + int dom, pci_bus;
  49 + unsigned slot;
  50 + int drive_idx, type, bus;
  51 + int success = 0;
  52 + PCIDevice *dev;
  53 +
  54 + if (pci_read_devaddr(pci_addr, &dom, &pci_bus, &slot)) {
  55 + term_printf("Invalid pci address\n");
  56 + return;
  57 + }
  58 +
  59 + dev = pci_find_device(pci_bus, slot, 0);
  60 + if (!dev) {
  61 + term_printf("no pci device with address %s\n", pci_addr);
  62 + return;
  63 + }
  64 +
  65 + drive_idx = add_init_drive(opts);
  66 + if (drive_idx < 0)
  67 + return;
  68 + type = drives_table[drive_idx].type;
  69 + bus = drive_get_max_bus (type);
  70 +
  71 + switch (type) {
  72 + case IF_SCSI:
  73 + success = 1;
  74 + lsi_scsi_attach (dev, drives_table[drive_idx].bdrv,
  75 + drives_table[drive_idx].unit);
  76 + break;
  77 + default:
  78 + term_printf("Can't hot-add drive to type %d\n", type);
  79 + }
  80 +
  81 + if (success)
  82 + term_printf("OK bus %d, unit %d\n", drives_table[drive_idx].bus,
  83 + drives_table[drive_idx].unit);
  84 + return;
  85 +}
  86 +
  87 +static PCIDevice *qemu_pci_hot_add_storage(PCIBus *pci_bus, const char *opts)
  88 +{
  89 + void *opaque = NULL;
  90 + int type = -1, drive_idx = -1;
  91 + char buf[128];
  92 +
  93 + if (get_param_value(buf, sizeof(buf), "if", opts)) {
  94 + if (!strcmp(buf, "scsi"))
  95 + type = IF_SCSI;
  96 + else if (!strcmp(buf, "virtio")) {
  97 + type = IF_VIRTIO;
  98 + }
  99 + } else {
  100 + term_printf("no if= specified\n");
  101 + return NULL;
  102 + }
  103 +
  104 + if (get_param_value(buf, sizeof(buf), "file", opts)) {
  105 + drive_idx = add_init_drive(opts);
  106 + if (drive_idx < 0)
  107 + return NULL;
  108 + } else if (type == IF_VIRTIO) {
  109 + term_printf("virtio requires a backing file/device.\n");
  110 + return NULL;
  111 + }
  112 +
  113 + switch (type) {
  114 + case IF_SCSI:
  115 + opaque = lsi_scsi_init (pci_bus, -1);
  116 + if (opaque && drive_idx >= 0)
  117 + lsi_scsi_attach (opaque, drives_table[drive_idx].bdrv,
  118 + drives_table[drive_idx].unit);
  119 + break;
  120 + case IF_VIRTIO:
  121 + opaque = virtio_blk_init (pci_bus, drives_table[drive_idx].bdrv);
  122 + break;
  123 + default:
  124 + term_printf ("type %s not a hotpluggable PCI device.\n", buf);
  125 + }
  126 +
  127 + return opaque;
  128 +}
  129 +
  130 +void pci_device_hot_add(const char *pci_addr, const char *type, const char *opts)
  131 +{
  132 + PCIDevice *dev = NULL;
  133 + PCIBus *pci_bus;
  134 + int dom, bus;
  135 + unsigned slot;
  136 +
  137 + if (pci_assign_devaddr(pci_addr, &dom, &bus, &slot)) {
  138 + term_printf("Invalid pci address\n");
  139 + return;
  140 + }
  141 +
  142 + pci_bus = pci_find_bus(bus);
  143 + if (!pci_bus) {
  144 + term_printf("Can't find pci_bus %d\n", bus);
  145 + return;
  146 + }
  147 +
  148 + if (strcmp(type, "nic") == 0)
  149 + dev = qemu_pci_hot_add_nic(pci_bus, opts);
  150 + else if (strcmp(type, "storage") == 0)
  151 + dev = qemu_pci_hot_add_storage(pci_bus, opts);
  152 + else
  153 + term_printf("invalid type: %s\n", type);
  154 +
  155 + if (dev) {
  156 + qemu_system_device_hot_add(bus, PCI_SLOT(dev->devfn), 1);
  157 + term_printf("OK domain %d, bus %d, slot %d, function %d\n",
  158 + 0, pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
  159 + PCI_FUNC(dev->devfn));
  160 + } else
  161 + term_printf("failed to add %s\n", opts);
  162 +}
  163 +#endif
  164 +
  165 +void pci_device_hot_remove(const char *pci_addr)
  166 +{
  167 + PCIDevice *d;
  168 + int dom, bus;
  169 + unsigned slot;
  170 +
  171 + if (pci_read_devaddr(pci_addr, &dom, &bus, &slot)) {
  172 + term_printf("Invalid pci address\n");
  173 + return;
  174 + }
  175 +
  176 + d = pci_find_device(bus, slot, 0);
  177 + if (!d) {
  178 + term_printf("slot %d empty\n", slot);
  179 + return;
  180 + }
  181 +
  182 + qemu_system_device_hot_add(bus, slot, 0);
  183 +}
  184 +
  185 +static int pci_match_fn(void *dev_private, void *arg)
  186 +{
  187 + PCIDevice *dev = dev_private;
  188 + PCIDevice *match = arg;
  189 +
  190 + return (dev == match);
  191 +}
  192 +
  193 +/*
  194 + * OS has executed _EJ0 method, we now can remove the device
  195 + */
  196 +void pci_device_hot_remove_success(int pcibus, int slot)
  197 +{
  198 + PCIDevice *d = pci_find_device(pcibus, slot, 0);
  199 + int class_code;
  200 +
  201 + if (!d) {
  202 + term_printf("invalid slot %d\n", slot);
  203 + return;
  204 + }
  205 +
  206 + class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1);
  207 +
  208 + switch(class_code) {
  209 + case PCI_BASE_CLASS_STORAGE:
  210 + destroy_bdrvs(pci_match_fn, d);
  211 + break;
  212 + case PCI_BASE_CLASS_NETWORK:
  213 + destroy_nic(pci_match_fn, d);
  214 + break;
  215 + }
  216 +
  217 + pci_unregister_device(d);
  218 +}
  219 +
... ...
hw/pci.h
... ... @@ -14,6 +14,9 @@ extern target_phys_addr_t pci_mem_base;
14 14  
15 15 /* Device classes and subclasses */
16 16  
  17 +#define PCI_BASE_CLASS_STORAGE 0x01
  18 +#define PCI_BASE_CLASS_NETWORK 0x02
  19 +
17 20 #define PCI_CLASS_STORAGE_SCSI 0x0100
18 21 #define PCI_CLASS_STORAGE_IDE 0x0101
19 22 #define PCI_CLASS_STORAGE_OTHER 0x0180
... ...
monitor.c
... ... @@ -1511,6 +1511,20 @@ static const term_cmd_t term_cmds[] = {
1511 1511 "", "cancel the current VM migration" },
1512 1512 { "migrate_set_speed", "s", do_migrate_set_speed,
1513 1513 "value", "set maximum speed (in bytes) for migrations" },
  1514 +#if defined(TARGET_I386)
  1515 + { "drive_add", "ss", drive_hot_add, "pci_addr=[[<domain>:]<bus>:]<slot>\n"
  1516 + "[file=file][,if=type][,bus=n]\n"
  1517 + "[,unit=m][,media=d][index=i]\n"
  1518 + "[,cyls=c,heads=h,secs=s[,trans=t]]\n"
  1519 + "[snapshot=on|off][,cache=on|off]",
  1520 + "add drive to PCI storage controller" },
  1521 + { "pci_add", "sss", pci_device_hot_add, "pci_addr=auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" },
  1522 + { "pci_del", "s", pci_device_hot_remove, "pci_addr=[[<domain>:]<bus>:]<slot>", "hot remove PCI device" },
  1523 + { "host_net_add", "ss", net_host_device_add,
  1524 + "[tap,user,socket,vde] options", "add host VLAN client" },
  1525 + { "host_net_remove", "is", net_host_device_remove,
  1526 + "vlan_id name", "remove host VLAN client" },
  1527 +#endif
1514 1528 { "balloon", "i", do_balloon,
1515 1529 "target", "request VM to change it's memory allocation (in MB)" },
1516 1530 { "set_link", "ss", do_set_link,
... ...
... ... @@ -1734,6 +1734,62 @@ void net_client_uninit(NICInfo *nd)
1734 1734 free((void *)nd->model);
1735 1735 }
1736 1736  
  1737 +static int net_host_check_device(const char *device)
  1738 +{
  1739 + int i;
  1740 + const char *valid_param_list[] = { "tap", "socket"
  1741 +#ifdef CONFIG_SLIRP
  1742 + ,"user"
  1743 +#endif
  1744 +#ifdef CONFIG_VDE
  1745 + ,"vde"
  1746 +#endif
  1747 + };
  1748 + for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
  1749 + if (!strncmp(valid_param_list[i], device,
  1750 + strlen(valid_param_list[i])))
  1751 + return 1;
  1752 + }
  1753 +
  1754 + return 0;
  1755 +}
  1756 +
  1757 +void net_host_device_add(const char *device, const char *opts)
  1758 +{
  1759 + if (!net_host_check_device(device)) {
  1760 + term_printf("invalid host network device %s\n", device);
  1761 + return;
  1762 + }
  1763 + net_client_init(device, opts);
  1764 +}
  1765 +
  1766 +void net_host_device_remove(int vlan_id, const char *device)
  1767 +{
  1768 + VLANState *vlan;
  1769 + VLANClientState *vc;
  1770 +
  1771 + if (!net_host_check_device(device)) {
  1772 + term_printf("invalid host network device %s\n", device);
  1773 + return;
  1774 + }
  1775 +
  1776 + vlan = qemu_find_vlan(vlan_id);
  1777 + if (!vlan) {
  1778 + term_printf("can't find vlan %d\n", vlan_id);
  1779 + return;
  1780 + }
  1781 +
  1782 + for(vc = vlan->first_client; vc != NULL; vc = vc->next)
  1783 + if (!strcmp(vc->name, device))
  1784 + break;
  1785 +
  1786 + if (!vc) {
  1787 + term_printf("can't find device %s\n", device);
  1788 + return;
  1789 + }
  1790 + qemu_del_vlan_client(vc);
  1791 +}
  1792 +
1737 1793 int net_client_parse(const char *str)
1738 1794 {
1739 1795 const char *p;
... ...
... ... @@ -102,6 +102,8 @@ void net_slirp_redir(const char *redir_str);
102 102 void net_cleanup(void);
103 103 int slirp_is_inited(void);
104 104 void net_client_check(void);
  105 +void net_host_device_add(const char *device, const char *opts);
  106 +void net_host_device_remove(int vlan_id, const char *device);
105 107  
106 108 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
107 109 #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
... ...
sysemu.h
... ... @@ -170,6 +170,20 @@ extern int drive_init(struct drive_opt *arg, int snapshot, void *machine);
170 170 void qemu_system_hot_add_init(void);
171 171 void qemu_system_device_hot_add(int pcibus, int slot, int state);
172 172  
  173 +/* device-hotplug */
  174 +
  175 +typedef int (dev_match_fn)(void *dev_private, void *arg);
  176 +
  177 +int add_init_drive(const char *opts);
  178 +void destroy_nic(dev_match_fn *match_fn, void *arg);
  179 +void destroy_bdrvs(dev_match_fn *match_fn, void *arg);
  180 +
  181 +/* pci-hotplug */
  182 +void pci_device_hot_add(const char *pci_addr, const char *type, const char *opts);
  183 +void drive_hot_add(const char *pci_addr, const char *opts);
  184 +void pci_device_hot_remove(const char *pci_addr);
  185 +void pci_device_hot_remove_success(int pcibus, int slot);
  186 +
173 187 /* serial ports */
174 188  
175 189 #define MAX_SERIAL_PORTS 4
... ...
... ... @@ -3405,6 +3405,7 @@ static void qemu_bh_update_timeout(int *timeout)
3405 3405 /* machine registration */
3406 3406  
3407 3407 static QEMUMachine *first_machine = NULL;
  3408 +QEMUMachine *current_machine = NULL;
3408 3409  
3409 3410 int qemu_register_machine(QEMUMachine *m)
3410 3411 {
... ... @@ -5587,6 +5588,8 @@ int main(int argc, char **argv, char **envp)
5587 5588 machine->init(ram_size, vga_ram_size, boot_devices,
5588 5589 kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
5589 5590  
  5591 + current_machine = machine;
  5592 +
5590 5593 /* Set KVM's vcpu state to qemu's initial CPUState. */
5591 5594 if (kvm_enabled()) {
5592 5595 int ret;
... ...