Skip to contentSkip to conversation

qom: fix refcounting in object_property_del_child()

PATCH In review Amos Kong 5 participants 12 messages May 11, 2012 – Jun 7, 2012

lore
Share & tools
lore

Amos Kong

author
#0 lore raw

PATCH 1/1 qom: fix refcounting in object_property_del_child()

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

object_ref() is called in object_property_add_child(), but we don't unref it in object_property_del_child().

| (qemu) device_del virti0-0-0
| (qemu) **
| ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)
---
 qom/object.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
qom/object.c +1 -0
index e721fc2..9da6b59 100644
@@ -320,6 +320,7 @@ static void object_property_del_child(Object *obj, Object *child, Error **errp)
     QTAILQ_FOREACH(prop, &obj->properties, node) {
         if (strstart(prop->type, "child<", NULL) && prop->opaque == child) {
             object_property_del(obj, prop->name, errp);
+            object_unref(child);
             break;
         }
     }
#1 lore raw

Il 11/05/2012 04:15, Amos Kong ha scritto:

Quoted 20 lines

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

object_ref() is called in object_property_add_child(), but we don't unref it in object_property_del_child().

| (qemu) device_del virti0-0-0
| (qemu) **
| ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)
---
 qom/object.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
qom/object.c +0 -0
index e721fc2..9da6b59 100644

qom/object.c static void object_property_del_child(Object *obj, Object *child, Error **errp) · line 323

Quoted 4 lines
qom/object.c +1 -0
@@ -320,6 +320,7 @@ static void object_property_del_child(Object *obj, Object *child, Error **errp)
     QTAILQ_FOREACH(prop, &obj->properties, node) {
         if (strstart(prop->type, "child<", NULL) && prop->opaque == child) {
             object_property_del(obj, prop->name, errp);
+            object_unref(child);

This should be called by object_finalize_child_property instead, can you check why this is not the case?

Paolo

Quoted 4 lines
qom/object.c +0 -0
             break;
         }
     }
author
#2 lore raw
Quoted 16 lines · Paolo Bonzini

Il 11/05/2012 04:15, Amos Kong ha scritto:

Quoted 15 lines

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

object_ref() is called in object_property_add_child(), but we don't unref it in object_property_del_child().

| (qemu) device_del virti0-0-0
| (qemu) **
| ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)
---
 qom/object.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
Quoted 12 lines

qom/object.c static void object_property_del_child(Object *obj, Object *child, Error **errp) · line 323

Quoted 9 lines
qom/object.c +1 -0
index e721fc2..9da6b59 100644
@@ -320,6 +320,7 @@ static void object_property_del_child(Object *obj, Object *child, Error **errp)
     QTAILQ_FOREACH(prop, &obj->properties, node) {
         if (strstart(prop->type, "child<", NULL) && prop->opaque == child) {
             object_property_del(obj, prop->name, errp);
+            object_unref(child);

This should be called by object_finalize_child_property instead, can you check why this is not the case?

Yes, original ref/unref are right.
I will post another patch to fix this issue.

NAK this patch.

Quoted 1 line

Paolo

Thanks!

Signature · 1 line
			Amos.
author
#3 lore raw

PATCH 1/1 pci: unplug all devs of same slot once

In reply to Paolo Bonzini

The whole PCI slot should be removed once. Currently only one func is cleaned in pci_unplug_device(), if you try to remove a single func by monitor cmd.

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

Execute 'device_del $blkid' in monitor
\_handle_user_command()

    \_qmp_device_del()
       \_qdev_unplug()
          \_pci_unplug_device()
               | //only one obj(func) is unpluged
               v //need process funcs here

object_unparent()
\_object_finalize_child_property()

Guest sets pci dev by ioport write (eject from acpi)
\_kvm_handle_io()

    \_pciej_write()
      \_acpi_piix_eject_slot()
           |
           v  //all qdevs(funcs) will be free
 QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
     PCIDevice *dev = PCI_DEVICE(qdev);
     if (PCI_SLOT(dev->devfn) == slot) {
         qdev_free()
---
 hw/pci.c |   34 ++++++++++++++++++++++++++--------
 1 files changed, 26 insertions(+), 8 deletions(-)
hw/pci.c +26 -8
index b706e69..960cf53 100644
@@ -1520,16 +1520,34 @@ static int pci_qdev_init(DeviceState *qdev)
 
 static int pci_unplug_device(DeviceState *qdev)
 {
-    PCIDevice *dev = PCI_DEVICE(qdev);
-    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+    DeviceState *dev, *next;
+    PCIDevice *cur;
+    int ret, slot = PCI_SLOT(PCI_DEVICE(qdev)->devfn);
+    BusState *bus = qdev->parent_bus;
+
+    ret = 0;
+    QTAILQ_FOREACH_SAFE(dev, &bus->children, sibling, next) {
+        cur = PCI_DEVICE(dev);
+
+        if (PCI_SLOT(cur->devfn) == slot) {
+            PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(cur);
+            if (pc->no_hotplug) {
+                qerror_report(QERR_DEVICE_NO_HOTPLUG,
+                              object_get_typename(OBJECT(dev)));
+                return -1;
+            }
 
-    if (pc->no_hotplug) {
-        qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev)));
-        return -1;
+            object_unparent(OBJECT(cur));
+            ret = cur->bus->hotplug(cur->bus->hotplug_qdev, cur,
+                                    PCI_HOTPLUG_DISABLED);
+            if (ret < 0) {
+                qerror_report(QERR_UNDEFINED_ERROR,
+                              object_get_typename(OBJECT(dev)));
+                break;
+            }
+        }
     }
-    object_unparent(OBJECT(dev));
-    return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
-                             PCI_HOTPLUG_DISABLED);
+    return ret;
 }
 
 PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
