Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 3160499aacb81f6735941eb4c372d87a > files > 81

kvm-83-164.el5_5.30.src.rpm

From 0d007443a048216f95fdcf5105b6e715a2aa5062 Mon Sep 17 00:00:00 2001
From: Marcelo Tosatti <mtosatti@redhat.com>
Date: Thu, 19 Feb 2009 21:28:10 -0300
Subject: [PATCH 2/3] Backport upstream QEMU PCI hotplug code

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Bugzilla: 489992
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Acked-by: Glauber Costa <glommer@redhat.com>
---
 qemu/Makefile.target     |    1 +
 qemu/block_int.h         |    3 +-
 qemu/hw/acpi.c           |    2 +-
 qemu/hw/device-hotplug.c |  274 ++++++++--------------------------------------
 qemu/hw/ide.c            |    2 +-
 qemu/hw/lsi53c895a.c     |    2 +-
 qemu/hw/pci-hotplug.c    |  254 ++++++++++++++++++++++++++++++++++++++++++
 qemu/hw/pci.c            |    2 +-
 qemu/hw/pci.h            |    3 +
 qemu/hw/virtio-blk.c     |    2 +-
 qemu/monitor.c           |   11 ++-
 qemu/net.c               |   89 ++++++++++++++-
 qemu/net.h               |    5 +-
 qemu/sysemu.h            |   16 ++-
 14 files changed, 421 insertions(+), 245 deletions(-)
 create mode 100644 qemu/hw/pci-hotplug.c

diff --git a/qemu/Makefile.target b/qemu/Makefile.target
index eaec68e..2a94634 100644
--- a/qemu/Makefile.target
+++ b/qemu/Makefile.target
@@ -703,6 +703,7 @@ OBJS += e1000.o
 OBJS+= hypercall.o
 
 OBJS += device-hotplug.o
+OBJS += pci-hotplug.o
 
 ifeq ($(USE_KVM_DEVICE_ASSIGNMENT), 1)
 OBJS+= device-assignment.o
diff --git a/qemu/block_int.h b/qemu/block_int.h
index 695452d..50b57ab 100644
--- a/qemu/block_int.h
+++ b/qemu/block_int.h
@@ -133,8 +133,7 @@ struct BlockDriverState {
     int cyls, heads, secs, translation;
     int type;
     char device_name[32];
-    /* PCI devfn of parent */
-    int devfn;
+    void *private;
     BlockDriverState *next;
 };
 
