All of lore.kernel.org
 help / color / mirror / Atom feed
From: Marcelo Tosatti <mtosatti@redhat.com>
To: qemu-devel@nongnu.org
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Subject: [Qemu-devel] [patch 18/18] qemu: PCI device, disk and host network hot-add / hot-remove
Date: Wed, 04 Feb 2009 11:33:21 -0200	[thread overview]
Message-ID: <20090204133924.976199377@localhost.localdomain> (raw)
In-Reply-To: 20090204133303.113145633@localhost.localdomain

[-- Attachment #1: dev-and-disk-hotadd --]
[-- Type: text/plain, Size: 13181 bytes --]

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.
    
Add monitor command to hot-remove devices, remove device data on _EJ0 notification.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>


Index: trunk/Makefile.target
===================================================================
--- trunk.orig/Makefile.target
+++ trunk/Makefile.target
@@ -580,6 +580,7 @@ OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND
 OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
 OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
 OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
+OBJS += device-hotplug.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
Index: trunk/hw/boards.h
===================================================================
--- trunk.orig/hw/boards.h
+++ trunk/hw/boards.h
@@ -25,6 +25,8 @@ typedef struct QEMUMachine {
 int qemu_register_machine(QEMUMachine *m);
 void register_machines(void);
 
+extern QEMUMachine *current_machine;
+
 /* Axis ETRAX.  */
 extern QEMUMachine bareetraxfs_machine;
 extern QEMUMachine axisdev88_machine;
Index: trunk/hw/device-hotplug.c
===================================================================
--- /dev/null
+++ trunk/hw/device-hotplug.c
@@ -0,0 +1,237 @@
+#include "hw.h"
+#include "boards.h"
+#include "pci.h"
+#include "net.h"
+#include "sysemu.h"
+#include "pc.h"
+#include "console.h"
+#include "block_int.h"
+#include "virtio-blk.h"
+
+#define PCI_BASE_CLASS_STORAGE          0x01
+#define PCI_BASE_CLASS_NETWORK          0x02
+
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+static PCIDevice *qemu_system_hot_add_nic(const char *opts, int bus_nr)
+{
+    int ret;
+    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;
+    }
+
+    ret = net_client_init ("nic", opts);
+    if (ret < 0 || !nd_table[ret].model)
+        return NULL;
+    return pci_nic_init (pci_bus, &nd_table[ret], -1, "rtl8139");
+}
+
+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 (opaque && 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, 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(pcibus, 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);
+}
+#endif
+
+void device_hot_remove(int pcibus, int slot)
+{
+    PCIDevice *d = pci_find_device(pcibus, slot);
+
+    if (!d) {
+        term_printf("invalid slot %d\n", slot);
+        return;
+    }
+
+    qemu_system_device_hot_add(pcibus, slot, 0);
+}
+
+static void destroy_nic(int slot)
+{
+    int i;
+    PCIDevice *dev;
+
+    for (i = 0; i < MAX_NICS; i++)
+        if (nd_table[i].used) {
+            dev = nd_table[i].private;
+            if (dev && PCI_SLOT(dev->devfn) == slot) {
+                if (nd_table[i].vlan) {
+                    VLANClientState *vc;
+                    vc = qemu_find_vlan_client(nd_table[i].vlan,
+                                               nd_table[i].private);
+                    if (vc)
+                        qemu_del_vlan_client(vc);
+                }
+                net_client_uninit(&nd_table[i]);
+            }
+        }
+}
+
+static void destroy_bdrvs(int slot)
+{
+    int i;
+    struct BlockDriverState *bs;
+    PCIDevice *dev;
+
+    for (i = 0; i <= MAX_DRIVES; i++) {
+        bs = drives_table[i].bdrv;
+        if (bs) {
+            dev = bs->private;
+            if (dev && PCI_SLOT(dev->devfn) == slot) {
+                drive_uninit(bs);
+                bdrv_delete(bs);
+            }
+        }
+    }
+}
+
+/*
+ * OS has executed _EJ0 method, we now can remove the device
+ */
+void device_hot_remove_success(int pcibus, int slot)
+{
+    PCIDevice *d = pci_find_device(pcibus, slot);
+    int class_code;
+
+    if (!d) {
+        term_printf("invalid slot %d\n", slot);
+        return;
+    }
+
+    class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1);
+
+    pci_unregister_device(d);
+
+    switch(class_code) {
+    case PCI_BASE_CLASS_STORAGE:
+        destroy_bdrvs(slot);
+        break;
+    case PCI_BASE_CLASS_NETWORK:
+        destroy_nic(slot);
+        break;
+    }
+
+}
+
Index: trunk/monitor.c
===================================================================
--- trunk.orig/monitor.c
+++ trunk/monitor.c
@@ -1515,6 +1515,19 @@ static const term_cmd_t term_cmds[] = {
       "", "cancel the current VM migration" },
     { "migrate_set_speed", "s", do_migrate_set_speed,
       "value", "set maximum speed (in bytes) for migrations" },