#4 lore raw

Re: [Qemu-devel] [PATCH] pci: unplug all devs of same slot once

Quoted 24 lines · Amos Kong

The whole PCI slot should be removed once. Currently only one func is cleaned in pci_unplug_device(), if you try to remove a single func by monitor cmd.

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

Execute 'device_del $blkid' in monitor
\_handle_user_command()
\_qmp_device_del()
\_qdev_unplug()
\_pci_unplug_device()
| //only one obj(func) is unpluged
v //need process funcs here
object_unparent()
\_object_finalize_child_property()

Guest sets pci dev by ioport write (eject from acpi)
\_kvm_handle_io()
\_pciej_write()

Quoted 8 lines
     \_acpi_piix_eject_slot()
          |
          v  //all qdevs(funcs) will be free
 QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
    PCIDevice *dev = PCI_DEVICE(qdev);
    if (PCI_SLOT(dev->devfn) == slot) {
        qdev_free()

This is a regression bug, it's caused by this
commit: 57c9fafe0f759c9f1efa5451662b3627f9bb95e0
Before commit this commit, we free object in qdev_free(), as you can see in above analysis, qdev_free() is called for all the functions. But after applied this patch, we only release object of one function in pci_unplug_device().

commit 57c9fafe0f759c9f1efa5451662b3627f9bb95e0

Author: Anthony Liguori <[email protected]>
Date:   Mon Jan 30 08:55:55 2012 -0600

qom: move properties from qdev to object

This is mostly code movement although not entirely. This makes
properties part
of the Object base class which means that we can now start using Object
in a
meaningful way outside of qdev.

Quoted 58 lines
---
 hw/pci.c |   34 ++++++++++++++++++++++++++--------
 1 files changed, 26 insertions(+), 8 deletions(-)
hw/pci.c +26 -8
index b706e69..960cf53 100644
@@ -1520,16 +1520,34 @@ static int pci_qdev_init(DeviceState *qdev)
 
 static int pci_unplug_device(DeviceState *qdev)
 {
-    PCIDevice *dev = PCI_DEVICE(qdev);
-    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+    DeviceState *dev, *next;
+    PCIDevice *cur;
+    int ret, slot = PCI_SLOT(PCI_DEVICE(qdev)->devfn);
+    BusState *bus = qdev->parent_bus;
+
+    ret = 0;
+    QTAILQ_FOREACH_SAFE(dev, &bus->children, sibling, next) {
+        cur = PCI_DEVICE(dev);
+
+        if (PCI_SLOT(cur->devfn) == slot) {
+            PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(cur);
+            if (pc->no_hotplug) {
+                qerror_report(QERR_DEVICE_NO_HOTPLUG,
+                              object_get_typename(OBJECT(dev)));
+                return -1;
+            }
 
-    if (pc->no_hotplug) {
-        qerror_report(QERR_DEVICE_NO_HOTPLUG,
 object_get_typename(OBJECT(dev)));
-        return -1;
+            object_unparent(OBJECT(cur));
+            ret = cur->bus->hotplug(cur->bus->hotplug_qdev, cur,
+                                    PCI_HOTPLUG_DISABLED);
+            if (ret < 0) {
+                qerror_report(QERR_UNDEFINED_ERROR,
+                              object_get_typename(OBJECT(dev)));
+                break;
+            }
+        }
    }
-    object_unparent(OBJECT(dev));
-    return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
-                             PCI_HOTPLUG_DISABLED);
+    return ret;
 }
 

PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool
multifunction,

#5 lore raw

Re: [Qemu-devel] [PATCH] pci: unplug all devs of same slot once

In reply to Amos Kong
Quoted 13 lines · Amos Kong

The whole PCI slot should be removed once. Currently only one func is cleaned in pci_unplug_device(), if you try to remove a single func by monitor cmd.

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

Execute 'device_del $blkid' in monitor
\_handle_user_command()

Quoted 7 lines
    \_qmp_device_del()
       \_qdev_unplug()
          \_pci_unplug_device()
               | //only one obj(func) is unpluged
               v //need process funcs here

object_unparent()
\_object_finalize_child_property()

This is the bug IMO. PCI device delete request through monitor simply notifies guest. It should not unparent the object or do anything else.

Quoted 11 lines

Guest sets pci dev by ioport write (eject from acpi)
\_kvm_handle_io()

    \_pciej_write()
      \_acpi_piix_eject_slot()
           |
           v  //all qdevs(funcs) will be free
 QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
     PCIDevice *dev = PCI_DEVICE(qdev);
     if (PCI_SLOT(dev->devfn) == slot) {
         qdev_free()
Quoted 2 lines

---

This was done as part of 57c9fafe0f759c9f1efa5451662b3627f9bb95e0. Should we just call object_unparent before qdev_free? Anthony?

Signature · 1 line
MST
#6 lore raw

Re: [Qemu-devel] [PATCH] pci: unplug all devs of same slot once

Il 13/05/2012 12:54, Michael S. Tsirkin ha scritto:

Quoted 3 lines

This was done as part of 57c9fafe0f759c9f1efa5451662b3627f9bb95e0. Should we just call object_unparent before qdev_free? Anthony?

Or just call object_unparent in object_delete?

Paolo

#7 lore raw

PATCH v3 1/1 pci: call object_unparent() before free_qdev()

In reply to Amos Kong

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

It's a regression introduced by commit 57c9fafe

The whole PCI slot should be removed once. Currently only one func is cleaned in pci_unplug_device(), if you try to remove a single func by monitor cmd.

free_qdev() are called for all functions in slot,
but unparent_delete() is only called for one
function.

---
aliguori has a better resolution, better to do it in 1.2

v2: fix warning: too many arguments for format
v3: move object_unparent() to acpi_piix_eject_slot()

---
 hw/acpi_piix4.c |    1 +
 hw/pci.c        |    1 -
 2 files changed, 1 insertions(+), 1 deletions(-)
hw/acpi_piix4.c +1 -0
index 585da4e..0345490 100644
@@ -299,6 +299,7 @@ static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
             if (pc->no_hotplug) {
                 slot_free = false;
             } else {
+                object_unparent(OBJECT(dev));
                 qdev_free(qdev);
             }
         }
hw/pci.c +0 -1
index b706e69..c1ebdde 100644
@@ -1527,7 +1527,6 @@ static int pci_unplug_device(DeviceState *qdev)
         qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev)));
         return -1;
     }