diff --git a/qemu/hw/acpi.c b/qemu/hw/acpi.c
index 68c1adf..b05bef0 100644
--- a/qemu/hw/acpi.c
+++ b/qemu/hw/acpi.c
@@ -721,7 +721,7 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
 {
     int slot = ffs(val) - 1;
 
-    device_hot_remove_success(0, slot);
+    pci_device_hot_remove_success(0, slot);
 
 #if defined(DEBUG)
     printf("pciej write %x <== %d\n", addr, val);
diff --git a/qemu/hw/device-hotplug.c b/qemu/hw/device-hotplug.c
index 56172e1..3bdc048 100644
--- a/qemu/hw/device-hotplug.c
+++ b/qemu/hw/device-hotplug.c
@@ -1,71 +1,34 @@
+/*
+ * QEMU device hotplug helpers
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #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 "device-assignment.h"
-#include "config.h"
-
-#define PCI_BASE_CLASS_STORAGE          0x01
-#define PCI_BASE_CLASS_NETWORK          0x02
-
-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");
-}
-
-#ifdef USE_KVM_DEVICE_ASSIGNMENT
-static PCIDevice *qemu_system_hot_assign_device(const char *opts, int bus_nr)
-{
-    PCIBus *pci_bus;
-    AssignedDevInfo *adev;
-    PCIDevice *ret;
-
-    pci_bus = pci_find_bus(bus_nr);
-    if (!pci_bus) {
-        term_printf ("Can't find pci_bus %d\n", bus_nr);
-        return NULL;
-    }
-    adev = add_assigned_device(opts);
-    if (adev == NULL) {
-        term_printf ("Error adding device; check syntax\n");
-        return NULL;
-    }
- 
-    ret = init_assigned_device(adev, pci_bus);
-    if (ret == NULL) {
-        term_printf("Failed to assign device\n");
-        free_assigned_device(adev);
-        return NULL;
-    }
-
-    term_printf("Registered host PCI device %02x:%02x.%1x "
-		"(\"%s\") as guest device %02x:%02x.%1x\n",
-		adev->bus, adev->dev, adev->func, adev->name,
-		pci_bus_num(pci_bus), (ret->devfn >> 3) & 0x1f,
-		adev->func);
-
-    return ret;
-}
-
-#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+#include "sysemu.h"
 
-static int add_init_drive(const char *opts)
+int add_init_drive(const char *opts)
 {
     int drive_opt_idx, drive_idx;
     int ret = -1;
@@ -83,182 +46,41 @@ static int add_init_drive(const char *opts)
     return drive_idx;
 }
 
-void drive_hot_add(int pcibus, const char *devfn_string, const char *opts)
+void destroy_nic(dev_match_fn *match_fn, void *arg)
 {
-    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;
+    int i;
+    NICInfo *nic;
+
+    for (i = 0; i < MAX_NICS; i++) {
+        nic = &nd_table[i];
+        if (nic->used) {
+            if (nic->private && match_fn(nic->private, arg)) {
+                if (nic->vlan) {
+                    VLANClientState *vc;
+                    vc = qemu_find_vlan_client(nic->vlan, nic->private);
+                    if (vc)
+                        qemu_del_vlan_client(vc);
+                }
+                net_client_uninit(nic);
+            }
         }
-    } 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, 0x1AF4, 0x1001,
-                                  drives_table[drive_idx].bdrv);
-        break;
-    default:
-        term_printf ("type %s not a hotpluggable PCI device.\n", buf);
-    }
-
-    return opaque;
-}
-
-#if defined(TARGET_I386) || defined(TARGET_X86_64)
-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);
-#ifdef USE_KVM_DEVICE_ASSIGNMENT
-    else if (strcmp(type, "host") == 0)
-        dev = qemu_system_hot_assign_device(opts, pcibus);
-#endif /* USE_KVM_DEVICE_ASSIGNMENT */
-    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);
-}
-
-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);
 }
-#endif
 
-static void destroy_nic(int slot)
-{
-    int i;
-
-    for (i = 0; i < MAX_NICS; i++)
-        if (nd_table[i].used &&
-            PCI_SLOT(nd_table[i].devfn) == slot)
-                net_client_uninit(&nd_table[i]);
-}
-
-static void destroy_bdrvs(int slot)
+void destroy_bdrvs(dev_match_fn *match_fn, void *arg)
 {
     int i;
     struct BlockDriverState *bs;
 
     for (i = 0; i <= MAX_DRIVES; i++) {
         bs = drives_table[i].bdrv;
-        if (bs && (PCI_SLOT(bs->devfn) == slot)) {
-            drive_uninit(bs);
-            bdrv_delete(bs);
+        if (bs) {
+            if (bs->private && match_fn(bs->private, arg)) {
+                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;
-    }
-
-}
 
diff --git a/qemu/hw/ide.c b/qemu/hw/ide.c
index a312a94..4e1020d 100644
--- a/qemu/hw/ide.c
+++ b/qemu/hw/ide.c
@@ -3360,7 +3360,7 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
 
     for (i = 0; i < 4; i++)
         if (hd_table[i])
-            hd_table[i]->devfn = d->dev.devfn;
+            hd_table[i]->private = &d->dev;
 
     register_savevm("ide", 0, 2, pci_ide_save, pci_ide_load, d);
 }
diff --git a/qemu/hw/lsi53c895a.c b/qemu/hw/lsi53c895a.c
index c65ff90..8f9fa72 100644
--- a/qemu/hw/lsi53c895a.c
+++ b/qemu/hw/lsi53c895a.c
@@ -1958,7 +1958,7 @@ void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id)
     s->scsi_dev[id] = scsi_generic_init(bd, 1, lsi_command_complete, s);
     if (s->scsi_dev[id] == NULL)
         s->scsi_dev[id] = scsi_disk_init(bd, 1, lsi_command_complete, s);
-    bd->devfn = s->pci_dev.devfn;
+    bd->private = &s->pci_dev;
 }
 
 int lsi_scsi_uninit(PCIDevice *d)
