PCI: Can continually add funcs after adding func0
authorAmos Kong <akong@redhat.com>
Fri, 25 Nov 2011 07:03:07 +0000 (15:03 +0800)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Tue, 14 Feb 2012 16:44:47 +0000 (08:44 -0800)
Boot up a KVM guest, and hotplug multifunction
devices(func1,func2,func0,func3) to guest.

for i in 1 2 0 3;do
qemu-img create /tmp/resize$i.qcow2 1G -f qcow2
(qemu) drive_add 0x11.$i id=drv11$i,if=none,file=/tmp/resize$i.qcow2
(qemu) device_add virtio-blk-pci,id=dev11$i,drive=drv11$i,addr=0x11.$i,multifunction=on
done

In linux kernel, when func0 of the slot is hot-added, the whole
slot will be marked as 'enabled', then driver will ignore other new
hotadded funcs.
But in Win7 & WinXP, we can continaully add other funcs after adding
func0, all funcs will be added in guest.

drivers/pci/hotplug/acpiphp_glue.c:
static int acpiphp_check_bridge(struct acpiphp_bridge *bridge)
{
....
        for (slot = bridge->slots; slot; slot = slot->next) {
                if (slot->flags & SLOT_ENABLED) {
                        acpiphp_disable_slot()
                else
                        acpiphp_enable_slot()
....                              |
}                                 v
                            enable_device()
                                  |
                                  v
        //only don't enable slot if func0 is not added
list_for_each_entry(func, &slot->funcs, sibling) {
               ...
        }
       slot->flags |= SLOT_ENABLED; //mark slot to 'enabled'

This patch just make pci driver can continaully add funcs after adding
func 0. Only mark slot to 'enabled' when all funcs are added.

For pci multifunction hotplug, we can add functions one by one(func 0 is
necessary), and all functions will be removed in one time.

Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/hotplug/acpiphp_glue.c

index 9ddf69e3bbef03487e12ea814be79bb3258fc0fa..12d070ca767480937e8e4bf92c88bd8bd4195b0d 100644 (file)
@@ -800,20 +800,10 @@ static int __ref enable_device(struct acpiphp_slot *slot)
        if (slot->flags & SLOT_ENABLED)
                goto err_exit;
 
-       /* sanity check: dev should be NULL when hot-plugged in */
-       dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0));
-       if (dev) {
-               /* This case shouldn't happen */
-               err("pci_dev structure already exists.\n");
-               pci_dev_put(dev);
-               retval = -1;
-               goto err_exit;
-       }
-
        num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
        if (num == 0) {
-               err("No new device found\n");
-               retval = -1;
+               /* Maybe only part of funcs are added. */
+               dbg("No new device found\n");
                goto err_exit;
        }
 
@@ -848,11 +838,16 @@ static int __ref enable_device(struct acpiphp_slot *slot)
 
        pci_bus_add_devices(bus);
 
+       slot->flags |= SLOT_ENABLED;
        list_for_each_entry(func, &slot->funcs, sibling) {
                dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
                                                  func->function));
-               if (!dev)
+               if (!dev) {
+                       /* Do not set SLOT_ENABLED flag if some funcs
+                          are not added. */
+                       slot->flags &= (~SLOT_ENABLED);
                        continue;
+               }
 
                if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
                    dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) {
@@ -867,7 +862,6 @@ static int __ref enable_device(struct acpiphp_slot *slot)
                pci_dev_put(dev);
        }
 
-       slot->flags |= SLOT_ENABLED;
 
  err_exit:
        return retval;
@@ -892,9 +886,12 @@ static int disable_device(struct acpiphp_slot *slot)
 {
        struct acpiphp_func *func;
        struct pci_dev *pdev;
+       struct pci_bus *bus = slot->bridge->pci_bus;
 
-       /* is this slot already disabled? */
-       if (!(slot->flags & SLOT_ENABLED))
+       /* The slot will be enabled when func 0 is added, so check
+          func 0 before disable the slot. */
+       pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0));
+       if (!pdev)
                goto err_exit;
 
        list_for_each_entry(func, &slot->funcs, sibling) {