-    object_unparent(OBJECT(dev));
     return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
                              PCI_HOTPLUG_DISABLED);
 }
Signature · 1 line
1.7.1
#8 lore raw

Re: [Qemu-devel] [PATCH v3] pci: call object_unparent() before free_qdev()

Quoted 17 lines · Amos Kong

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

It's a regression introduced by commit 57c9fafe

The whole PCI slot should be removed once. Currently only one func is cleaned in pci_unplug_device(), if you try to remove a single func by monitor cmd.

free_qdev() are called for all functions in slot,
but unparent_delete() is only called for one
function.

Quoted 7 lines

---
aliguori has a better resolution, better to do it in 1.2

v2: fix warning: too many arguments for format
v3: move object_unparent() to acpi_piix_eject_slot()

commit is mangled up a bit. It should be:

subject: xxxx

commit log

Signed-off-by XXXX

---

Versioning info

diff

No need to repost just we that. But we also need to update other pci hotplug users: hw//shpc.c hw//pcie.c
Not sure about pci-hotplug.c (calls qdev_free on error handling) - add a virtio blk function with wrong drive parameter using pci_add and see. Anything else?

Quoted 31 lines
---
 hw/acpi_piix4.c |    1 +
 hw/pci.c        |    1 -
 2 files changed, 1 insertions(+), 1 deletions(-)