diff --git a/qemu/hw/pci-hotplug.c b/qemu/hw/pci-hotplug.c
new file mode 100644
index 0000000..fb1ffd2
--- /dev/null
+++ b/qemu/hw/pci-hotplug.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU PCI hotplug support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#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"
+#include "device-assignment.h"
+
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+static PCIDevice *qemu_pci_hot_add_nic(PCIBus *pci_bus, const char *opts)
+{
+    int ret;
+
+    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");
+}
+
+void drive_hot_add(const char *pci_addr, const char *opts)
+{
+    int dom, pci_bus;
+    unsigned slot;
+    int drive_idx, type, bus;
+    int success = 0;
+    PCIDevice *dev;
+
+    if (pci_read_devaddr(pci_addr, &dom, &pci_bus, &slot)) {
+        term_printf("Invalid pci address\n");
+        return;
+    }
+
+    dev = pci_find_device(pci_bus, slot);
+    if (!dev) {
+        term_printf("no pci device with address %s\n", pci_addr);
+        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_pci_hot_add_storage(PCIBus *pci_bus, const char *opts)
+{
+    void *opaque = NULL;
+    int type = -1, drive_idx = -1;
+    char buf[128];
+
+    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;
+}
+
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+static PCIDevice *qemu_pci_hot_assign_device(PCIBus *pci_bus, const char *opts)
+{
+    AssignedDevInfo *adev;
+    PCIDevice *ret;
+
+    adev = add_assigned_device(opts);
+    if (adev == NULL) {
+        term_printf ("Error adding device; check syntax\n");
+        return NULL;
+    }
+
+    ret = init_assigned_device(adev, pci_bus);
+    if (ret == NULL) {
+        term_printf("Failed to assign device\n");
+        free_assigned_device(adev);
+        return NULL;
+    }
+
+    term_printf("Registered host PCI device %02x:%02x.%1x "
+		"(\"%s\") as guest device %02x:%02x.%1x\n",
+		adev->bus, adev->dev, adev->func, adev->name,
+		pci_bus_num(pci_bus), (ret->devfn >> 3) & 0x1f,
+		adev->func);
+
+    return ret;
+}
+
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+
+void pci_device_hot_add(const char *pci_addr, const char *type, const char *opts)
+{
+    PCIDevice *dev = NULL;
+    PCIBus *pci_bus;
+    int dom, bus;
+    unsigned slot;
+
+    if (pci_assign_devaddr(pci_addr, &dom, &bus, &slot)) {
+        term_printf("Invalid pci address\n");
+        return;
+    }
+
+    pci_bus = pci_find_bus(bus);
+    if (!pci_bus) {
+        term_printf("Can't find pci_bus %d\n", bus);
+        return;
+    }
+
+    if (strcmp(type, "nic") == 0)
+        dev = qemu_pci_hot_add_nic(pci_bus, opts);
+    else if (strcmp(type, "storage") == 0)
+        dev = qemu_pci_hot_add_storage(pci_bus, opts);
+#ifdef USE_KVM_DEVICE_ASSIGNMENT
+    else if (strcmp(type, "host") == 0)
+        dev = qemu_pci_hot_assign_device(pci_bus, opts);
+#endif /* USE_KVM_DEVICE_ASSIGNMENT */
+    else
+        term_printf("invalid type: %s\n", type);
+
+    if (dev) {
+        qemu_system_device_hot_add(bus, PCI_SLOT(dev->devfn), 1);
+        term_printf("OK domain %d, bus %d, slot %d, function %d\n",
+                    0, pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
+                    PCI_FUNC(dev->devfn));
+    } else
+        term_printf("failed to add %s\n", opts);
+}
+#endif
+
+void pci_device_hot_remove(const char *pci_addr)
+{
+    PCIDevice *d;
+    int dom, bus;
+    unsigned slot;
+
+    if (pci_read_devaddr(pci_addr, &dom, &bus, &slot)) {
+        term_printf("Invalid pci address\n");
+        return;
+    }
+
+    d = pci_find_device(bus, slot);
+    if (!d) {
+        term_printf("slot %d empty\n", slot);
+        return;
+    }
+
+    qemu_system_device_hot_add(bus, slot, 0);
+}
+
+static int pci_match_fn(void *dev_private, void *arg)
+{
+    PCIDevice *dev = dev_private;
+    PCIDevice *match = arg;
+
+    return (dev == match);
+}
+
+/*
+ * OS has executed _EJ0 method, we now can remove the device
+ */
+void pci_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);
+
+    switch(class_code) {
+    case PCI_BASE_CLASS_STORAGE:
+        destroy_bdrvs(pci_match_fn, d);
+        break;
+    case PCI_BASE_CLASS_NETWORK:
+        destroy_nic(pci_match_fn, d);
+        break;
+    }
+
+    pci_unregister_device(d);
+}
+
diff --git a/qemu/hw/pci.c b/qemu/hw/pci.c
index 27bdb7c..a7afb73 100644
--- a/qemu/hw/pci.c
+++ b/qemu/hw/pci.c
@@ -831,7 +831,7 @@ PCIDevice *pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn,
         if (strcmp(nd->model, pci_nic_models[i]) == 0) {
             pci_dev = pci_nic_init_fns[i](bus, nd, devfn);
             if (pci_dev) {
-                nd->devfn = pci_dev->devfn;
+                nd->private = pci_dev;
             }
             return pci_dev;
         }
