* [PATCH] pci: clean all funcs when hot-removing multifunc device [not found] <1315976141-6684-1-git-send-email-akong@redhat.com> @ 2011-09-14 4:56 ` Amos Kong 2011-09-15 19:03 ` Bjorn Helgaas ` (2 subsequent siblings) 3 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2011-09-14 4:56 UTC (permalink / raw) To: linux-pci; +Cc: jbarnes, mst, alex williamson, mtosatti, kvm CC: kvm@vger.kernel.org ----- Original Message ----- 'slot->funcs' is initialized in acpiphp_glue.c:register_slot() before hotpluging device, and only one entry(func 0) is added to it, no new entry will be added to the list when hotpluging devices to the slot. When we release the whole device, there is only one entry in the list, this causes func1~7 could not be released. I try to add entries for all hotpluged device in enable_device(), but it doesn't work, because 'slot->funcs' is used in many place which we only need to process func 0. This patch just try to clean all funcs in disable_device(). drivers/pci/hotplug/acpiphp_glue.c: static int disable_device(struct acpiphp_slot *slot) { list_for_each_entry(func, &slot->funcs, sibling) { pdev = pci_get_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, func->function)); ..clean code.. // those code can only be executed one time(func 0) pci_remove_bus_device(pdev); --- pci_bus_add_device() is called for each func device in acpiphp_glue.c:enable_device(). pci_remove_bus_device(pdev) is only called for func 0 in acpiphp_glue.c:disable_device(). Boot up a KVM guest, hotplug a multifunc device(8 funcs), we can find it in the guest. @ ls /dev/vd* vda vdb vdc vde vdf vdg vdh @ lspci 00:06.0 SCSI storage controller: Red Hat, Inc Virtio block device ... 00:06.7 SCSI storage controller: Red Hat, Inc Virtio block device But func 1~7 still exist in guest after hot-removing the multifunc device through qemu monitor. @ lspci (00:06.0 disappeared) 00:06.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) ... 00:06.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) ^^^^^^^^ @ ls /dev/vd* vdb vdc vde vdf vdg vdh @ mkfs /dev/vdb INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. (task hung) Hotpluging multifunc of WinXp is fine. Signed-off-by: Amos Kong <akong@redhat.com> --- drivers/pci/hotplug/acpiphp_glue.c | 27 ++++++++++++++++++--------- 1 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index a70fa89..3b86d1a 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -880,6 +880,8 @@ static int disable_device(struct acpiphp_slot *slot) { struct acpiphp_func *func; struct pci_dev *pdev; + struct pci_bus *bus = slot->bridge->pci_bus; + int i, num = 1; /* is this slot already disabled? */ if (!(slot->flags & SLOT_ENABLED)) @@ -893,16 +895,23 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { - pci_stop_bus_device(pdev); - if (pdev->subordinate) { - disable_bridges(pdev->subordinate); - pci_disable_device(pdev); + pdev = pci_scan_single_device(bus, + PCI_DEVFN(slot->device, 0)); + if (!pdev) + goto err_exit; + if (pdev->multifunction == 1) + num = 8; + for (i=0; i<num; i++) { + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, i)); + if (pdev) { + pci_stop_bus_device(pdev); + if (pdev->subordinate) { + disable_bridges(pdev->subordinate); + pci_disable_device(pdev); + } + pci_remove_bus_device(pdev); + pci_dev_put(pdev); } - pci_remove_bus_device(pdev); - pci_dev_put(pdev); } } -- 1.7.6.1 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH] pci: clean all funcs when hot-removing multifunc device [not found] <1315976141-6684-1-git-send-email-akong@redhat.com> 2011-09-14 4:56 ` [PATCH] pci: clean all funcs when hot-removing multifunc device Amos Kong @ 2011-09-15 19:03 ` Bjorn Helgaas 2011-09-15 23:56 ` Kenji Kaneshige 2012-05-09 15:25 ` [Qemu-devel] " Amos Kong 2012-05-10 15:44 ` [Qemu-devel] " Amos Kong 3 siblings, 1 reply; 27+ messages in thread From: Bjorn Helgaas @ 2011-09-15 19:03 UTC (permalink / raw) To: Amos Kong Cc: linux-pci, jbarnes, mst, alex.williamson, mtosatti, kvm, Kenji Kaneshige, Kevin O'Connor, seabios On Tue, Sep 13, 2011 at 10:55 PM, Amos Kong <akong@redhat.com> wrote: > 'slot->funcs' is initialized in acpiphp_glue.c:register_slot() before > hotpluging device, and only one entry(func 0) is added to it, > no new entry will be added to the list when hotpluging devices to the slot. > When we release the whole device, there is only one entry in the list, > this causes func1~7 could not be released. > I try to add entries for all hotpluged device in enable_device(), but > it doesn't work, because 'slot->funcs' is used in many place which we only > need to process func 0. This patch just try to clean all funcs in > disable_device(). ... > Hotpluging multifunc of WinXp is fine. I'm going to ignore this patch for now. Please consider these questions, then repost it if you still want it: I assume you mean that Linux and WinXP are both running on top of the same SeaBIOS, and hot-remove of a multifunction device works in WinXP and fails in Linux. That sounds like Linux is broken, and we should fix it. We might want to make a SeaBIOS change for other reasons, but it'd still be good to fix Linux in case there are other similar BIOSes. Why do we need pci_scan_single_device()? The device should have been scanned already when it was added, and I would think that should have set pdev->multifunction. Your patch needs spaces around the operators in the "for" loop. In the changelog, it would be nice to have the URL of a bugzilla where the dmesg and DSDT are attached. Bjorn > Signed-off-by: Amos Kong <akong@redhat.com> > --- > drivers/pci/hotplug/acpiphp_glue.c | 27 ++++++++++++++++++--------- > 1 files changed, 18 insertions(+), 9 deletions(-) > > diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c > index a70fa89..3b86d1a 100644 > --- a/drivers/pci/hotplug/acpiphp_glue.c > +++ b/drivers/pci/hotplug/acpiphp_glue.c > @@ -880,6 +880,8 @@ static int disable_device(struct acpiphp_slot *slot) > { > struct acpiphp_func *func; > struct pci_dev *pdev; > + struct pci_bus *bus = slot->bridge->pci_bus; > + int i, num = 1; > > /* is this slot already disabled? */ > if (!(slot->flags & SLOT_ENABLED)) > @@ -893,16 +895,23 @@ static int disable_device(struct acpiphp_slot *slot) > func->bridge = NULL; > } > > - pdev = pci_get_slot(slot->bridge->pci_bus, > - PCI_DEVFN(slot->device, func->function)); > - if (pdev) { > - pci_stop_bus_device(pdev); > - if (pdev->subordinate) { > - disable_bridges(pdev->subordinate); > - pci_disable_device(pdev); > + pdev = pci_scan_single_device(bus, > + PCI_DEVFN(slot->device, 0)); > + if (!pdev) > + goto err_exit; > + if (pdev->multifunction == 1) > + num = 8; > + for (i=0; i<num; i++) { > + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, i)); > + if (pdev) { > + pci_stop_bus_device(pdev); > + if (pdev->subordinate) { > + disable_bridges(pdev->subordinate); > + pci_disable_device(pdev); > + } > + pci_remove_bus_device(pdev); > + pci_dev_put(pdev); > } > - pci_remove_bus_device(pdev); > - pci_dev_put(pdev); > } > } > > -- > 1.7.6.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH] pci: clean all funcs when hot-removing multifunc device 2011-09-15 19:03 ` Bjorn Helgaas @ 2011-09-15 23:56 ` Kenji Kaneshige 0 siblings, 0 replies; 27+ messages in thread From: Kenji Kaneshige @ 2011-09-15 23:56 UTC (permalink / raw) To: Bjorn Helgaas, Amos Kong Cc: linux-pci, jbarnes, mst, alex.williamson, mtosatti, kvm, Kevin O'Connor, seabios (2011/09/16 4:03), Bjorn Helgaas wrote: > On Tue, Sep 13, 2011 at 10:55 PM, Amos Kong<akong@redhat.com> wrote: >> 'slot->funcs' is initialized in acpiphp_glue.c:register_slot() before >> hotpluging device, and only one entry(func 0) is added to it, >> no new entry will be added to the list when hotpluging devices to the slot. >> When we release the whole device, there is only one entry in the list, >> this causes func1~7 could not be released. >> I try to add entries for all hotpluged device in enable_device(), but >> it doesn't work, because 'slot->funcs' is used in many place which we only >> need to process func 0. This patch just try to clean all funcs in >> disable_device(). > ... >> Hotpluging multifunc of WinXp is fine. > > I'm going to ignore this patch for now. Please consider these > questions, then repost it if you still want it: > > I assume you mean that Linux and WinXP are both running on top of the > same SeaBIOS, and hot-remove of a multifunction device works in WinXP > and fails in Linux. That sounds like Linux is broken, and we should > fix it. We might want to make a SeaBIOS change for other reasons, but > it'd still be good to fix Linux in case there are other similar > BIOSes. No objection about fixing Linux. > > Why do we need pci_scan_single_device()? The device should have been > scanned already when it was added, and I would think that should have > set pdev->multifunction. It should be pci_get_slot() instead. Note that it needs corresponding pci_dev_put(). Regards, Kenji Kaneshige > > Your patch needs spaces around the operators in the "for" loop. > > In the changelog, it would be nice to have the URL of a bugzilla where > the dmesg and DSDT are attached. > > Bjorn > >> Signed-off-by: Amos Kong<akong@redhat.com> >> --- >> drivers/pci/hotplug/acpiphp_glue.c | 27 ++++++++++++++++++--------- >> 1 files changed, 18 insertions(+), 9 deletions(-) >> >> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >> index a70fa89..3b86d1a 100644 >> --- a/drivers/pci/hotplug/acpiphp_glue.c >> +++ b/drivers/pci/hotplug/acpiphp_glue.c >> @@ -880,6 +880,8 @@ static int disable_device(struct acpiphp_slot *slot) >> { >> struct acpiphp_func *func; >> struct pci_dev *pdev; >> + struct pci_bus *bus = slot->bridge->pci_bus; >> + int i, num = 1; >> >> /* is this slot already disabled? */ >> if (!(slot->flags& SLOT_ENABLED)) >> @@ -893,16 +895,23 @@ static int disable_device(struct acpiphp_slot *slot) >> func->bridge = NULL; >> } >> >> - pdev = pci_get_slot(slot->bridge->pci_bus, >> - PCI_DEVFN(slot->device, func->function)); >> - if (pdev) { >> - pci_stop_bus_device(pdev); >> - if (pdev->subordinate) { >> - disable_bridges(pdev->subordinate); >> - pci_disable_device(pdev); >> + pdev = pci_scan_single_device(bus, >> + PCI_DEVFN(slot->device, 0)); >> + if (!pdev) >> + goto err_exit; >> + if (pdev->multifunction == 1) >> + num = 8; >> + for (i=0; i<num; i++) { >> + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, i)); >> + if (pdev) { >> + pci_stop_bus_device(pdev); >> + if (pdev->subordinate) { >> + disable_bridges(pdev->subordinate); >> + pci_disable_device(pdev); >> + } >> + pci_remove_bus_device(pdev); >> + pci_dev_put(pdev); >> } >> - pci_remove_bus_device(pdev); >> - pci_dev_put(pdev); >> } >> } >> >> -- >> 1.7.6.1 >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-pci" in >> the body of a message to majordomo@vger.kernel.org >> More majordomo info at http://vger.kernel.org/majordomo-info.html >> > > ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v2] pci: clean all funcs when hot-removing multifunc device [not found] <1315976141-6684-1-git-send-email-akong@redhat.com> @ 2012-05-09 15:25 ` Amos Kong 2011-09-15 19:03 ` Bjorn Helgaas ` (2 subsequent siblings) 3 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-09 15:25 UTC (permalink / raw) To: mst, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=737720#c8 (dmesg and DSDT were attached in bz) Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device, in seabios only defines one device for a slot in ACPI DSDT table, then there is only one entry (for func#0) would be added into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); I try to add entries for all hotpluged device in enable_device(), but it doesn't work, because 'slot->funcs' is used in many place which we only need to process func#0. This patch just try to clean all funcs in disable_device(). Hotpluging multifunc of guests(WinXp/Win7) is fine. --- v1 thread: http://marc.info/?t=131597601700003&r=1&w=2 Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Signed-off-by: Amos Kong <akong@redhat.com> --- drivers/pci/hotplug/acpiphp_glue.c | 20 +++++++++++--------- 1 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..42f9bb9 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -887,6 +887,7 @@ static int disable_device(struct acpiphp_slot *slot) struct acpiphp_func *func; struct pci_dev *pdev; struct pci_bus *bus = slot->bridge->pci_bus; + int fn; /* The slot will be enabled when func 0 is added, so check func 0 before disable the slot. */ @@ -902,16 +903,17 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { - pci_stop_bus_device(pdev); - if (pdev->subordinate) { - disable_bridges(pdev->subordinate); - pci_disable_device(pdev); + for (fn = 0; fn < 8; fn++) { + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, fn)); + if (pdev) { + pci_stop_bus_device(pdev); + if (pdev->subordinate) { + disable_bridges(pdev->subordinate); + pci_disable_device(pdev); + } + __pci_remove_bus_device(pdev); + pci_dev_put(pdev); } - __pci_remove_bus_device(pdev); - pci_dev_put(pdev); } } ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [Qemu-devel] [PATCH v2] pci: clean all funcs when hot-removing multifunc device @ 2012-05-09 15:25 ` Amos Kong 0 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-09 15:25 UTC (permalink / raw) To: mst, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=737720#c8 (dmesg and DSDT were attached in bz) Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device, in seabios only defines one device for a slot in ACPI DSDT table, then there is only one entry (for func#0) would be added into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); I try to add entries for all hotpluged device in enable_device(), but it doesn't work, because 'slot->funcs' is used in many place which we only need to process func#0. This patch just try to clean all funcs in disable_device(). Hotpluging multifunc of guests(WinXp/Win7) is fine. --- v1 thread: http://marc.info/?t=131597601700003&r=1&w=2 Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Signed-off-by: Amos Kong <akong@redhat.com> --- drivers/pci/hotplug/acpiphp_glue.c | 20 +++++++++++--------- 1 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..42f9bb9 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -887,6 +887,7 @@ static int disable_device(struct acpiphp_slot *slot) struct acpiphp_func *func; struct pci_dev *pdev; struct pci_bus *bus = slot->bridge->pci_bus; + int fn; /* The slot will be enabled when func 0 is added, so check func 0 before disable the slot. */ @@ -902,16 +903,17 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { - pci_stop_bus_device(pdev); - if (pdev->subordinate) { - disable_bridges(pdev->subordinate); - pci_disable_device(pdev); + for (fn = 0; fn < 8; fn++) { + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, fn)); + if (pdev) { + pci_stop_bus_device(pdev); + if (pdev->subordinate) { + disable_bridges(pdev->subordinate); + pci_disable_device(pdev); + } + __pci_remove_bus_device(pdev); + pci_dev_put(pdev); } - __pci_remove_bus_device(pdev); - pci_dev_put(pdev); } } ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v3] pci: clean all funcs when hot-removing multifunc device [not found] <1315976141-6684-1-git-send-email-akong@redhat.com> @ 2012-05-10 15:44 ` Amos Kong 2011-09-15 19:03 ` Bjorn Helgaas ` (2 subsequent siblings) 3 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-10 15:44 UTC (permalink / raw) To: mst, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin Hotplug CallTrace: int acpiphp_enable_slot(struct acpiphp_slot *slot) \_enable_device(slot); \_pci_bus_add_devices(bus); # un-added new devs(all funcs in slot) will be added list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->is_added) continue; pci_bus_add_device(dev); device_add(&dev->dev); dev->is_added = 1; 'dev->is_added' is used to trace if pci dev is added to bus, all funcs in same slot would be added to bus in enable_device(slot). So we need to clean all funcs of same slot in disable_device(slot). But hot-remove exists bug: https://bugzilla.kernel.org/show_bug.cgi?id=43219 (dmesg and DSDT were attached in bz), detail: . Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ . Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | . Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device in pciphp spec, seabios only defines one device for a slot in ACPI DSDT table. In acpiphp_glue.c:register_slot(), only one entry is added (for func#0) into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); Hotpluging multifunc of guests(WinXp/Win7) is fine. --- v1 thread: http://marc.info/?t=131597601700003&r=1&w=2 Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Changes from v2: - update detail reason(calltrace) to commitlog - remove hardcode 8, find funcs in pci devlist Signed-off-by: Amos Kong <akong@redhat.com> --- drivers/pci/hotplug/acpiphp_glue.c | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..a7442d9 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) static int disable_device(struct acpiphp_slot *slot) { struct acpiphp_func *func; - struct pci_dev *pdev; + struct pci_dev *pdev, *tmp; struct pci_bus *bus = slot->bridge->pci_bus; /* The slot will be enabled when func 0 is added, so check @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { + if (PCI_SLOT(pdev->devfn) != slot->device) + continue; + pci_stop_bus_device(pdev); if (pdev->subordinate) { disable_bridges(pdev->subordinate); ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-10 15:44 ` Amos Kong 0 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-10 15:44 UTC (permalink / raw) To: mst, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin Hotplug CallTrace: int acpiphp_enable_slot(struct acpiphp_slot *slot) \_enable_device(slot); \_pci_bus_add_devices(bus); # un-added new devs(all funcs in slot) will be added list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->is_added) continue; pci_bus_add_device(dev); device_add(&dev->dev); dev->is_added = 1; 'dev->is_added' is used to trace if pci dev is added to bus, all funcs in same slot would be added to bus in enable_device(slot). So we need to clean all funcs of same slot in disable_device(slot). But hot-remove exists bug: https://bugzilla.kernel.org/show_bug.cgi?id=43219 (dmesg and DSDT were attached in bz), detail: . Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ . Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | . Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device in pciphp spec, seabios only defines one device for a slot in ACPI DSDT table. In acpiphp_glue.c:register_slot(), only one entry is added (for func#0) into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); Hotpluging multifunc of guests(WinXp/Win7) is fine. --- v1 thread: http://marc.info/?t=131597601700003&r=1&w=2 Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Changes from v2: - update detail reason(calltrace) to commitlog - remove hardcode 8, find funcs in pci devlist Signed-off-by: Amos Kong <akong@redhat.com> --- drivers/pci/hotplug/acpiphp_glue.c | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..a7442d9 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) static int disable_device(struct acpiphp_slot *slot) { struct acpiphp_func *func; - struct pci_dev *pdev; + struct pci_dev *pdev, *tmp; struct pci_bus *bus = slot->bridge->pci_bus; /* The slot will be enabled when func 0 is added, so check @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { + if (PCI_SLOT(pdev->devfn) != slot->device) + continue; + pci_stop_bus_device(pdev); if (pdev->subordinate) { disable_bridges(pdev->subordinate); ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-10 15:44 ` [Qemu-devel] " Amos Kong @ 2012-05-10 17:09 ` Jiang Liu -1 siblings, 0 replies; 27+ messages in thread From: Jiang Liu @ 2012-05-10 17:09 UTC (permalink / raw) To: Amos Kong Cc: mst, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin On 05/10/2012 11:44 PM, Amos Kong wrote: > diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c > index 806c44f..a7442d9 100644 > --- a/drivers/pci/hotplug/acpiphp_glue.c > +++ b/drivers/pci/hotplug/acpiphp_glue.c > @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) > static int disable_device(struct acpiphp_slot *slot) > { > struct acpiphp_func *func; > - struct pci_dev *pdev; > + struct pci_dev *pdev, *tmp; > struct pci_bus *bus = slot->bridge->pci_bus; > > /* The slot will be enabled when func 0 is added, so check > @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) > func->bridge = NULL; > } > > - pdev = pci_get_slot(slot->bridge->pci_bus, > - PCI_DEVFN(slot->device, func->function)); > - if (pdev) { > + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { > + if (PCI_SLOT(pdev->devfn) != slot->device) > + continue; > + The pci_bus_sem lock should be acquired when walking the bus->devices list. Otherwise it may cause invalid memory access if another thread is modifying the bus->devices list concurrently. BTW, what's the relationship with "[PATCH v3] hotplug: add device per func in ACPI DSDT tables"? Seems they are both solving the same issue. > pci_stop_bus_device(pdev); > if (pdev->subordinate) { > disable_bridges(pdev->subordinate); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-10 17:09 ` Jiang Liu 0 siblings, 0 replies; 27+ messages in thread From: Jiang Liu @ 2012-05-10 17:09 UTC (permalink / raw) To: Amos Kong Cc: mst, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin On 05/10/2012 11:44 PM, Amos Kong wrote: > diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c > index 806c44f..a7442d9 100644 > --- a/drivers/pci/hotplug/acpiphp_glue.c > +++ b/drivers/pci/hotplug/acpiphp_glue.c > @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) > static int disable_device(struct acpiphp_slot *slot) > { > struct acpiphp_func *func; > - struct pci_dev *pdev; > + struct pci_dev *pdev, *tmp; > struct pci_bus *bus = slot->bridge->pci_bus; > > /* The slot will be enabled when func 0 is added, so check > @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) > func->bridge = NULL; > } > > - pdev = pci_get_slot(slot->bridge->pci_bus, > - PCI_DEVFN(slot->device, func->function)); > - if (pdev) { > + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { > + if (PCI_SLOT(pdev->devfn) != slot->device) > + continue; > + The pci_bus_sem lock should be acquired when walking the bus->devices list. Otherwise it may cause invalid memory access if another thread is modifying the bus->devices list concurrently. BTW, what's the relationship with "[PATCH v3] hotplug: add device per func in ACPI DSDT tables"? Seems they are both solving the same issue. > pci_stop_bus_device(pdev); > if (pdev->subordinate) { > disable_bridges(pdev->subordinate); > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-10 17:09 ` [Qemu-devel] " Jiang Liu @ 2012-05-10 18:55 ` Michael S. Tsirkin -1 siblings, 0 replies; 27+ messages in thread From: Michael S. Tsirkin @ 2012-05-10 18:55 UTC (permalink / raw) To: Jiang Liu Cc: Amos Kong, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: > On 05/10/2012 11:44 PM, Amos Kong wrote: > > > diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c > > index 806c44f..a7442d9 100644 > > --- a/drivers/pci/hotplug/acpiphp_glue.c > > +++ b/drivers/pci/hotplug/acpiphp_glue.c > > @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) > > static int disable_device(struct acpiphp_slot *slot) > > { > > struct acpiphp_func *func; > > - struct pci_dev *pdev; > > + struct pci_dev *pdev, *tmp; > > struct pci_bus *bus = slot->bridge->pci_bus; > > > > /* The slot will be enabled when func 0 is added, so check > > @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) > > func->bridge = NULL; > > } > > > > - pdev = pci_get_slot(slot->bridge->pci_bus, > > - PCI_DEVFN(slot->device, func->function)); > > - if (pdev) { > > + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { > > + if (PCI_SLOT(pdev->devfn) != slot->device) > > + continue; > > + > The pci_bus_sem lock should be acquired when walking the bus->devices list. > Otherwise it may cause invalid memory access if another thread is modifying > the bus->devices list concurrently. > > BTW, what's the relationship with "[PATCH v3] hotplug: add device per func > in ACPI DSDT tables"? Seems they are both solving the same issue. That's a bios patch. It's needed if you want broken linux to work. This makes linux behave properly on the original bios. > > pci_stop_bus_device(pdev); > > if (pdev->subordinate) { > > disable_bridges(pdev->subordinate); > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-10 18:55 ` Michael S. Tsirkin 0 siblings, 0 replies; 27+ messages in thread From: Michael S. Tsirkin @ 2012-05-10 18:55 UTC (permalink / raw) To: Jiang Liu Cc: linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin, Amos Kong On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: > On 05/10/2012 11:44 PM, Amos Kong wrote: > > > diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c > > index 806c44f..a7442d9 100644 > > --- a/drivers/pci/hotplug/acpiphp_glue.c > > +++ b/drivers/pci/hotplug/acpiphp_glue.c > > @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) > > static int disable_device(struct acpiphp_slot *slot) > > { > > struct acpiphp_func *func; > > - struct pci_dev *pdev; > > + struct pci_dev *pdev, *tmp; > > struct pci_bus *bus = slot->bridge->pci_bus; > > > > /* The slot will be enabled when func 0 is added, so check > > @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) > > func->bridge = NULL; > > } > > > > - pdev = pci_get_slot(slot->bridge->pci_bus, > > - PCI_DEVFN(slot->device, func->function)); > > - if (pdev) { > > + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { > > + if (PCI_SLOT(pdev->devfn) != slot->device) > > + continue; > > + > The pci_bus_sem lock should be acquired when walking the bus->devices list. > Otherwise it may cause invalid memory access if another thread is modifying > the bus->devices list concurrently. > > BTW, what's the relationship with "[PATCH v3] hotplug: add device per func > in ACPI DSDT tables"? Seems they are both solving the same issue. That's a bios patch. It's needed if you want broken linux to work. This makes linux behave properly on the original bios. > > pci_stop_bus_device(pdev); > > if (pdev->subordinate) { > > disable_bridges(pdev->subordinate); > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-10 18:55 ` [Qemu-devel] " Michael S. Tsirkin @ 2012-05-10 23:54 ` Amos Kong -1 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-10 23:54 UTC (permalink / raw) To: Michael S. Tsirkin Cc: Jiang Liu, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: > On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >> On 05/10/2012 11:44 PM, Amos Kong wrote: >> >>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>> index 806c44f..a7442d9 100644 >>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>> static int disable_device(struct acpiphp_slot *slot) >>> { >>> struct acpiphp_func *func; >>> - struct pci_dev *pdev; >>> + struct pci_dev *pdev, *tmp; >>> struct pci_bus *bus = slot->bridge->pci_bus; >>> >>> /* The slot will be enabled when func 0 is added, so check >>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>> func->bridge = NULL; >>> } >>> >>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>> - PCI_DEVFN(slot->device, func->function)); >>> - if (pdev) { >>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>> + continue; >>> + >> The pci_bus_sem lock should be acquired when walking the bus->devices list. >> Otherwise it may cause invalid memory access if another thread is modifying >> the bus->devices list concurrently. >> BTW, what's the relationship with "[PATCH v3] hotplug: add device per func >> in ACPI DSDT tables"? Seems they are both solving the same issue. Two work need to be done when we disable a slot, cleaning configuration(in OS) and power off slot. Currently the second part(power off) works fine, all funcs disappear from "(qemu)#info block" after hot-remove slot. The only problem is func 1~7 are not unconfigured, so I NAKed seabios patch, and try to fix this problem in pci driver. (btw, winxp & win7 hotplug works currently) /** * acpiphp_disable_slot - power off slot * @slot: ACPI PHP slot */ int acpiphp_disable_slot(struct acpiphp_slot *slot) { mutex_lock(&slot->crit_sect); /* unconfigure all functions */ retval = disable_device(slot); /* power off all functions */ retval = power_off_slot(slot); .... } > That's a bios patch. It's needed if you want broken linux to work. This > makes linux behave properly on the original bios. > >>> pci_stop_bus_device(pdev); >>> if (pdev->subordinate) { >>> disable_bridges(pdev->subordinate); -- Amos. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-10 23:54 ` Amos Kong 0 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-10 23:54 UTC (permalink / raw) To: Michael S. Tsirkin Cc: linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin, Jiang Liu On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: > On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >> On 05/10/2012 11:44 PM, Amos Kong wrote: >> >>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>> index 806c44f..a7442d9 100644 >>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>> static int disable_device(struct acpiphp_slot *slot) >>> { >>> struct acpiphp_func *func; >>> - struct pci_dev *pdev; >>> + struct pci_dev *pdev, *tmp; >>> struct pci_bus *bus = slot->bridge->pci_bus; >>> >>> /* The slot will be enabled when func 0 is added, so check >>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>> func->bridge = NULL; >>> } >>> >>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>> - PCI_DEVFN(slot->device, func->function)); >>> - if (pdev) { >>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>> + continue; >>> + >> The pci_bus_sem lock should be acquired when walking the bus->devices list. >> Otherwise it may cause invalid memory access if another thread is modifying >> the bus->devices list concurrently. >> BTW, what's the relationship with "[PATCH v3] hotplug: add device per func >> in ACPI DSDT tables"? Seems they are both solving the same issue. Two work need to be done when we disable a slot, cleaning configuration(in OS) and power off slot. Currently the second part(power off) works fine, all funcs disappear from "(qemu)#info block" after hot-remove slot. The only problem is func 1~7 are not unconfigured, so I NAKed seabios patch, and try to fix this problem in pci driver. (btw, winxp & win7 hotplug works currently) /** * acpiphp_disable_slot - power off slot * @slot: ACPI PHP slot */ int acpiphp_disable_slot(struct acpiphp_slot *slot) { mutex_lock(&slot->crit_sect); /* unconfigure all functions */ retval = disable_device(slot); /* power off all functions */ retval = power_off_slot(slot); .... } > That's a bios patch. It's needed if you want broken linux to work. This > makes linux behave properly on the original bios. > >>> pci_stop_bus_device(pdev); >>> if (pdev->subordinate) { >>> disable_bridges(pdev->subordinate); -- Amos. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-10 23:54 ` Amos Kong @ 2012-05-11 0:24 ` Amos Kong -1 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-11 0:24 UTC (permalink / raw) To: Michael S. Tsirkin Cc: Jiang Liu, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin On 05/11/2012 07:54 AM, Amos Kong wrote: > On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>> >>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>> index 806c44f..a7442d9 100644 >>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>> static int disable_device(struct acpiphp_slot *slot) >>>> { >>>> struct acpiphp_func *func; >>>> - struct pci_dev *pdev; >>>> + struct pci_dev *pdev, *tmp; >>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>> >>>> /* The slot will be enabled when func 0 is added, so check >>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>> func->bridge = NULL; >>>> } >>>> >>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>> - PCI_DEVFN(slot->device, func->function)); >>>> - if (pdev) { >>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>> + continue; >>>> + >>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>> Otherwise it may cause invalid memory access if another thread is modifying >>> the bus->devices list concurrently. pci_bus_sem lock is only request for writing &bus->devices list, right ? and this protection already exists in pci_destory_dev(). static int disable_device(struct acpiphp_slot *slot) \_ list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { \_ __pci_remove_bus_device(pdev); \_ pci_destroy_dev(dev); static void pci_destroy_dev(struct pci_dev *dev) { /* Remove the device from the device lists, and prevent any further * list accesses from this device */ down_write(&pci_bus_sem); list_del(&dev->bus_list); dev->bus_list.next = dev->bus_list.prev = NULL; up_write(&pci_bus_sem); ... } -- Amos. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-11 0:24 ` Amos Kong 0 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-11 0:24 UTC (permalink / raw) To: Michael S. Tsirkin Cc: linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin, Jiang Liu On 05/11/2012 07:54 AM, Amos Kong wrote: > On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>> >>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>> index 806c44f..a7442d9 100644 >>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>> static int disable_device(struct acpiphp_slot *slot) >>>> { >>>> struct acpiphp_func *func; >>>> - struct pci_dev *pdev; >>>> + struct pci_dev *pdev, *tmp; >>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>> >>>> /* The slot will be enabled when func 0 is added, so check >>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>> func->bridge = NULL; >>>> } >>>> >>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>> - PCI_DEVFN(slot->device, func->function)); >>>> - if (pdev) { >>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>> + continue; >>>> + >>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>> Otherwise it may cause invalid memory access if another thread is modifying >>> the bus->devices list concurrently. pci_bus_sem lock is only request for writing &bus->devices list, right ? and this protection already exists in pci_destory_dev(). static int disable_device(struct acpiphp_slot *slot) \_ list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { \_ __pci_remove_bus_device(pdev); \_ pci_destroy_dev(dev); static void pci_destroy_dev(struct pci_dev *dev) { /* Remove the device from the device lists, and prevent any further * list accesses from this device */ down_write(&pci_bus_sem); list_del(&dev->bus_list); dev->bus_list.next = dev->bus_list.prev = NULL; up_write(&pci_bus_sem); ... } -- Amos. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-11 0:24 ` Amos Kong (?) @ 2012-05-11 14:00 ` Jiang Liu 2012-05-16 15:26 ` Bjorn Helgaas -1 siblings, 1 reply; 27+ messages in thread From: Jiang Liu @ 2012-05-11 14:00 UTC (permalink / raw) To: Amos Kong Cc: Michael S. Tsirkin, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin On 05/11/2012 08:24 AM, Amos Kong wrote: > On 05/11/2012 07:54 AM, Amos Kong wrote: >> On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >>> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>>> >>>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>>> index 806c44f..a7442d9 100644 >>>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>>> static int disable_device(struct acpiphp_slot *slot) >>>>> { >>>>> struct acpiphp_func *func; >>>>> - struct pci_dev *pdev; >>>>> + struct pci_dev *pdev, *tmp; >>>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>>> >>>>> /* The slot will be enabled when func 0 is added, so check >>>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>>> func->bridge = NULL; >>>>> } >>>>> >>>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>>> - PCI_DEVFN(slot->device, func->function)); >>>>> - if (pdev) { >>>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>>> + continue; >>>>> + >>>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>>> Otherwise it may cause invalid memory access if another thread is modifying >>>> the bus->devices list concurrently. > > pci_bus_sem lock is only request for writing &bus->devices list, right ? > and this protection already exists in pci_destory_dev(). That's for writer. For reader to walk the pci_bus->devices list, you also need to acquire the reader lock by down_read(&pci_bus_sem). Please refer to pci_get_slot() for example. This especially import for native OS because there may be multiple PCI slots/devices on the bus. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-11 14:00 ` Jiang Liu @ 2012-05-16 15:26 ` Bjorn Helgaas 0 siblings, 0 replies; 27+ messages in thread From: Bjorn Helgaas @ 2012-05-16 15:26 UTC (permalink / raw) To: Jiang Liu Cc: Amos Kong, Michael S. Tsirkin, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin, Yinghai Lu, Kenji Kaneshige On Fri, May 11, 2012 at 8:00 AM, Jiang Liu <liuj97@gmail.com> wrote: > On 05/11/2012 08:24 AM, Amos Kong wrote: >> On 05/11/2012 07:54 AM, Amos Kong wrote: >>> On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >>>> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>>>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>>>> >>>>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>>>> index 806c44f..a7442d9 100644 >>>>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>>>> static int disable_device(struct acpiphp_slot *slot) >>>>>> { >>>>>> struct acpiphp_func *func; >>>>>> - struct pci_dev *pdev; >>>>>> + struct pci_dev *pdev, *tmp; >>>>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>>>> >>>>>> /* The slot will be enabled when func 0 is added, so check >>>>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>>>> func->bridge = NULL; >>>>>> } >>>>>> >>>>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>>>> - PCI_DEVFN(slot->device, func->function)); >>>>>> - if (pdev) { >>>>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>>>> + continue; I think the concept is good: in enable_device(), we use pci_scan_slot(), which scans all possible functions in the slot. So in disable_device() we should do something symmetric to remove all the functions. >>>>>> + >>>>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>>>> Otherwise it may cause invalid memory access if another thread is modifying >>>>> the bus->devices list concurrently. >> >> pci_bus_sem lock is only request for writing &bus->devices list, right ? >> and this protection already exists in pci_destory_dev(). > That's for writer. For reader to walk the pci_bus->devices list, you also need > to acquire the reader lock by down_read(&pci_bus_sem). Please refer to > pci_get_slot() for example. This especially import for native OS because there > may be multiple PCI slots/devices on the bus. There is a lot of existing code that walks bus->devices without holding pci_bus_sem, but most of it is boot-time code that is arguably safe (though I think things like pcibios_fixup_bus() are poorly designed and don't fit well in the hotplug-enabled world). In this case, I do think we need to protect against updates while we're walking bus->devices. It's probably not trivial because __pci_remove_bus_device() calls pci_destroy_dev(), where we do the down_write(), so simply wrapping the whole thing with down_read() will cause a deadlock. Kenji-san, Yinghai, do you have any input? Bjorn ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-16 15:26 ` Bjorn Helgaas 0 siblings, 0 replies; 27+ messages in thread From: Bjorn Helgaas @ 2012-05-16 15:26 UTC (permalink / raw) To: Jiang Liu Cc: Michael S. Tsirkin, linux-pci, seabios, qemu-devel, jbarnes, alex.williamson, kevin, Kenji Kaneshige, Amos Kong, Yinghai Lu On Fri, May 11, 2012 at 8:00 AM, Jiang Liu <liuj97@gmail.com> wrote: > On 05/11/2012 08:24 AM, Amos Kong wrote: >> On 05/11/2012 07:54 AM, Amos Kong wrote: >>> On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >>>> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>>>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>>>> >>>>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>>>> index 806c44f..a7442d9 100644 >>>>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>>>> static int disable_device(struct acpiphp_slot *slot) >>>>>> { >>>>>> struct acpiphp_func *func; >>>>>> - struct pci_dev *pdev; >>>>>> + struct pci_dev *pdev, *tmp; >>>>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>>>> >>>>>> /* The slot will be enabled when func 0 is added, so check >>>>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>>>> func->bridge = NULL; >>>>>> } >>>>>> >>>>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>>>> - PCI_DEVFN(slot->device, func->function)); >>>>>> - if (pdev) { >>>>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>>>> + continue; I think the concept is good: in enable_device(), we use pci_scan_slot(), which scans all possible functions in the slot. So in disable_device() we should do something symmetric to remove all the functions. >>>>>> + >>>>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>>>> Otherwise it may cause invalid memory access if another thread is modifying >>>>> the bus->devices list concurrently. >> >> pci_bus_sem lock is only request for writing &bus->devices list, right ? >> and this protection already exists in pci_destory_dev(). > That's for writer. For reader to walk the pci_bus->devices list, you also need > to acquire the reader lock by down_read(&pci_bus_sem). Please refer to > pci_get_slot() for example. This especially import for native OS because there > may be multiple PCI slots/devices on the bus. There is a lot of existing code that walks bus->devices without holding pci_bus_sem, but most of it is boot-time code that is arguably safe (though I think things like pcibios_fixup_bus() are poorly designed and don't fit well in the hotplug-enabled world). In this case, I do think we need to protect against updates while we're walking bus->devices. It's probably not trivial because __pci_remove_bus_device() calls pci_destroy_dev(), where we do the down_write(), so simply wrapping the whole thing with down_read() will cause a deadlock. Kenji-san, Yinghai, do you have any input? Bjorn ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v4] pci: clean all funcs when hot-removing multifunc device 2012-05-16 15:26 ` Bjorn Helgaas @ 2012-05-20 2:31 ` kongjianjun -1 siblings, 0 replies; 27+ messages in thread From: kongjianjun @ 2012-05-20 2:31 UTC (permalink / raw) To: liuj97, linux-pci, qemu-devel, jbarnes, kaneshige.kenji, bhelgaas Cc: Amos Kong From: Amos Kong <kongjianjun@gmail.com> Hotplug CallTrace: int acpiphp_enable_slot(struct acpiphp_slot *slot) \_enable_device(slot); \_pci_bus_add_devices(bus); # un-added new devs(all funcs in slot) will be added list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->is_added) continue; pci_bus_add_device(dev); device_add(&dev->dev); dev->is_added = 1; 'dev->is_added' is used to trace if pci dev is added to bus, all funcs in same slot would be added to bus in enable_device(slot). So we need to clean all funcs of same slot in disable_device(slot). But hot-remove exists bug: https://bugzilla.kernel.org/show_bug.cgi?id=43219 (dmesg and DSDT were attached in bz), detail: . Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ . Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | . Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device in pciphp spec, seabios only defines one device for a slot in ACPI DSDT table. In acpiphp_glue.c:register_slot(), only one entry is added (for func#0) into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); Hotpluging multifunc of guests(WinXp/Win7) is fine. --- v1 thread: http://marc.info/?t=131597601700003&r=1&w=2 Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Changes from v2: - update detail reason(calltrace) to commitlog - remove hardcode 8, find funcs in pci devlist Changes from v3: - use pci_bus_sem lock when walking bus->devices list Signed-off-by: Amos Kong <kongjianjun@gmail.com> --- drivers/pci/hotplug/acpiphp_glue.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..7109506 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -893,6 +893,7 @@ static int disable_device(struct acpiphp_slot *slot) pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); if (!pdev) goto err_exit; + pci_dev_put(pdev); list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { @@ -902,9 +903,20 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { + while (1) { + /* pci_bus_sem is used to protect bus->devices list, + it may cause invalid memory access if threads + modify bus->devices list concurrently. */ + down_read(&pci_bus_sem); + list_for_each_entry(pdev, &bus->devices, bus_list) + if (PCI_SLOT(pdev->devfn) == slot->device) { + pci_dev_get(pdev); + break; + } + up_read(&pci_bus_sem); + + if (PCI_SLOT(pdev->devfn) != slot->device) + break; pci_stop_bus_device(pdev); if (pdev->subordinate) { disable_bridges(pdev->subordinate); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [Qemu-devel] [PATCH v4] pci: clean all funcs when hot-removing multifunc device @ 2012-05-20 2:31 ` kongjianjun 0 siblings, 0 replies; 27+ messages in thread From: kongjianjun @ 2012-05-20 2:31 UTC (permalink / raw) To: liuj97, linux-pci, qemu-devel, jbarnes, kaneshige.kenji, bhelgaas Cc: Amos Kong From: Amos Kong <kongjianjun@gmail.com> Hotplug CallTrace: int acpiphp_enable_slot(struct acpiphp_slot *slot) \_enable_device(slot); \_pci_bus_add_devices(bus); # un-added new devs(all funcs in slot) will be added list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->is_added) continue; pci_bus_add_device(dev); device_add(&dev->dev); dev->is_added = 1; 'dev->is_added' is used to trace if pci dev is added to bus, all funcs in same slot would be added to bus in enable_device(slot). So we need to clean all funcs of same slot in disable_device(slot). But hot-remove exists bug: https://bugzilla.kernel.org/show_bug.cgi?id=43219 (dmesg and DSDT were attached in bz), detail: . Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ . Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | . Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device in pciphp spec, seabios only defines one device for a slot in ACPI DSDT table. In acpiphp_glue.c:register_slot(), only one entry is added (for func#0) into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); Hotpluging multifunc of guests(WinXp/Win7) is fine. --- v1 thread: http://marc.info/?t=131597601700003&r=1&w=2 Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Changes from v2: - update detail reason(calltrace) to commitlog - remove hardcode 8, find funcs in pci devlist Changes from v3: - use pci_bus_sem lock when walking bus->devices list Signed-off-by: Amos Kong <kongjianjun@gmail.com> --- drivers/pci/hotplug/acpiphp_glue.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..7109506 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -893,6 +893,7 @@ static int disable_device(struct acpiphp_slot *slot) pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); if (!pdev) goto err_exit; + pci_dev_put(pdev); list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { @@ -902,9 +903,20 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { + while (1) { + /* pci_bus_sem is used to protect bus->devices list, + it may cause invalid memory access if threads + modify bus->devices list concurrently. */ + down_read(&pci_bus_sem); + list_for_each_entry(pdev, &bus->devices, bus_list) + if (PCI_SLOT(pdev->devfn) == slot->device) { + pci_dev_get(pdev); + break; + } + up_read(&pci_bus_sem); + + if (PCI_SLOT(pdev->devfn) != slot->device) + break; pci_stop_bus_device(pdev); if (pdev->subordinate) { disable_bridges(pdev->subordinate); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device 2012-05-16 15:26 ` Bjorn Helgaas @ 2012-05-20 2:36 ` Amos Kong -1 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-20 2:36 UTC (permalink / raw) To: Bjorn Helgaas Cc: Jiang Liu, Amos Kong, Michael S. Tsirkin, linux-pci, qemu-devel, jbarnes, alex.williamson, kevin, Yinghai Lu, Kenji Kaneshige On Wed, May 16, 2012 at 11:26 PM, Bjorn Helgaas <bhelgaas@google.com> wrote: > On Fri, May 11, 2012 at 8:00 AM, Jiang Liu <liuj97@gmail.com> wrote: >> On 05/11/2012 08:24 AM, Amos Kong wrote: >>> On 05/11/2012 07:54 AM, Amos Kong wrote: >>>> On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >>>>> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>>>>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>>>>> >>>>>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>>>>> index 806c44f..a7442d9 100644 >>>>>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>>>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>>>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>>>>> static int disable_device(struct acpiphp_slot *slot) >>>>>>> { >>>>>>> struct acpiphp_func *func; >>>>>>> - struct pci_dev *pdev; >>>>>>> + struct pci_dev *pdev, *tmp; >>>>>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>>>>> >>>>>>> /* The slot will be enabled when func 0 is added, so check >>>>>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>>>>> func->bridge = NULL; >>>>>>> } >>>>>>> >>>>>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>>>>> - PCI_DEVFN(slot->device, func->function)); >>>>>>> - if (pdev) { >>>>>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>>>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>>>>> + continue; > > I think the concept is good: in enable_device(), we use > pci_scan_slot(), which scans all possible functions in the slot. So > in disable_device() we should do something symmetric to remove all the > functions. Right! >>>>>>> + >>>>>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>>>>> Otherwise it may cause invalid memory access if another thread is modifying >>>>>> the bus->devices list concurrently. >>> >>> pci_bus_sem lock is only request for writing &bus->devices list, right ? >>> and this protection already exists in pci_destory_dev(). >> That's for writer. For reader to walk the pci_bus->devices list, you also need >> to acquire the reader lock by down_read(&pci_bus_sem). Please refer to >> pci_get_slot() for example. This especially import for native OS because there >> may be multiple PCI slots/devices on the bus. > > There is a lot of existing code that walks bus->devices without > holding pci_bus_sem, but most of it is boot-time code that is arguably > safe (though I think things like pcibios_fixup_bus() are poorly > designed and don't fit well in the hotplug-enabled world). disable_remove() is not boot-time code, we might hot-remove devices when system is running. > In this case, I do think we need to protect against updates while > we're walking bus->devices. It's probably not trivial because > __pci_remove_bus_device() calls pci_destroy_dev(), where we do the > down_write(), so simply wrapping the whole thing with down_read() will > cause a deadlock. I posted a V4 to add pci_bus_sem protection , please help to review. Thanks for Jiang Liu's guide. > Kenji-san, Yinghai, do you have any input? > > Bjorn ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [Qemu-devel] [PATCH v3] pci: clean all funcs when hot-removing multifunc device @ 2012-05-20 2:36 ` Amos Kong 0 siblings, 0 replies; 27+ messages in thread From: Amos Kong @ 2012-05-20 2:36 UTC (permalink / raw) To: Bjorn Helgaas Cc: Michael S. Tsirkin, linux-pci, qemu-devel, jbarnes, alex.williamson, kevin, Kenji Kaneshige, Amos Kong, Yinghai Lu, Jiang Liu On Wed, May 16, 2012 at 11:26 PM, Bjorn Helgaas <bhelgaas@google.com> wrote: > On Fri, May 11, 2012 at 8:00 AM, Jiang Liu <liuj97@gmail.com> wrote: >> On 05/11/2012 08:24 AM, Amos Kong wrote: >>> On 05/11/2012 07:54 AM, Amos Kong wrote: >>>> On 05/11/2012 02:55 AM, Michael S. Tsirkin wrote: >>>>> On Fri, May 11, 2012 at 01:09:13AM +0800, Jiang Liu wrote: >>>>>> On 05/10/2012 11:44 PM, Amos Kong wrote: >>>>>> >>>>>>> diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c >>>>>>> index 806c44f..a7442d9 100644 >>>>>>> --- a/drivers/pci/hotplug/acpiphp_glue.c >>>>>>> +++ b/drivers/pci/hotplug/acpiphp_glue.c >>>>>>> @@ -885,7 +885,7 @@ static void disable_bridges(struct pci_bus *bus) >>>>>>> static int disable_device(struct acpiphp_slot *slot) >>>>>>> { >>>>>>> struct acpiphp_func *func; >>>>>>> - struct pci_dev *pdev; >>>>>>> + struct pci_dev *pdev, *tmp; >>>>>>> struct pci_bus *bus = slot->bridge->pci_bus; >>>>>>> >>>>>>> /* The slot will be enabled when func 0 is added, so check >>>>>>> @@ -902,9 +902,10 @@ static int disable_device(struct acpiphp_slot *slot) >>>>>>> func->bridge = NULL; >>>>>>> } >>>>>>> >>>>>>> - pdev = pci_get_slot(slot->bridge->pci_bus, >>>>>>> - PCI_DEVFN(slot->device, func->function)); >>>>>>> - if (pdev) { >>>>>>> + list_for_each_entry_safe(pdev, tmp, &bus->devices, bus_list) { >>>>>>> + if (PCI_SLOT(pdev->devfn) != slot->device) >>>>>>> + continue; > > I think the concept is good: in enable_device(), we use > pci_scan_slot(), which scans all possible functions in the slot. So > in disable_device() we should do something symmetric to remove all the > functions. Right! >>>>>>> + >>>>>> The pci_bus_sem lock should be acquired when walking the bus->devices list. >>>>>> Otherwise it may cause invalid memory access if another thread is modifying >>>>>> the bus->devices list concurrently. >>> >>> pci_bus_sem lock is only request for writing &bus->devices list, right ? >>> and this protection already exists in pci_destory_dev(). >> That's for writer. For reader to walk the pci_bus->devices list, you also need >> to acquire the reader lock by down_read(&pci_bus_sem). Please refer to >> pci_get_slot() for example. This especially import for native OS because there >> may be multiple PCI slots/devices on the bus. > > There is a lot of existing code that walks bus->devices without > holding pci_bus_sem, but most of it is boot-time code that is arguably > safe (though I think things like pcibios_fixup_bus() are poorly > designed and don't fit well in the hotplug-enabled world). disable_remove() is not boot-time code, we might hot-remove devices when system is running. > In this case, I do think we need to protect against updates while > we're walking bus->devices. It's probably not trivial because > __pci_remove_bus_device() calls pci_destroy_dev(), where we do the > down_write(), so simply wrapping the whole thing with down_read() will > cause a deadlock. I posted a V4 to add pci_bus_sem protection , please help to review. Thanks for Jiang Liu's guide. > Kenji-san, Yinghai, do you have any input? > > Bjorn ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v5] pci: clean all funcs when hot-removing multifunc device 2012-05-16 15:26 ` Bjorn Helgaas @ 2012-05-20 2:59 ` kongjianjun -1 siblings, 0 replies; 27+ messages in thread From: kongjianjun @ 2012-05-20 2:59 UTC (permalink / raw) To: liuj97, linux-pci, qemu-devel, jbarnes, kaneshige.kenji, bhelgaas Cc: Amos Kong From: Amos Kong <kongjianjun@gmail.com> Hotplug CallTrace: int acpiphp_enable_slot(struct acpiphp_slot *slot) \_enable_device(slot); \_pci_bus_add_devices(bus); # un-added new devs(all funcs in slot) will be added list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->is_added) continue; pci_bus_add_device(dev); device_add(&dev->dev); dev->is_added = 1; 'dev->is_added' is used to trace if pci dev is added to bus, all funcs in same slot would be added to bus in enable_device(slot). So we need to clean all funcs of same slot in disable_device(slot). But hot-remove exists bug: https://bugzilla.kernel.org/show_bug.cgi?id=43219 (dmesg and DSDT were attached in bz), detail: . Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ . Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | . Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device in pciphp spec, seabios only defines one device for a slot in ACPI DSDT table. In acpiphp_glue.c:register_slot(), only one entry is added (for func#0) into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); Hotpluging multifunc of guests(WinXp/Win7) is fine. Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Changes from v2: - update detail reason(calltrace) to commitlog - remove hardcode 8, find funcs in pci devlist Changes from v3: - use pci_bus_sem lock when walking bus->devices list Changes from V4: - check null point of pdev Signed-off-by: Amos Kong <kongjianjun@gmail.com> --- drivers/pci/hotplug/acpiphp_glue.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..ba46724 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -893,6 +893,7 @@ static int disable_device(struct acpiphp_slot *slot) pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); if (!pdev) goto err_exit; + pci_dev_put(pdev); list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { @@ -902,9 +903,20 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { + while (1) { + /* pci_bus_sem is used to protect bus->devices list, + it may cause invalid memory access if threads + modify bus->devices list concurrently. */ + down_read(&pci_bus_sem); + list_for_each_entry(pdev, &bus->devices, bus_list) + if (PCI_SLOT(pdev->devfn) == slot->device) { + pci_dev_get(pdev); + break; + } + up_read(&pci_bus_sem); + + if (!pdev || PCI_SLOT(pdev->devfn) != slot->device) + break; pci_stop_bus_device(pdev); if (pdev->subordinate) { disable_bridges(pdev->subordinate); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* [Qemu-devel] [PATCH v5] pci: clean all funcs when hot-removing multifunc device @ 2012-05-20 2:59 ` kongjianjun 0 siblings, 0 replies; 27+ messages in thread From: kongjianjun @ 2012-05-20 2:59 UTC (permalink / raw) To: liuj97, linux-pci, qemu-devel, jbarnes, kaneshige.kenji, bhelgaas Cc: Amos Kong From: Amos Kong <kongjianjun@gmail.com> Hotplug CallTrace: int acpiphp_enable_slot(struct acpiphp_slot *slot) \_enable_device(slot); \_pci_bus_add_devices(bus); # un-added new devs(all funcs in slot) will be added list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->is_added) continue; pci_bus_add_device(dev); device_add(&dev->dev); dev->is_added = 1; 'dev->is_added' is used to trace if pci dev is added to bus, all funcs in same slot would be added to bus in enable_device(slot). So we need to clean all funcs of same slot in disable_device(slot). But hot-remove exists bug: https://bugzilla.kernel.org/show_bug.cgi?id=43219 (dmesg and DSDT were attached in bz), detail: . Boot up a Linux VM with 8 pci block devices which are the 8 functions in one pci slot. | # qemu-kvm ... | -drive file=images/u0,if=none,id=drv0,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv0,id=v0,multifunction=on,addr=0x03.0 \ | .... | -drive file=images/u7,if=none,id=drv7,format=qcow2,cache=none \ | -device virtio-blk-pci,drive=drv7,id=v7,multifunction=on,addr=0x03.7 \ . Check devices in guest. | vm)# ls /dev/vd* | vda vdb vdc vde vdf vdg vdh | vm)# lspci |grep block | 00:03.0 SCSI storage controller: Red Hat, Inc Virtio block device | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device | . Func1~7 still exist in guest after hot-removing the whole slot by qemu monitor cmd. | vm)# lspci |grep block (00:03.0 disappeared) | 00:03.1 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | ... | 00:03.7 SCSI storage controller: Red Hat, Inc Virtio block device (rev ff) | vm)# ls /dev/vd* (vda disappeared) | vdb vdc vde vdf vdg vdh | vm)# mkfs /dev/vdb | INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. We process pci slot as a whole device in pciphp spec, seabios only defines one device for a slot in ACPI DSDT table. In acpiphp_glue.c:register_slot(), only one entry is added (for func#0) into 'slot->funcs' list. When we release the whole slot, only the entry in 'slot->funcs' will be cleaned, so func#1~7 could not be cleaned from system. | drivers/pci/hotplug/acpiphp_glue.c: | static int disable_device(struct acpiphp_slot *slot) { | list_for_each_entry(func, &slot->funcs, sibling) { | pdev = pci_get_slot(slot->bridge->pci_bus, | PCI_DEVFN(slot->device, func->function)); | ..clean code.. // those code is only executed 1 time(for func#0) | __pci_remove_bus_device(pdev); | pci_dev_put(pdev); Hotpluging multifunc of guests(WinXp/Win7) is fine. Changes from v1: - rebase patch to latest linux.git - remove unnecessary multiplefunction check - rename 'i' to meaningful 'fn' - fix coding style Changes from v2: - update detail reason(calltrace) to commitlog - remove hardcode 8, find funcs in pci devlist Changes from v3: - use pci_bus_sem lock when walking bus->devices list Changes from V4: - check null point of pdev Signed-off-by: Amos Kong <kongjianjun@gmail.com> --- drivers/pci/hotplug/acpiphp_glue.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44f..ba46724 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -893,6 +893,7 @@ static int disable_device(struct acpiphp_slot *slot) pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); if (!pdev) goto err_exit; + pci_dev_put(pdev); list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { @@ -902,9 +903,20 @@ static int disable_device(struct acpiphp_slot *slot) func->bridge = NULL; } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { + while (1) { + /* pci_bus_sem is used to protect bus->devices list, + it may cause invalid memory access if threads + modify bus->devices list concurrently. */ + down_read(&pci_bus_sem); + list_for_each_entry(pdev, &bus->devices, bus_list) + if (PCI_SLOT(pdev->devfn) == slot->device) { + pci_dev_get(pdev); + break; + } + up_read(&pci_bus_sem); + + if (!pdev || PCI_SLOT(pdev->devfn) != slot->device) + break; pci_stop_bus_device(pdev); if (pdev->subordinate) { disable_bridges(pdev->subordinate); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 27+ messages in thread
[parent not found: <4E7045C3.6040604@jp.fujitsu.com>]
* Re: [PATCH] pci: clean all funcs when hot-removing multifunc device [not found] <4E7045C3.6040604@jp.fujitsu.com> @ 2011-09-14 8:13 ` Amos Kong 2011-09-14 11:45 ` Amos Kong 0 siblings, 1 reply; 27+ messages in thread From: Amos Kong @ 2011-09-14 8:13 UTC (permalink / raw) To: Kenji Kaneshige; +Cc: linux-pci, jbarnes, mst, alex williamson, mtosatti, kvm ----- Original Message ----- > (2011/09/14 13:55), Amos Kong wrote: > > 'slot->funcs' is initialized in acpiphp_glue.c:register_slot() > > before > > hotpluging device, and only one entry(func 0) is added to it, > > no new entry will be added to the list when hotpluging devices to > > the slot. > > I guess your hotplug slot has only one device object (for func#0) > in ACPI Namespace (DSDT), and guess this is why there is only one > entry in the 'slot->funcs'. If so, what about adding device objects > for function 1-7 to ACPI Namespace? I think most of bare-metal > environments have such definition in ACPI Namespace. For example: Hi Kaneshige, I did some test, fix acpi tables can resolve this problem, then register_slot() will be executed for all funcs, and each func has a entry in slot->funcs. I will send a patch to seabios. Thanks a lot! Amos > Device (P2P) { // PCI to PCI bridge > Name (_ADR, ...) // PCI address > Name (_HPP, ...) // Hot Plug parameter > ... > Device (S0F0) { // For function 0 > Name (_ADR, ...) > Name (_SUN, ...) > Method (_EJ0, ...) > } > Device (S0F1) { // For function 1 > ... > } > ... > Device (S0F7) { // For function 7 > ... > } > } > > Regards, > Kenji Kaneshige > > > > When we release the whole device, there is only one entry in the > > list, > > this causes func1~7 could not be released. > > I try to add entries for all hotpluged device in enable_device(), > > but > > it doesn't work, because 'slot->funcs' is used in many place which > > we only > > need to process func 0. This patch just try to clean all funcs in > > disable_device(). > > > > drivers/pci/hotplug/acpiphp_glue.c: > > static int disable_device(struct acpiphp_slot *slot) { > > list_for_each_entry(func,&slot->funcs, sibling) { > > pdev = pci_get_slot(slot->bridge->pci_bus, > > PCI_DEVFN(slot->device, func->function)); > > ..clean code.. // those code can only be executed one time(func 0) > > pci_remove_bus_device(pdev); > > --- > > pci_bus_add_device() is called for each func device in > > acpiphp_glue.c:enable_device(). > > pci_remove_bus_device(pdev) is only called for func 0 in > > acpiphp_glue.c:disable_device(). > > > > Boot up a KVM guest, hotplug a multifunc device(8 funcs), we can > > find it in the guest. > > @ ls /dev/vd* > > vda vdb vdc vde vdf vdg vdh > > @ lspci > > 00:06.0 SCSI storage controller: Red Hat, Inc Virtio block > > device > > ... > > 00:06.7 SCSI storage controller: Red Hat, Inc Virtio block > > device > > > > But func 1~7 still exist in guest after hot-removing the multifunc > > device through qemu monitor. > > @ lspci (00:06.0 disappeared) > > 00:06.1 SCSI storage controller: Red Hat, Inc Virtio block > > device (rev ff) > > ... > > 00:06.7 SCSI storage controller: Red Hat, Inc Virtio block > > device (rev ff) > > ^^^^^^^^ > > @ ls /dev/vd* > > vdb vdc vde vdf vdg vdh > > @ mkfs /dev/vdb > > INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. > > (task hung) > > > > Hotpluging multifunc of WinXp is fine. > > > > Signed-off-by: Amos Kong<akong@redhat.com> > > --- > > drivers/pci/hotplug/acpiphp_glue.c | 27 > > ++++++++++++++++++--------- > > 1 files changed, 18 insertions(+), 9 deletions(-) > > > > diff --git a/drivers/pci/hotplug/acpiphp_glue.c > > b/drivers/pci/hotplug/acpiphp_glue.c > > index a70fa89..3b86d1a 100644 > > --- a/drivers/pci/hotplug/acpiphp_glue.c > > +++ b/drivers/pci/hotplug/acpiphp_glue.c > > @@ -880,6 +880,8 @@ static int disable_device(struct acpiphp_slot > > *slot) > > { > > struct acpiphp_func *func; > > struct pci_dev *pdev; > > + struct pci_bus *bus = slot->bridge->pci_bus; > > + int i, num = 1; > > > > /* is this slot already disabled? */ > > if (!(slot->flags& SLOT_ENABLED)) > > @@ -893,16 +895,23 @@ static int disable_device(struct acpiphp_slot > > *slot) > > func->bridge = NULL; > > } > > > > - pdev = pci_get_slot(slot->bridge->pci_bus, > > - PCI_DEVFN(slot->device, func->function)); > > - if (pdev) { > > - pci_stop_bus_device(pdev); > > - if (pdev->subordinate) { > > - disable_bridges(pdev->subordinate); > > - pci_disable_device(pdev); > > + pdev = pci_scan_single_device(bus, > > + PCI_DEVFN(slot->device, 0)); > > + if (!pdev) > > + goto err_exit; > > + if (pdev->multifunction == 1) > > + num = 8; > > + for (i=0; i<num; i++) { > > + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, i)); > > + if (pdev) { > > + pci_stop_bus_device(pdev); > > + if (pdev->subordinate) { > > + disable_bridges(pdev->subordinate); > > + pci_disable_device(pdev); > > + } > > + pci_remove_bus_device(pdev); > > + pci_dev_put(pdev); > > } > > - pci_remove_bus_device(pdev); > > - pci_dev_put(pdev); > > } > > } > > ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH] pci: clean all funcs when hot-removing multifunc device 2011-09-14 8:13 ` [PATCH] " Amos Kong @ 2011-09-14 11:45 ` Amos Kong 2011-09-15 1:59 ` Kevin O'Connor 0 siblings, 1 reply; 27+ messages in thread From: Amos Kong @ 2011-09-14 11:45 UTC (permalink / raw) To: Kenji Kaneshige Cc: linux-pci, jbarnes, mst, alex williamson, mtosatti, kvm, seabios [-- Attachment #1: Type: text/plain, Size: 5970 bytes --] ----- Original Message ----- > ----- Original Message ----- > > (2011/09/14 13:55), Amos Kong wrote: > > > 'slot->funcs' is initialized in acpiphp_glue.c:register_slot() > > > before > > > hotpluging device, and only one entry(func 0) is added to it, > > > no new entry will be added to the list when hotpluging devices to > > > the slot. > > > > I guess your hotplug slot has only one device object (for func#0) > > in ACPI Namespace (DSDT), and guess this is why there is only one > > entry in the 'slot->funcs'. If so, what about adding device objects > > for function 1-7 to ACPI Namespace? I think most of bare-metal > > environments have such definition in ACPI Namespace. For example: > > Hi Kaneshige, > > I did some test, fix acpi tables can resolve this problem, > then register_slot() will be executed for all funcs, > and each func has a entry in slot->funcs. > I will send a patch to seabios. The size of bios.bin compiled from seabios original: 128K only apply patch1: 256K only apply patch2: 128K patch1: add 6 slot(only slot6 has 8 funcs) to the table can hotplug/hot-remove a multifunc device to slot 6 successfully patch2: add 31 slot(with 8 funcs) to the table could not boot up guest. I found there is a special process for large bios.bin in qemu, problem maybe exist here, I'm driving into it... qemu/hw/pc.c: void pc_memory_init(... .... /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = bios_size; if (isa_bios_size > (128 * 1024)) isa_bios_size = 128 * 1024; > > > Device (P2P) { // PCI to PCI bridge > > Name (_ADR, ...) // PCI address > > Name (_HPP, ...) // Hot Plug parameter > > ... > > Device (S0F0) { // For function 0 > > Name (_ADR, ...) > > Name (_SUN, ...) > > Method (_EJ0, ...) > > } > > Device (S0F1) { // For function 1 > > ... > > } > > ... > > Device (S0F7) { // For function 7 > > ... > > } > > } > > > > Regards, > > Kenji Kaneshige > > > > > > > When we release the whole device, there is only one entry in the > > > list, > > > this causes func1~7 could not be released. > > > I try to add entries for all hotpluged device in enable_device(), > > > but > > > it doesn't work, because 'slot->funcs' is used in many place which > > > we only > > > need to process func 0. This patch just try to clean all funcs in > > > disable_device(). > > > > > > drivers/pci/hotplug/acpiphp_glue.c: > > > static int disable_device(struct acpiphp_slot *slot) { > > > list_for_each_entry(func,&slot->funcs, sibling) { > > > pdev = pci_get_slot(slot->bridge->pci_bus, > > > PCI_DEVFN(slot->device, func->function)); > > > ..clean code.. // those code can only be executed one time(func > > > 0) > > > pci_remove_bus_device(pdev); > > > --- > > > pci_bus_add_device() is called for each func device in > > > acpiphp_glue.c:enable_device(). > > > pci_remove_bus_device(pdev) is only called for func 0 in > > > acpiphp_glue.c:disable_device(). > > > > > > Boot up a KVM guest, hotplug a multifunc device(8 funcs), we can > > > find it in the guest. > > > @ ls /dev/vd* > > > vda vdb vdc vde vdf vdg vdh > > > @ lspci > > > 00:06.0 SCSI storage controller: Red Hat, Inc Virtio block > > > device > > > ... > > > 00:06.7 SCSI storage controller: Red Hat, Inc Virtio block > > > device > > > > > > But func 1~7 still exist in guest after hot-removing the multifunc > > > device through qemu monitor. > > > @ lspci (00:06.0 disappeared) > > > 00:06.1 SCSI storage controller: Red Hat, Inc Virtio block > > > device (rev ff) > > > ... > > > 00:06.7 SCSI storage controller: Red Hat, Inc Virtio block > > > device (rev ff) > > > ^^^^^^^^ > > > @ ls /dev/vd* > > > vdb vdc vde vdf vdg vdh > > > @ mkfs /dev/vdb > > > INFO: task mkfs.ext2:1784 blocked for more than 120 seconds. > > > (task hung) > > > > > > Hotpluging multifunc of WinXp is fine. > > > > > > Signed-off-by: Amos Kong<akong@redhat.com> > > > --- > > > drivers/pci/hotplug/acpiphp_glue.c | 27 > > > ++++++++++++++++++--------- > > > 1 files changed, 18 insertions(+), 9 deletions(-) > > > > > > diff --git a/drivers/pci/hotplug/acpiphp_glue.c > > > b/drivers/pci/hotplug/acpiphp_glue.c > > > index a70fa89..3b86d1a 100644 > > > --- a/drivers/pci/hotplug/acpiphp_glue.c > > > +++ b/drivers/pci/hotplug/acpiphp_glue.c > > > @@ -880,6 +880,8 @@ static int disable_device(struct acpiphp_slot > > > *slot) > > > { > > > struct acpiphp_func *func; > > > struct pci_dev *pdev; > > > + struct pci_bus *bus = slot->bridge->pci_bus; > > > + int i, num = 1; > > > > > > /* is this slot already disabled? */ > > > if (!(slot->flags& SLOT_ENABLED)) > > > @@ -893,16 +895,23 @@ static int disable_device(struct > > > acpiphp_slot > > > *slot) > > > func->bridge = NULL; > > > } > > > > > > - pdev = pci_get_slot(slot->bridge->pci_bus, > > > - PCI_DEVFN(slot->device, func->function)); > > > - if (pdev) { > > > - pci_stop_bus_device(pdev); > > > - if (pdev->subordinate) { > > > - disable_bridges(pdev->subordinate); > > > - pci_disable_device(pdev); > > > + pdev = pci_scan_single_device(bus, > > > + PCI_DEVFN(slot->device, 0)); > > > + if (!pdev) > > > + goto err_exit; > > > + if (pdev->multifunction == 1) > > > + num = 8; > > > + for (i=0; i<num; i++) { > > > + pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, i)); > > > + if (pdev) { > > > + pci_stop_bus_device(pdev); > > > + if (pdev->subordinate) { > > > + disable_bridges(pdev->subordinate); > > > + pci_disable_device(pdev); > > > + } > > > + pci_remove_bus_device(pdev); > > > + pci_dev_put(pdev); > > > } > > > - pci_remove_bus_device(pdev); > > > - pci_dev_put(pdev); > > > } > > > } > > > > -- > To unsubscribe from this list: send the line "unsubscribe kvm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html [-- Attachment #2: patch1.txt --] [-- Type: text/plain, Size: 2325 bytes --] diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 08412e2..c1504d0 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -128,9 +128,9 @@ DefinitionBlock ( PCRM, 32, } -#define hotplug_slot(name, nr) \ - Device (S##name) { \ - Name (_ADR, nr##0000) \ +#define hotplug_func(name, nr, fn) \ + Device (S##name##fn) { \ + Name (_ADR, nr##fn) \ Method (_EJ0,1) { \ Store(ShiftLeft(1, nr), B0EJ) \ Return (0x0) \ @@ -138,6 +138,16 @@ DefinitionBlock ( Name (_SUN, name) \ } +#define hotplug_slot(name, nr) \ + hotplug_func(name, nr##0000, 0) \ + hotplug_func(name, nr##0001, 1) \ + hotplug_func(name, nr##0002, 2) \ + hotplug_func(name, nr##0003, 3) \ + hotplug_func(name, nr##0004, 4) \ + hotplug_func(name, nr##0005, 5) \ + hotplug_func(name, nr##0006, 6) \ + hotplug_func(name, nr##0007, 7) + hotplug_slot(1, 0x0001) hotplug_slot(2, 0x0002) hotplug_slot(3, 0x0003) @@ -842,13 +852,22 @@ DefinitionBlock ( Return(0x01) } -#define gen_pci_hotplug(nr) \ +#define gen_pci_hotplug_func(nr, fn) \ If (And(\_SB.PCI0.PCIU, ShiftLeft(1, nr))) { \ - Notify(\_SB.PCI0.S##nr, 1) \ + Notify(\_SB.PCI0.S##nr##fn, 1) \ } \ If (And(\_SB.PCI0.PCID, ShiftLeft(1, nr))) { \ - Notify(\_SB.PCI0.S##nr, 3) \ + Notify(\_SB.PCI0.S##nr##fn, 3) \ } +#define gen_pci_hotplug(nr) \ + gen_pci_hotplug_func(nr, 0) \ + gen_pci_hotplug_func(nr, 1) \ + gen_pci_hotplug_func(nr, 2) \ + gen_pci_hotplug_func(nr, 3) \ + gen_pci_hotplug_func(nr, 4) \ + gen_pci_hotplug_func(nr, 5) \ + gen_pci_hotplug_func(nr, 6) \ + gen_pci_hotplug_func(nr, 7) Method(_L01) { gen_pci_hotplug(1) [-- Attachment #3: patch2.txt --] [-- Type: text/plain, Size: 3968 bytes --] diff --git a/src/acpi-dsdt.dsl b/src/acpi-dsdt.dsl index 08412e2..10b651e 100644 --- a/src/acpi-dsdt.dsl +++ b/src/acpi-dsdt.dsl @@ -128,9 +128,9 @@ DefinitionBlock ( PCRM, 32, } -#define hotplug_slot(name, nr) \ - Device (S##name) { \ - Name (_ADR, nr##0000) \ +#define hotplug_func(name, nr, fn) \ + Device (S##name##fn) { \ + Name (_ADR, nr) \ Method (_EJ0,1) { \ Store(ShiftLeft(1, nr), B0EJ) \ Return (0x0) \ @@ -138,13 +138,30 @@ DefinitionBlock ( Name (_SUN, name) \ } +#define hotplug_slot(name, nr) \ + hotplug_func(name, nr##0000, 0)/* \ + hotplug_func(name, nr##0001, 1) \ + hotplug_func(name, nr##0002, 2) \ + hotplug_func(name, nr##0003, 3) \ + hotplug_func(name, nr##0004, 4) \ + hotplug_func(name, nr##0005, 5) \ + hotplug_func(name, nr##0006, 6) \ + hotplug_func(name, nr##0007, 7)*/ + hotplug_slot(1, 0x0001) hotplug_slot(2, 0x0002) hotplug_slot(3, 0x0003) hotplug_slot(4, 0x0004) hotplug_slot(5, 0x0005) - hotplug_slot(6, 0x0006) - hotplug_slot(7, 0x0007) + hotplug_func(6, 0x00060000, 0) + hotplug_func(6, 0x00060001, 1) + hotplug_func(6, 0x00060002, 2) + hotplug_func(6, 0x00060003, 3) + hotplug_func(6, 0x00060004, 4) + hotplug_func(6, 0x00060005, 5) + hotplug_func(6, 0x00060006, 6) + hotplug_func(6, 0x00060007, 7) + /*hotplug_slot(7, 0x0007) hotplug_slot(8, 0x0008) hotplug_slot(9, 0x0009) hotplug_slot(10, 0x000a) @@ -168,7 +185,7 @@ DefinitionBlock ( hotplug_slot(28, 0x001c) hotplug_slot(29, 0x001d) hotplug_slot(30, 0x001e) - hotplug_slot(31, 0x001f) + hotplug_slot(31, 0x001f)*/ Name (_CRS, ResourceTemplate () { @@ -842,13 +859,22 @@ DefinitionBlock ( Return(0x01) } -#define gen_pci_hotplug(nr) \ +#define gen_pci_hotplug_func(nr, fn) \ If (And(\_SB.PCI0.PCIU, ShiftLeft(1, nr))) { \ - Notify(\_SB.PCI0.S##nr, 1) \ + Notify(\_SB.PCI0.S##nr##fn, 1) \ } \ If (And(\_SB.PCI0.PCID, ShiftLeft(1, nr))) { \ - Notify(\_SB.PCI0.S##nr, 3) \ + Notify(\_SB.PCI0.S##nr##fn, 3) \ } +#define gen_pci_hotplug(nr) \ + gen_pci_hotplug_func(nr, 0)/* \ + gen_pci_hotplug_func(nr, 1) \ + gen_pci_hotplug_func(nr, 2) \ + gen_pci_hotplug_func(nr, 3) \ + gen_pci_hotplug_func(nr, 4) \ + gen_pci_hotplug_func(nr, 5) \ + gen_pci_hotplug_func(nr, 6) \ + gen_pci_hotplug_func(nr, 7)*/ Method(_L01) { gen_pci_hotplug(1) @@ -856,8 +882,15 @@ DefinitionBlock ( gen_pci_hotplug(3) gen_pci_hotplug(4) gen_pci_hotplug(5) - gen_pci_hotplug(6) - gen_pci_hotplug(7) + gen_pci_hotplug_func(6, 0) + gen_pci_hotplug_func(6, 1) + gen_pci_hotplug_func(6, 2) + gen_pci_hotplug_func(6, 3) + gen_pci_hotplug_func(6, 4) + gen_pci_hotplug_func(6, 5) + gen_pci_hotplug_func(6, 6) + gen_pci_hotplug_func(6, 7) + /*gen_pci_hotplug(7) gen_pci_hotplug(8) gen_pci_hotplug(9) gen_pci_hotplug(10) @@ -881,7 +914,7 @@ DefinitionBlock ( gen_pci_hotplug(28) gen_pci_hotplug(29) gen_pci_hotplug(30) - gen_pci_hotplug(31) + gen_pci_hotplug(31) */ Return (0x01) ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH] pci: clean all funcs when hot-removing multifunc device 2011-09-14 11:45 ` Amos Kong @ 2011-09-15 1:59 ` Kevin O'Connor 0 siblings, 0 replies; 27+ messages in thread From: Kevin O'Connor @ 2011-09-15 1:59 UTC (permalink / raw) To: Amos Kong Cc: Kenji Kaneshige, linux-pci, jbarnes, mst, alex williamson, mtosatti, kvm, seabios On Wed, Sep 14, 2011 at 07:45:59AM -0400, Amos Kong wrote: > The size of bios.bin compiled from seabios > original: 128K > only apply patch1: 256K > only apply patch2: 128K > > patch1: add 6 slot(only slot6 has 8 funcs) to the table > can hotplug/hot-remove a multifunc device to slot 6 successfully > > patch2: add 31 slot(with 8 funcs) to the table > could not boot up guest. > I found there is a special process for large bios.bin in qemu, > problem maybe exist here, I'm driving into it... > > qemu/hw/pc.c: > void pc_memory_init(... > .... > /* map the last 128KB of the BIOS in ISA space */ > isa_bios_size = bios_size; > if (isa_bios_size > (128 * 1024)) > isa_bios_size = 128 * 1024; This is probably a regression since seabios commit 87b533bf. Prior to that commit, seabios did not mark the early 32bit initialization code as init code. However, a side effect of marking that code (handle_post) as init code is that it is more likely the linker could place the code at an address less than 0xe0000. I'm guesing the patch below (just a hack) would cover up the issue. -Kevin --- a/src/post.c +++ b/src/post.c @@ -336,7 +336,7 @@ reloc_init(void) // Start of Power On Self Test (POST) - the BIOS initilization phase. // This function does the setup needed for code relocation, and then // invokes the relocation and main setup code. -void VISIBLE32INIT +void VISIBLE32FLAT handle_post(void) { debug_serial_setup(); @@ -356,6 +356,14 @@ handle_post(void) // Allow writes to modify bios area (0xf0000) make_bios_writable(); + + void handle_post2(void); + handle_post2(); +} + +void VISIBLE32INIT +handle_post2(void) +{ HaveRunPost = 1; // Detect ram and setup internal malloc. ^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2012-05-20 2:59 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1315976141-6684-1-git-send-email-akong@redhat.com>
2011-09-14 4:56 ` [PATCH] pci: clean all funcs when hot-removing multifunc device Amos Kong
2011-09-15 19:03 ` Bjorn Helgaas
2011-09-15 23:56 ` Kenji Kaneshige
2012-05-09 15:25 ` [PATCH v2] " Amos Kong
2012-05-09 15:25 ` [Qemu-devel] " Amos Kong
2012-05-10 15:44 ` [PATCH v3] " Amos Kong
2012-05-10 15:44 ` [Qemu-devel] " Amos Kong
2012-05-10 17:09 ` Jiang Liu
2012-05-10 17:09 ` [Qemu-devel] " Jiang Liu
2012-05-10 18:55 ` Michael S. Tsirkin
2012-05-10 18:55 ` [Qemu-devel] " Michael S. Tsirkin
2012-05-10 23:54 ` Amos Kong
2012-05-10 23:54 ` Amos Kong
2012-05-11 0:24 ` Amos Kong
2012-05-11 0:24 ` Amos Kong
2012-05-11 14:00 ` Jiang Liu
2012-05-16 15:26 ` Bjorn Helgaas
2012-05-16 15:26 ` Bjorn Helgaas
2012-05-20 2:31 ` [PATCH v4] " kongjianjun
2012-05-20 2:31 ` [Qemu-devel] " kongjianjun
2012-05-20 2:36 ` [Qemu-devel] [PATCH v3] " Amos Kong
2012-05-20 2:36 ` Amos Kong
2012-05-20 2:59 ` [PATCH v5] " kongjianjun
2012-05-20 2:59 ` [Qemu-devel] " kongjianjun
[not found] <4E7045C3.6040604@jp.fujitsu.com>
2011-09-14 8:13 ` [PATCH] " Amos Kong
2011-09-14 11:45 ` Amos Kong
2011-09-15 1:59 ` Kevin O'Connor
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.