hw/acpi_piix4.c +1 -0
index 585da4e..0345490 100644
@@ -299,6 +299,7 @@ static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
             if (pc->no_hotplug) {
                 slot_free = false;
             } else {
+                object_unparent(OBJECT(dev));
                 qdev_free(qdev);
             }
         }
hw/pci.c +0 -1
index b706e69..c1ebdde 100644
@@ -1527,7 +1527,6 @@ static int pci_unplug_device(DeviceState *qdev)
         qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev)));
         return -1;
     }
-    object_unparent(OBJECT(dev));
     return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
                              PCI_HOTPLUG_DISABLED);
 }
Signature · 1 line
1.7.1
#9 lore raw

Re: [Qemu-devel] [PATCH v3] pci: call object_unparent() before free_qdev()

In reply to Amos Kong
Quoted 9 lines · Amos Kong

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

It's a regression introduced by commit 57c9fafe

I found a similar assertion where the parent reference isn't cleared, doing:

(qemu) device_add pci-bridge

in the monitor. I posted for patches for it under:

Subject: [Qemu-devel] [PATCH 0/2] pci: hotplug bridge fixes

It's still an issue with the current tree.

Thanks,

-Jason

#10 lore raw

Re: [Qemu-devel] [PATCH v3] pci: call object_unparent() before free_qdev()

Quoted 18 lines · Jason Baron
Quoted 9 lines · Amos Kong

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

It's a regression introduced by commit 57c9fafe

I found a similar assertion where the parent reference isn't cleared, doing:

(qemu) device_add pci-bridge

in the monitor. I posted for patches for it under:

Quoted 7 lines

Subject: [Qemu-devel] [PATCH 0/2] pci: hotplug bridge fixes

It's still an issue with the current tree.

Thanks,

-Jason

I pushed your patches on my tree pci branch, care to test there?

#11 lore raw

Re: [Qemu-devel] [PATCH v3] pci: call object_unparent() before free_qdev()

Quoted 21 lines · Michael S. Tsirkin
Quoted 20 lines · Jason Baron
Quoted 9 lines · Amos Kong

Start VM with 8 multiple-function block devs, hot-removing those block devs by 'device_del ...' would cause qemu abort.

| (qemu) device_del virti0-0-0
| (qemu) **
|ERROR:qom/object.c:389:object_delete: assertion failed: (obj->ref == 0)

It's a regression introduced by commit 57c9fafe

I found a similar assertion where the parent reference isn't cleared, doing:

(qemu) device_add pci-bridge

in the monitor. I posted for patches for it under:

Subject: [Qemu-devel] [PATCH 0/2] pci: hotplug bridge fixes

Quoted 9 lines
Quoted 5 lines

It's still an issue with the current tree.

Thanks,

-Jason

I pushed your patches on my tree pci branch, care to test there?

Yes, fixes are confirmed.

Test case is quite simple:

(qemu) device_add pci_bridge

causes segfault without the 2 patches applied.

Thanks,

-Jason