diff --git a/qemu/hw/pci.h b/qemu/hw/pci.h
index 5667f36..c28f4aa 100644
--- a/qemu/hw/pci.h
+++ b/qemu/hw/pci.h
@@ -11,6 +11,9 @@
 /* PCI bus */
 extern target_phys_addr_t pci_mem_base;
 
+#define PCI_BASE_CLASS_STORAGE           0x01
+#define PCI_BASE_CLASS_NETWORK           0x02
+
 /* see pci-ids.txt */
 #define PCI_VENDOR_ID_REDHAT_QUMRANET    0x1af4
 #define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4
diff --git a/qemu/hw/virtio-blk.c b/qemu/hw/virtio-blk.c
index ec27efb..4e44313 100644
--- a/qemu/hw/virtio-blk.c
+++ b/qemu/hw/virtio-blk.c
@@ -317,7 +317,7 @@ void *virtio_blk_init(PCIBus *bus, BlockDriverState *bs)
     s->vdev.get_features = virtio_blk_get_features;
     s->vdev.reset = virtio_blk_reset;
     s->bs = bs;
-    bs->devfn = s->vdev.pci_dev.devfn;
+    bs->private = &s->vdev.pci_dev;
     s->rq = NULL;
     bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
     bdrv_set_geometry_hint(s->bs, cylinders, heads, secs);
