diff mbox

`qdev_free` when unplug a pci device

Message ID 1298396180-23734-1-git-send-email-wdauchy@gmail.com
State New
Headers show

Commit Message

William Dauchy Feb. 22, 2011, 5:36 p.m. UTC
`qdev_free` when unplug a pci device
 It resolves a bug when detaching/attaching a network device

 # virsh detach-interface dom0 network --mac 52:54:00:f6:84:ba
 Interface detached successfully

 # virsh attach-interface dom0 network default --mac 52:54:00:f6:84:ba
 error: Failed to attach interface
 error: internal error unable to execute QEMU command 'device_add': Duplicate ID 'net0' for device

A detached pci device was not freed of the list `qemu_device_opts`
---
 hw/pci.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

Comments

Isaku Yamahata Feb. 23, 2011, 2:50 a.m. UTC | #1
On Tue, Feb 22, 2011 at 06:36:20PM +0100, William Dauchy wrote:
>  `qdev_free` when unplug a pci device
>  It resolves a bug when detaching/attaching a network device
> 
>  # virsh detach-interface dom0 network --mac 52:54:00:f6:84:ba
>  Interface detached successfully
> 
>  # virsh attach-interface dom0 network default --mac 52:54:00:f6:84:ba
>  error: Failed to attach interface
>  error: internal error unable to execute QEMU command 'device_add': Duplicate ID 'net0' for device
> 
> A detached pci device was not freed of the list `qemu_device_opts`
> ---
>  hw/pci.c |    5 ++++-
>  1 files changed, 4 insertions(+), 1 deletions(-)
> 
> diff --git a/hw/pci.c b/hw/pci.c
> index 8b76cea..1e9a8f0 100644
> --- a/hw/pci.c
> +++ b/hw/pci.c
> @@ -1671,13 +1671,16 @@ static int pci_unplug_device(DeviceState *qdev)
>  {
>      PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
>      PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
> +    int unplug;
>  
>      if (info->no_hotplug) {
>          qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
>          return -1;
>      }
> -    return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
> +    unplug = dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
>                               PCI_HOTPLUG_DISABLED);
> +    qdev_free(qdev);

Good catch.
qdev_free() should be called only when unplug had succeeded.
Although piix4 unplug always successes, pcie unplug may fail.

> +    return unplug;
>  }
>  
>  void pci_qdev_register(PCIDeviceInfo *info)
> -- 
> William
> 
>
Markus Armbruster Feb. 23, 2011, 8:30 a.m. UTC | #2
Isaku Yamahata <yamahata@valinux.co.jp> writes:

> On Tue, Feb 22, 2011 at 06:36:20PM +0100, William Dauchy wrote:
>>  `qdev_free` when unplug a pci device
>>  It resolves a bug when detaching/attaching a network device
>> 
>>  # virsh detach-interface dom0 network --mac 52:54:00:f6:84:ba
>>  Interface detached successfully
>> 
>>  # virsh attach-interface dom0 network default --mac 52:54:00:f6:84:ba
>>  error: Failed to attach interface
>>  error: internal error unable to execute QEMU command 'device_add': Duplicate ID 'net0' for device
>> 
>> A detached pci device was not freed of the list `qemu_device_opts`
>> ---
>>  hw/pci.c |    5 ++++-
>>  1 files changed, 4 insertions(+), 1 deletions(-)
>> 
>> diff --git a/hw/pci.c b/hw/pci.c
>> index 8b76cea..1e9a8f0 100644
>> --- a/hw/pci.c
>> +++ b/hw/pci.c
>> @@ -1671,13 +1671,16 @@ static int pci_unplug_device(DeviceState *qdev)
>>  {
>>      PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
>>      PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
>> +    int unplug;
>>  
>>      if (info->no_hotplug) {
>>          qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
>>          return -1;
>>      }
>> -    return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
>> +    unplug = dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
>>                               PCI_HOTPLUG_DISABLED);
>> +    qdev_free(qdev);
>
> Good catch.
> qdev_free() should be called only when unplug had succeeded.
> Although piix4 unplug always successes, pcie unplug may fail.
>
>> +    return unplug;
>>  }
>>  
>>  void pci_qdev_register(PCIDeviceInfo *info)

I don't think this patch is correct.  Let me explain.

Device hot unplug is *not* guaranteed to succeed.

For some buses, such as USB, it always succeeds immediately, i.e. when
the device_del monitor command finishes, the device is gone.  Live is
good.

But for PCI, device_del merely initiates the ACPI unplug rain dance.  It
doesn't wait for the dance to complete.  Why?  The dance can take an
unpredictable amount of time, including forever.

Problem: Subsequent device_add can fail if it reuses the qdev ID or PCI
slot, and the unplug has not yet completed (race condition), or it
failed.  Yes, Virginia, PCI hotplug *can* fail.

When unplug succeeds, the qdev is automatically destroyed.
pciej_write() does that for PIIX4.  Looks like pcie_cap_slot_event()
does it for PCIE.

Your patch adds a *second* qdev_free().  No good.
William Dauchy Feb. 23, 2011, 9:32 a.m. UTC | #3
Hi Markus,

On Wed, Feb 23, 2011 at 9:30 AM, Markus Armbruster <armbru@redhat.com> wrote:
> I don't think this patch is correct.  Let me explain.
>
> Device hot unplug is *not* guaranteed to succeed.
>
> For some buses, such as USB, it always succeeds immediately, i.e. when
> the device_del monitor command finishes, the device is gone.  Live is
> good.
>
> But for PCI, device_del merely initiates the ACPI unplug rain dance.  It
> doesn't wait for the dance to complete.  Why?  The dance can take an
> unpredictable amount of time, including forever.
>
> Problem: Subsequent device_add can fail if it reuses the qdev ID or PCI
> slot, and the unplug has not yet completed (race condition), or it
> failed.  Yes, Virginia, PCI hotplug *can* fail.
>
> When unplug succeeds, the qdev is automatically destroyed.
> pciej_write() does that for PIIX4.  Looks like pcie_cap_slot_event()
> does it for PCIE.
>
> Your patch adds a *second* qdev_free().  No good.

Thanks for the complete explanation. I now understand my mistake and
find out why it wasn't working as expected.
On a Linux virtual machine I forgot to load the `acpiphp` module which
makes the pci device disappear when doing a detach. I thought that,
even without this module the detach should have freed the
corresponding device on qemu side, regardless if the virtual machine
was supporting or not acpi signals.
Please ignore my patch.

Regards,
diff mbox

Patch

diff --git a/hw/pci.c b/hw/pci.c
index 8b76cea..1e9a8f0 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -1671,13 +1671,16 @@  static int pci_unplug_device(DeviceState *qdev)
 {
     PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
     PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
+    int unplug;
 
     if (info->no_hotplug) {
         qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
         return -1;
     }
-    return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
+    unplug = dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
                              PCI_HOTPLUG_DISABLED);
+    qdev_free(qdev);
+    return unplug;
 }
 
 void pci_qdev_register(PCIDeviceInfo *info)