+#if defined(TARGET_I386)
+    { "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" },
+    { "pci_del", "ii", device_hot_remove, "bus slot-number", "hot remove PCI device" },
+    { "host_net_add", "ss", net_host_device_add,
+      "[tap,user,socket,vde] options", "add host VLAN client" },
+    { "host_net_remove", "is", net_host_device_remove,
+      "vlan_id name", "remove host VLAN client" },
+#endif
     { "balloon", "i", do_balloon,
       "target", "request VM to change it's memory allocation (in MB)" },
     { "set_link", "ss", do_set_link, "name [up|down]" },
Index: trunk/sysemu.h
===================================================================
--- trunk.orig/sysemu.h
+++ trunk/sysemu.h
@@ -170,6 +170,12 @@ extern int drive_init(struct drive_opt *
 void qemu_system_hot_add_init(void);
 void qemu_system_device_hot_add(int pcibus, 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);
+void device_hot_remove(int pcibus, int slot);
+void device_hot_remove_success(int pcibus, int slot);
+
 /* serial ports */
 
 #define MAX_SERIAL_PORTS 4
Index: trunk/vl.c
===================================================================
--- trunk.orig/vl.c
+++ trunk/vl.c
@@ -3419,6 +3419,7 @@ static void qemu_bh_update_timeout(int *
 /* machine registration */
 
 static QEMUMachine *first_machine = NULL;
+QEMUMachine *current_machine = NULL;
 
 int qemu_register_machine(QEMUMachine *m)
 {
@@ -5603,6 +5604,8 @@ int main(int argc, char **argv, char **e
     machine->init(ram_size, vga_ram_size, boot_devices,
                   kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
 
+    current_machine = machine;
+
     /* Set KVM's vcpu state to qemu's initial CPUState. */
     if (kvm_enabled()) {
         int ret;
Index: trunk/hw/acpi.c
===================================================================
--- trunk.orig/hw/acpi.c
+++ trunk/hw/acpi.c
@@ -679,8 +679,12 @@ static uint32_t pciej_read(void *opaque,
 
 static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
 {
+#if defined (TARGET_I386)
     int slot = ffs(val) - 1;
 
+    device_hot_remove_success(0, slot);
+#endif
+
 #if defined(DEBUG)
     printf("pciej write %lx <== %d\n", addr, val);
 #endif
Index: trunk/net.c
===================================================================
--- trunk.orig/net.c
+++ trunk/net.c
@@ -1748,6 +1748,59 @@ void net_client_uninit(NICInfo *nd)
     free((void *)nd->model);
 }
 
+static int net_host_check_device(const char *device)
+{
+    int i;
+    const char *valid_param_list[] = { "tap", "user", "socket"
+#ifdef CONFIG_VDE
+                                       ,"vde"
+#endif
+    };
+    for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
+        if (!strncmp(valid_param_list[i], device,
+                     strlen(valid_param_list[i])))
+            return 1;
+    }
+
+    return 0;
+}
+
+void net_host_device_add(const char *device, const char *opts)
+{
+    if (!net_host_check_device(device)) {
+        term_printf("invalid host network device %s\n", device);
+        return;
+    }
+    net_client_init(device, opts);
+}
+
+void net_host_device_remove(int vlan_id, const char *device)
+{
+    VLANState *vlan;
+    VLANClientState *vc;
+
+    if (!net_host_check_device(device)) {
+        term_printf("invalid host network device %s\n", device);
+        return;
+    }
+
+    vlan = qemu_find_vlan(vlan_id);
+    if (!vlan) {
+        term_printf("can't find vlan %d\n", vlan_id);
+        return;
+    }
+
+   for(vc = vlan->first_client; vc != NULL; vc = vc->next)
+        if (!strcmp(vc->name, device))
+            break;
+
+    if (!vc) {
+        term_printf("can't find device %s\n", device);
+        return;
+    }
+    qemu_del_vlan_client(vc);
+}
+
 int net_client_parse(const char *str)
 {
     const char *p;
Index: trunk/net.h
===================================================================
--- trunk.orig/net.h
+++ trunk/net.h
@@ -102,6 +102,8 @@ void net_slirp_redir(const char *redir_s
 void net_cleanup(void);
 int slirp_is_inited(void);
 void net_client_check(void);
+void net_host_device_add(const char *device, const char *opts);
+void net_host_device_remove(int vlan_id, const char *device);
 
 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
 #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"

-- 

  parent reply	other threads:[~2009-02-04 13:46 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-04 13:33 [Qemu-devel] [patch 00/18] acpi pci hotplug support Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 01/18] qemu: add pci helper functions Marcelo Tosatti
2009-02-04 14:56   ` Paul Brook
2009-02-04 19:15     ` Anthony Liguori
2009-02-06 17:35       ` Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 02/18] qemu: return PCIDevice on net device init and record devfn Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 03/18] qemu: dynamic drive/drive_opt index allocation Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 04/18] qemu: dynamic nic info " Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 05/18] qemu: drive removal support Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 06/18] qemu: record devfn on block driver instance Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 07/18] qemu: move drives_opt for external use Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 08/18] qemu: net/drive add/remove tweaks Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 09/18] qemu: add net_client_uninit / qemu_find_vlan_client Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 10/18] qemu: add cpu_unregister_io_memory and make io mem table index dynamic Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 11/18] qemu: add qemu_free_irqs Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 12/18] qemu: add pci_unregister_device Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 13/18] qemu: warn if PCI region is not power of two Marcelo Tosatti
2009-02-04 14:38   ` Paul Brook
2009-02-06 17:34     ` Marcelo Tosatti
2009-02-08  6:08       ` malc
2009-02-08 17:20         ` Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 14/18] qemu: LSI SCSI and e1000 unregister callbacks Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 15/18] qemu: zero ioport_opaque on isa_unassign_ioport Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 16/18] qemu: initialize hot add system / acpi gpe Marcelo Tosatti
2009-02-04 13:33 ` [Qemu-devel] [patch 17/18] qemu: pci hotplug GPE support Marcelo Tosatti
2009-02-04 13:33 ` Marcelo Tosatti [this message]
2009-02-04 14:38   ` [Qemu-devel] [patch 18/18] qemu: PCI device, disk and host network hot-add / hot-remove Daniel P. Berrange
2009-02-04 15:21     ` Anthony Liguori
2009-02-04 15:37       ` Daniel P. Berrange
2009-02-04 15:59       ` Avi Kivity
2009-02-04 16:46         ` Anthony Liguori
2009-02-04 14:59   ` Paul Brook
2009-02-04 16:17   ` Markus Armbruster
2009-02-04 17:47   ` Blue Swirl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090204133924.976199377@localhost.localdomain \
    --to=mtosatti@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.