diff --git a/qemu/monitor.c b/qemu/monitor.c
index a8b1159..7022744 100644
--- a/qemu/monitor.c
+++ b/qemu/monitor.c
@@ -1605,13 +1605,18 @@ static term_cmd_t term_cmds[] = {
     { "set_link", "ss", do_set_link, "name [up|down]" },
     { "cpu_set", "is", do_cpu_set_nr, "cpu [online|offline]", "change cpu state" },
 #if defined(TARGET_I386) || defined(TARGET_X86_64)
-    { "drive_add", "iss", drive_hot_add, "pcibus pcidevfn [file=file][,if=type][,bus=n]\n"
+    { "drive_add", "ss", drive_hot_add, "pci_addr=[[<domain>:]<bus>:]<slot>\n"
+                                         "[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|host [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]... [host=02:00.0[,name=string][,dma=none]", "hot-add PCI device" },
-    { "pci_del", "ii", device_hot_remove, "bus slot-number", "hot remove PCI device" },
+    { "pci_add", "sss", pci_device_hot_add, "pci_addr=auto|[[<domain>:]<bus>:]<slot> nic|storage|host [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]... [host=02:00.0[,name=string][,dma=none]", "hot-add PCI device" },
+    { "pci_del", "s", pci_device_hot_remove, "pci_addr=[[<domain>:]<bus>:]<slot>", "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
 #ifdef CONFIG_QXL
     { "set_qxl_log_level", "i", qxl_do_set_log_level, "", "set qxl log level" },
diff --git a/qemu/net.c b/qemu/net.c
index b4c92da..2de134e 100644
--- a/qemu/net.c
+++ b/qemu/net.c
@@ -370,6 +370,29 @@ void qemu_del_vlan_client(VLANClientState *vc)
             pvc = &(*pvc)->next;
 }
 
+VLANClientState *qemu_find_vlan_client(VLANState *vlan, void *opaque)
+{
+    VLANClientState **pvc = &vlan->first_client;
+
+    while (*pvc != NULL)
+        if ((*pvc)->opaque == opaque)
+            return *pvc;
+        else
+            pvc = &(*pvc)->next;
+
+    return NULL;
+}
+
+static int nic_get_free_idx(void)
+{
+    int index;
+
+    for (index = 0; index < MAX_NICS; index++)
+        if (!nd_table[index].used)
+            return index;
+    return -1;
+}
+
 int qemu_can_send_packet(VLANClientState *vc1)
 {
     VLANState *vlan = vc1->vlan;
@@ -1754,19 +1777,20 @@ int net_client_init(const char *device, const char *p)
     if (!strcmp(device, "nic")) {
         NICInfo *nd;
         uint8_t *macaddr;
+        int idx = nic_get_free_idx();
 
-        if (nb_nics >= MAX_NICS) {
+        if (idx == -1 || nb_nics >= MAX_NICS) {
             fprintf(stderr, "Too Many NICs\n");
             return -1;
         }
-        nd = &nd_table[nb_nics];
+        nd = &nd_table[idx];
         macaddr = nd->macaddr;
         macaddr[0] = 0x52;
         macaddr[1] = 0x54;
         macaddr[2] = 0x00;
         macaddr[3] = 0x12;
         macaddr[4] = 0x34;
-        macaddr[5] = 0x56 + nb_nics;
+        macaddr[5] = 0x56 + idx;
 
         if (get_param_value(buf, sizeof(buf), "macaddr", p)) {
             if (parse_macaddr(macaddr, buf) < 0) {
@@ -1779,10 +1803,11 @@ int net_client_init(const char *device, const char *p)
         }
         nd->vlan = vlan;
         nd->name = name;
+        nd->used = 1;
         name = NULL;
         nb_nics++;
         vlan->nb_guest_devs++;
-        ret = 0;
+        ret = idx;
     } else
     if (!strcmp(device, "none")) {
         /* does nothing. It is needed to signal that no network cards
@@ -1907,6 +1932,62 @@ 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", "socket"
+#ifdef CONFIG_SLIRP
+                                       ,"user"
+#endif
+#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;
diff --git a/qemu/net.h b/qemu/net.h
index 35d4788..88f33b7 100644
--- a/qemu/net.h
+++ b/qemu/net.h
@@ -44,6 +44,7 @@ VLANClientState *qemu_new_vlan_client(VLANState *vlan,
                                       IOCanRWHandler *fd_can_read,
                                       void *opaque);
 void qemu_del_vlan_client(VLANClientState *vc);
+VLANClientState *qemu_find_vlan_client(VLANState *vlan, void *opaque);
 int qemu_can_send_packet(VLANClientState *vc);
 ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov,
                           int iovcnt);
@@ -69,7 +70,7 @@ struct NICInfo {
     const char *model;
     const char *name;
     VLANState *vlan;
-    int devfn;
+    void *private;
     int used;
 };
 
@@ -106,6 +107,8 @@ void net_slirp_redir(const char *redir_str);
 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"
diff --git a/qemu/sysemu.h b/qemu/sysemu.h
index 48f8464..5d510f8 100644
--- a/qemu/sysemu.h
+++ b/qemu/sysemu.h
@@ -184,10 +184,18 @@ void qemu_system_hot_add_init(const char *cpu_model);
 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);
+
+typedef int (dev_match_fn)(void *dev_private, void *arg);
+
+int add_init_drive(const char *opts);
+void destroy_nic(dev_match_fn *match_fn, void *arg);
+void destroy_bdrvs(dev_match_fn *match_fn, void *arg);
+
+/* pci-hotplug */
+void pci_device_hot_add(const char *pci_addr, const char *type, const char *opts);
+void drive_hot_add(const char *pci_addr, const char *opts);
+void pci_device_hot_remove(const char *pci_addr);
+void pci_device_hot_remove_success(int pcibus, int slot);
 
 /* vmchannel devices */
 
-- 
1.6.1