diff mbox

[v6,02/27] bootindex: add del_boot_device_path function

Message ID 1409392827-9372-3-git-send-email-arei.gonglei@huawei.com
State New
Headers show

Commit Message

Gonglei (Arei) Aug. 30, 2014, 10 a.m. UTC
From: Gonglei <arei.gonglei@huawei.com>

Introduce del_boot_device_path() to clean up fw_cfg content when
hot-unplugging a device that refers to a bootindex.

Signed-off-by: Gonglei <arei.gonglei@huawei.com>
Signed-off-by: Chenliang <chenliang88@huawei.com>
---
 include/sysemu/sysemu.h |  1 +
 vl.c                    | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

Comments

Gerd Hoffmann Sept. 1, 2014, 6:43 a.m. UTC | #1
Hi,

> +static bool is_same_fw_dev_path(DeviceState *src, DeviceState *dst)
> +{
> +    bool ret = false;
> +    char *devpath_src = qdev_get_fw_dev_path(src);
> +    char *devpath_dst = qdev_get_fw_dev_path(dst);
> +
> +    if (!strcmp(devpath_src, devpath_dst)) {
> +        ret = true;
> +    }
> +
> +    g_free(devpath_src);
> +    g_free(devpath_dst);
> +    return ret;
> +}
> +
> +void del_boot_device_path(DeviceState *dev)
> +{
> +    FWBootEntry *i;
> +
> +    assert(dev != NULL);
> +
> +    /* remove all entries of the assigned device */
> +    QTAILQ_FOREACH(i, &fw_boot_order, link) {
> +        if (i->dev == NULL) {
> +            continue;
> +        }
> +        if ((i->dev == dev || is_same_fw_dev_path(i->dev, dev))) {

Why this is needed?  Is there any case where i-->dev != dev but
is_same_fw_dev_path() returns true?

cheers,
  Gerd
Gonglei (Arei) Sept. 1, 2014, 6:47 a.m. UTC | #2
> From: Gerd Hoffmann [mailto:kraxel@redhat.com]

> Sent: Monday, September 01, 2014 2:43 PM

> Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function

> Importance: High

> 

>   Hi,

> 

> > +static bool is_same_fw_dev_path(DeviceState *src, DeviceState *dst)

> > +{

> > +    bool ret = false;

> > +    char *devpath_src = qdev_get_fw_dev_path(src);

> > +    char *devpath_dst = qdev_get_fw_dev_path(dst);

> > +

> > +    if (!strcmp(devpath_src, devpath_dst)) {

> > +        ret = true;

> > +    }

> > +

> > +    g_free(devpath_src);

> > +    g_free(devpath_dst);

> > +    return ret;

> > +}

> > +

> > +void del_boot_device_path(DeviceState *dev)

> > +{

> > +    FWBootEntry *i;

> > +

> > +    assert(dev != NULL);

> > +

> > +    /* remove all entries of the assigned device */

> > +    QTAILQ_FOREACH(i, &fw_boot_order, link) {

> > +        if (i->dev == NULL) {

> > +            continue;

> > +        }

> > +        if ((i->dev == dev || is_same_fw_dev_path(i->dev, dev))) {

> 

> Why this is needed?  Is there any case where i-->dev != dev but

> is_same_fw_dev_path() returns true?

> 

Yes, it is needed. At present, the virito-net-pci device 
compliance with this situation.

Please see the qom path about virtio-net-pci and virtio-net device:

id: null, /machine/peripheral/nic1/virtio-backend
id: nic1, /machine/peripheral/nic1

Best regards,
-Gonglei
Eduardo Habkost Sept. 2, 2014, 6 p.m. UTC | #3
On Mon, Sep 01, 2014 at 06:47:13AM +0000, Gonglei (Arei) wrote:
> > From: Gerd Hoffmann [mailto:kraxel@redhat.com]
> > Sent: Monday, September 01, 2014 2:43 PM
> > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> > Importance: High
> > 
> >   Hi,
> > 
> > > +static bool is_same_fw_dev_path(DeviceState *src, DeviceState *dst)
> > > +{
> > > +    bool ret = false;
> > > +    char *devpath_src = qdev_get_fw_dev_path(src);
> > > +    char *devpath_dst = qdev_get_fw_dev_path(dst);
> > > +
> > > +    if (!strcmp(devpath_src, devpath_dst)) {
> > > +        ret = true;
> > > +    }
> > > +
> > > +    g_free(devpath_src);
> > > +    g_free(devpath_dst);
> > > +    return ret;
> > > +}
> > > +
> > > +void del_boot_device_path(DeviceState *dev)
> > > +{
> > > +    FWBootEntry *i;
> > > +
> > > +    assert(dev != NULL);
> > > +
> > > +    /* remove all entries of the assigned device */
> > > +    QTAILQ_FOREACH(i, &fw_boot_order, link) {
> > > +        if (i->dev == NULL) {
> > > +            continue;
> > > +        }
> > > +        if ((i->dev == dev || is_same_fw_dev_path(i->dev, dev))) {
> > 
> > Why this is needed?  Is there any case where i-->dev != dev but
> > is_same_fw_dev_path() returns true?
> > 
> Yes, it is needed. At present, the virito-net-pci device 
> compliance with this situation.
> 
> Please see the qom path about virtio-net-pci and virtio-net device:
> 
> id: null, /machine/peripheral/nic1/virtio-backend
> id: nic1, /machine/peripheral/nic1

And why exactly is the caller passing a different pointer to
del_boot_device_path()? Why not simply change the caller to pass the
same pointer to add_boot_device_path() and del_boot_device_path()?
Gonglei (Arei) Sept. 3, 2014, 2:35 a.m. UTC | #4
Hi,

> From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> Sent: Wednesday, September 03, 2014 2:00 AM
> Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> Importance: High
> 
> On Mon, Sep 01, 2014 at 06:47:13AM +0000, Gonglei (Arei) wrote:
> > > From: Gerd Hoffmann [mailto:kraxel@redhat.com]
> > > Sent: Monday, September 01, 2014 2:43 PM
> > > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path
> function
> > > Importance: High
> > >
> > >   Hi,
> > >
> > > > +static bool is_same_fw_dev_path(DeviceState *src, DeviceState *dst)
> > > > +{
> > > > +    bool ret = false;
> > > > +    char *devpath_src = qdev_get_fw_dev_path(src);
> > > > +    char *devpath_dst = qdev_get_fw_dev_path(dst);
> > > > +
> > > > +    if (!strcmp(devpath_src, devpath_dst)) {
> > > > +        ret = true;
> > > > +    }
> > > > +
> > > > +    g_free(devpath_src);
> > > > +    g_free(devpath_dst);
> > > > +    return ret;
> > > > +}
> > > > +
> > > > +void del_boot_device_path(DeviceState *dev)
> > > > +{
> > > > +    FWBootEntry *i;
> > > > +
> > > > +    assert(dev != NULL);
> > > > +
> > > > +    /* remove all entries of the assigned device */
> > > > +    QTAILQ_FOREACH(i, &fw_boot_order, link) {
> > > > +        if (i->dev == NULL) {
> > > > +            continue;
> > > > +        }
> > > > +        if ((i->dev == dev || is_same_fw_dev_path(i->dev, dev))) {
> > >
> > > Why this is needed?  Is there any case where i-->dev != dev but
> > > is_same_fw_dev_path() returns true?
> > >
> > Yes, it is needed. At present, the virito-net-pci device
> > compliance with this situation.
> >
> > Please see the qom path about virtio-net-pci and virtio-net device:
> >
> > id: null, /machine/peripheral/nic1/virtio-backend
> > id: nic1, /machine/peripheral/nic1
> 
> And why exactly is the caller passing a different pointer to
> del_boot_device_path()? Why not simply change the caller to pass the
> same pointer to add_boot_device_path() and del_boot_device_path()?
> 
1. When we want to create a virtio-net device, we must use '-device virtio-net-pci'.
This device is a pci device, and virtio-net-device is its child:
 object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
2. Both virtio-net-pci and virtio-net-device own bootindex property because they
include "DEFINE_NIC_PROPERTIES(VirtIONetPCI, vdev.nic_conf)".
3. Only virtio-net-device will call add_boot_device_path() in virtio_net_device_realize(),
which use its own pointer, but not virtio-net-pci's pointer:
 add_boot_device_path(n->nic_conf.bootindex, dev, "/ethernet-phy@0");
4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's pointer to 
del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a function
named is_same_fw_dev_path() to handle this situation. 


Best regards,
-Gonglei
Gonglei (Arei) Sept. 3, 2014, 2:40 a.m. UTC | #5
> Subject: RE: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> 
> Hi,
> 
> > From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> > Sent: Wednesday, September 03, 2014 2:00 AM
> > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> > Importance: High
> >
> > On Mon, Sep 01, 2014 at 06:47:13AM +0000, Gonglei (Arei) wrote:
> > > > From: Gerd Hoffmann [mailto:kraxel@redhat.com]
> > > > Sent: Monday, September 01, 2014 2:43 PM
> > > > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path
> > function
> > > > Importance: High
> > > >
> > > >   Hi,
> > > >
> > > > > +static bool is_same_fw_dev_path(DeviceState *src, DeviceState *dst)
> > > > > +{
> > > > > +    bool ret = false;
> > > > > +    char *devpath_src = qdev_get_fw_dev_path(src);
> > > > > +    char *devpath_dst = qdev_get_fw_dev_path(dst);
> > > > > +
> > > > > +    if (!strcmp(devpath_src, devpath_dst)) {
> > > > > +        ret = true;
> > > > > +    }
> > > > > +
> > > > > +    g_free(devpath_src);
> > > > > +    g_free(devpath_dst);
> > > > > +    return ret;
> > > > > +}
> > > > > +
> > > > > +void del_boot_device_path(DeviceState *dev)
> > > > > +{
> > > > > +    FWBootEntry *i;
> > > > > +
> > > > > +    assert(dev != NULL);
> > > > > +
> > > > > +    /* remove all entries of the assigned device */
> > > > > +    QTAILQ_FOREACH(i, &fw_boot_order, link) {
> > > > > +        if (i->dev == NULL) {
> > > > > +            continue;
> > > > > +        }
> > > > > +        if ((i->dev == dev || is_same_fw_dev_path(i->dev, dev))) {
> > > >
> > > > Why this is needed?  Is there any case where i-->dev != dev but
> > > > is_same_fw_dev_path() returns true?
> > > >
> > > Yes, it is needed. At present, the virito-net-pci device
> > > compliance with this situation.
> > >
> > > Please see the qom path about virtio-net-pci and virtio-net device:
> > >
> > > id: null, /machine/peripheral/nic1/virtio-backend
> > > id: nic1, /machine/peripheral/nic1
> >
> > And why exactly is the caller passing a different pointer to
> > del_boot_device_path()? Why not simply change the caller to pass the
> > same pointer to add_boot_device_path() and del_boot_device_path()?
> >
> 1. When we want to create a virtio-net device, we must use '-device
> virtio-net-pci'.
> This device is a pci device, and virtio-net-device is its child:
>  object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev),
> NULL);
> 2. Both virtio-net-pci and virtio-net-device own bootindex property because
> they
> include "DEFINE_NIC_PROPERTIES(VirtIONetPCI, vdev.nic_conf)".
> 3. Only virtio-net-device will call add_boot_device_path() in
> virtio_net_device_realize(),
> which use its own pointer, but not virtio-net-pci's pointer:
>  add_boot_device_path(n->nic_conf.bootindex, dev, "/ethernet-phy@0");
> 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's pointer

Sorry, my typo, s/hotplug/hot-unplug/

> to del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a
> function named is_same_fw_dev_path() to handle this situation.
> 
> 
> Best regards,
> -Gonglei
Gerd Hoffmann Sept. 3, 2014, 6:24 a.m. UTC | #6
Hi,

> 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's pointer to 
> del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a function
> named is_same_fw_dev_path() to handle this situation. 

When hot-unplugging virtio-net-pci I'd expect we free both
virtio-net-pci and virtio-net-device (and therefore call
del_boot_device_path twice, once for each device).  Can you check that?

thanks,
  Gerd
Gonglei (Arei) Sept. 3, 2014, 6:45 a.m. UTC | #7
Best regards,
-Gonglei


> -----Original Message-----

> From: Gerd Hoffmann [mailto:kraxel@redhat.com]

> Sent: Wednesday, September 03, 2014 2:25 PM

> To: Gonglei (Arei)

> Cc: Eduardo Habkost; qemu-devel@nongnu.org; aliguori@amazon.com;

> mst@redhat.com; pbonzini@redhat.com; akong@redhat.com;

> hutao@cn.fujitsu.com; eblake@redhat.com; afaerber@suse.de;

> armbru@redhat.com; imammedo@redhat.com; aik@ozlabs.ru;

> peter.crosthwaite@xilinx.com; lcapitulino@redhat.com; hani@linux.com;

> stefanha@redhat.com; agraf@suse.de; chenliang (T); Huangweidong (C);

> Luonengjun; Huangpeng (Peter); kwolf@redhat.com

> Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function

> Importance: High

> 

> 

>   Hi,

> 

> > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's pointer

> to

> > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a

> function

> > named is_same_fw_dev_path() to handle this situation.

> 

> When hot-unplugging virtio-net-pci I'd expect we free both

> virtio-net-pci and virtio-net-device (and therefore call

> del_boot_device_path twice, once for each device).  Can you check that?

> 

Yes, I can. 

The del_boot_device_path() is called only once, just for virtio-net-pci.
For its child, virtio-net-devcie's resource is cleaned by qbus->child unrealizing 
process, will not call device_finalize().

Command line:

# ./qemu-system-x86_64 -enable-kvm -m 4096 -smp 4 -name redhat6.2 -drive file=/mnt/sdb/gonglei/image/win7_32_2U,if=none,id=drive-ide0-0-0 -device ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 -drive file=/mnt/sdb/gonglei/iso/rhel-server-7.0-x86_64-dvd.iso,if=none,id=drive-ide0-0-1 -device ide-cd,bus=ide.0,unit=1,drive=drive-ide0-0-1,id=ide0-0-1,bootindex=4 -vnc 0.0.0.0:10 -netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=3,id=nic1
QEMU 2.1.50 monitor - type 'help' for more information
(qemu) device_del nic1
(qemu)

The below is the backtrace by gdb:

Breakpoint 1, virtio_net_device_unrealize (dev=0x7f1b1ea87d58, errp=0x7f1b17d8c350)
    at /mnt/sdb/gonglei/qemu.git/qemu/hw/net/virtio-net.c:1649
1649    {
(gdb) bt
#0  virtio_net_device_unrealize (dev=0x7f1b1ea87d58, errp=0x7f1b17d8c350)
    at /mnt/sdb/gonglei/qemu.git/qemu/hw/net/virtio-net.c:1649
#1  0x00007f1b1dd69b71 in virtio_device_unrealize (dev=0x7f1b1ea87d58, errp=0x7f1b17d8c3c8)
    at /mnt/sdb/gonglei/qemu.git/qemu/hw/virtio/virtio.c:1312
#2  0x00007f1b1de8a1a4 in device_set_realized (obj=0x7f1b1ea87d58, value=false, errp=0x7f1b17d8c568) at hw/core/qdev.c:885
#3  0x00007f1b1dfa01f7 in property_set_bool (obj=0x7f1b1ea87d58, v=0x7f1b1eb29de0, opaque=0x7f1b1eaa72c0, name=
    0x7f1b1e0c2b69 "realized", errp=0x7f1b17d8c568) at qom/object.c:1467
#4  0x00007f1b1df9e82f in object_property_set (obj=0x7f1b1ea87d58, v=0x7f1b1eb29de0, name=0x7f1b1e0c2b69 "realized", errp=
    0x7f1b17d8c568) at qom/object.c:814
#5  0x00007f1b1dfa0c45 in object_property_set_qobject (obj=0x7f1b1ea87d58, value=0x7f1b1eb09c80, name=0x7f1b1e0c2b69 "realized", 
    errp=0x7f1b17d8c568) at qom/qom-qobject.c:24
#6  0x00007f1b1df9ebd7 in object_property_set_bool (obj=0x7f1b1ea87d58, value=false, name=0x7f1b1e0c2b69 "realized", errp=
    0x7f1b17d8c568) at qom/object.c:878
#7  0x00007f1b1de893fa in bus_set_realized (obj=0x7f1b1ea87ce0, value=false, errp=0x7f1b17d8c718) at hw/core/qdev.c:583
#8  0x00007f1b1dfa01f7 in property_set_bool (obj=0x7f1b1ea87ce0, v=0x7f1b1ead5a10, opaque=0x7f1b1eaa1640, name=
    0x7f1b1e0c2b69 "realized", errp=0x7f1b17d8c718) at qom/object.c:1467
#9  0x00007f1b1df9e82f in object_property_set (obj=0x7f1b1ea87ce0, v=0x7f1b1ead5a10, name=0x7f1b1e0c2b69 "realized", errp=
    0x7f1b17d8c718) at qom/object.c:814
#10 0x00007f1b1dfa0c45 in object_property_set_qobject (obj=0x7f1b1ea87ce0, value=0x7f1b1eb09c60, name=0x7f1b1e0c2b69 "realized", 
    errp=0x7f1b17d8c718) at qom/qom-qobject.c:24
#11 0x00007f1b1df9ebd7 in object_property_set_bool (obj=0x7f1b1ea87ce0, value=false, name=0x7f1b1e0c2b69 "realized", errp=
    0x7f1b17d8c718) at qom/object.c:878
#12 0x00007f1b1de8a127 in device_set_realized (obj=0x7f1b1ea87380, value=false, errp=0x0) at hw/core/qdev.c:875
#13 0x00007f1b1dfa01f7 in property_set_bool (obj=0x7f1b1ea87380, v=0x7f1b1eacf940, opaque=0x7f1b1ea89f20, name=
    0x7f1b1e0c2b69 "realized", errp=0x0) at qom/object.c:1467
#14 0x00007f1b1df9e82f in object_property_set (obj=0x7f1b1ea87380, v=0x7f1b1eacf940, name=0x7f1b1e0c2b69 "realized", errp=0x0)
    at qom/object.c:814
#15 0x00007f1b1dfa0c45 in object_property_set_qobject (obj=0x7f1b1ea87380, value=0x7f1b1eb085e0, name=0x7f1b1e0c2b69 "realized", 
    errp=0x0) at qom/qom-qobject.c:24
#16 0x00007f1b1df9ebd7 in object_property_set_bool (obj=0x7f1b1ea87380, value=false, name=0x7f1b1e0c2b69 "realized", errp=0x0)
    at qom/object.c:878
#17 0x00007f1b1de8a7d0 in device_unparent (obj=0x7f1b1ea87380) at hw/core/qdev.c:1010
#18 0x00007f1b1df9f26b in object_finalize_child_property (obj=0x7f1b1e9f4f30, name=0x7f1b1eaa7ba0 "nic1", opaque=0x7f1b1ea87380)
    at qom/object.c:1036
#19 0x00007f1b1df9e671 in object_property_del (obj=0x7f1b1e9f4f30, name=0x7f1b1eaa7ba0 "nic1", errp=0x0) at qom/object.c:778
#20 0x00007f1b1df9d692 in object_property_del_child (obj=0x7f1b1e9f4f30, child=0x7f1b1ea87380, errp=0x0) at qom/object.c:382
#21 0x00007f1b1df9d74b in object_unparent (obj=0x7f1b1ea87380) at qom/object.c:391
#22 0x00007f1b1de52317 in acpi_pcihp_eject_slot (s=0x7f1b1eaf3438, bsel=0, slots=8) at hw/acpi/pcihp.c:139
#23 0x00007f1b1de529b5 in pci_write (opaque=0x7f1b1eaf3438, addr=8, data=8, size=4) at hw/acpi/pcihp.c:277
#24 0x00007f1b1dd1e958 in memory_region_write_accessor (mr=0x7f1b1eaf4048, addr=8, value=0x7f1b17d8cb38, size=4, shift=0, mask=
    4294967295) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:443
#25 0x00007f1b1dd1ea94 in access_with_adjusted_size (addr=8, value=0x7f1b17d8cb38, size=4, access_size_min=1, access_size_max=4, 
    access=0x7f1b1dd1e8cb <memory_region_write_accessor>, mr=0x7f1b1eaf4048) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:480
#26 0x00007f1b1dd22061 in memory_region_dispatch_write (mr=0x7f1b1eaf4048, addr=8, data=8, size=4)
    at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1137
#27 0x00007f1b1dd25c10 in io_mem_write (mr=0x7f1b1eaf4048, addr=8, val=8, size=4) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1973
#28 0x00007f1b1dcd01a9 in address_space_rw (as=0x7f1b1e4ed180 <address_space_io>, addr=44552, buf=0x7f1b1dc1f000 "\b", len=4, 
    is_write=true) at /mnt/sdb/gonglei/qemu.git/qemu/exec.c:2042
#29 0x00007f1b1dd1b186 in kvm_handle_io (port=44552, data=0x7f1b1dc1f000, direction=1, size=4, count=1)
    at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1597
#30 0x00007f1b1dd1b7c4 in kvm_cpu_exec (cpu=0x7f1b1e9f85b0) at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1748
#31 0x00007f1b1dd02c88 in qemu_kvm_cpu_thread_fn (arg=0x7f1b1e9f85b0) at /mnt/sdb/gonglei/qemu.git/qemu/cpus.c:940
#32 0x00007f1b1af3b7f6 in start_thread () from /lib64/libpthread.so.0
#33 0x00007f1b1ac9709d in clone () from /lib64/libc.so.6
#34 0x0000000000000000 in ?? ()
(gdb) c
Continuing.

Breakpoint 2, device_finalize (obj=0x7f1b1ea87380) at hw/core/qdev.c:971
971     {
(gdb) bt
#0  device_finalize (obj=0x7f1b1ea87380) at hw/core/qdev.c:971
#1  0x00007f1b1df9d79b in object_deinit (obj=0x7f1b1ea87380, type=0x7f1b1e99a970) at qom/object.c:398
#2  0x00007f1b1df9d7bd in object_deinit (obj=0x7f1b1ea87380, type=0x7f1b1e990a40) at qom/object.c:402
#3  0x00007f1b1df9d7bd in object_deinit (obj=0x7f1b1ea87380, type=0x7f1b1e987700) at qom/object.c:402
#4  0x00007f1b1df9d7bd in object_deinit (obj=0x7f1b1ea87380, type=0x7f1b1e987fc0) at qom/object.c:402
#5  0x00007f1b1df9d81a in object_finalize (data=0x7f1b1ea87380) at qom/object.c:412
#6  0x00007f1b1df9e3d9 in object_unref (obj=0x7f1b1ea87380) at qom/object.c:719
#7  0x00007f1b1df9f280 in object_finalize_child_property (obj=0x7f1b1e9f4f30, name=0x7f1b1eaa7ba0 "nic1", opaque=0x7f1b1ea87380)
    at qom/object.c:1039
#8  0x00007f1b1df9e671 in object_property_del (obj=0x7f1b1e9f4f30, name=0x7f1b1eaa7ba0 "nic1", errp=0x0) at qom/object.c:778
#9  0x00007f1b1df9d692 in object_property_del_child (obj=0x7f1b1e9f4f30, child=0x7f1b1ea87380, errp=0x0) at qom/object.c:382
#10 0x00007f1b1df9d74b in object_unparent (obj=0x7f1b1ea87380) at qom/object.c:391
#11 0x00007f1b1de52317 in acpi_pcihp_eject_slot (s=0x7f1b1eaf3438, bsel=0, slots=8) at hw/acpi/pcihp.c:139
#12 0x00007f1b1de529b5 in pci_write (opaque=0x7f1b1eaf3438, addr=8, data=8, size=4) at hw/acpi/pcihp.c:277
#13 0x00007f1b1dd1e958 in memory_region_write_accessor (mr=0x7f1b1eaf4048, addr=8, value=0x7f1b17d8cb38, size=4, shift=0, mask=
    4294967295) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:443
#14 0x00007f1b1dd1ea94 in access_with_adjusted_size (addr=8, value=0x7f1b17d8cb38, size=4, access_size_min=1, access_size_max=4, 
    access=0x7f1b1dd1e8cb <memory_region_write_accessor>, mr=0x7f1b1eaf4048) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:480
#15 0x00007f1b1dd22061 in memory_region_dispatch_write (mr=0x7f1b1eaf4048, addr=8, data=8, size=4)
    at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1137
#16 0x00007f1b1dd25c10 in io_mem_write (mr=0x7f1b1eaf4048, addr=8, val=8, size=4) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1973
#17 0x00007f1b1dcd01a9 in address_space_rw (as=0x7f1b1e4ed180 <address_space_io>, addr=44552, buf=0x7f1b1dc1f000 "\b", len=4, 
    is_write=true) at /mnt/sdb/gonglei/qemu.git/qemu/exec.c:2042
#18 0x00007f1b1dd1b186 in kvm_handle_io (port=44552, data=0x7f1b1dc1f000, direction=1, size=4, count=1)
    at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1597
#19 0x00007f1b1dd1b7c4 in kvm_cpu_exec (cpu=0x7f1b1e9f85b0) at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1748
#20 0x00007f1b1dd02c88 in qemu_kvm_cpu_thread_fn (arg=0x7f1b1e9f85b0) at /mnt/sdb/gonglei/qemu.git/qemu/cpus.c:940
#21 0x00007f1b1af3b7f6 in start_thread () from /lib64/libpthread.so.0
#22 0x00007f1b1ac9709d in clone () from /lib64/libc.so.6
#23 0x0000000000000000 in ?? ()
(gdb) n
974         DeviceState *dev = DEVICE(obj);
(gdb) 
977         del_boot_device_path(dev);
(gdb) p *dev
$1 = {parent_obj = {class = 0x7f1b1e9b7270, free = 0x7f1b1caf1a30 <g_free>, properties = {tqh_first = 0x0, tqh_last = 
    0x7f1b1ea87390}, ref = 0, parent = 0x0}, id = 0x7f1b1e9cbb30 "nic1", realized = false, pending_deleted_event = true, opts = 
    0x7f1b1e9cb2f0, hotplugged = 0, parent_bus = 0x0, gpios = {lh_first = 0x0}, child_bus = {lh_first = 0x0}, num_child_bus = 0, 
  instance_id_alias = -1, alias_required_for_version = 0}
(gdb) n
[Thread 0x7f1b18770700 (LWP 13982) exited]
979         if (dev->opts) {
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00007f1b1dd5def2 in virtio_net_device_unrealize 
                                                   at /mnt/sdb/gonglei/qemu.git/qemu/hw/net/virtio-net.c:1649
        breakpoint already hit 1 time
2       breakpoint     keep y   0x00007f1b1de8a608 in device_finalize at hw/core/qdev.c:971
        breakpoint already hit 1 time
(gdb) c
Continuing.
[New Thread 0x7f1b18770700 (LWP 15096)]
[Thread 0x7f1b18770700 (LWP 15096) exited]
[New Thread 0x7f1b18770700 (LWP 16434)]
[Thread 0x7f1b18770700 (LWP 16434) exited]
Eduardo Habkost Sept. 3, 2014, 6:13 p.m. UTC | #8
On Wed, Sep 03, 2014 at 06:45:56AM +0000, Gonglei (Arei) wrote:
[...]
> > > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's pointer
> > to
> > > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a
> > function
> > > named is_same_fw_dev_path() to handle this situation.
> > 
> > When hot-unplugging virtio-net-pci I'd expect we free both
> > virtio-net-pci and virtio-net-device (and therefore call
> > del_boot_device_path twice, once for each device).  Can you check that?
> > 
> Yes, I can. 
> 
> The del_boot_device_path() is called only once, just for virtio-net-pci.
> For its child, virtio-net-devcie's resource is cleaned by qbus->child unrealizing 
> process, will not call device_finalize().

Then we need to fix this to make sure there's a corresponding
del_boot_device_path() call (with the same pointer) to every
add_boot_device_path() call, instead of adding a hack to
del_boot_device_path().
Gonglei (Arei) Sept. 4, 2014, 3:01 a.m. UTC | #9
Hi,

> From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> Sent: Thursday, September 04, 2014 2:13 AM
> Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> 
> On Wed, Sep 03, 2014 at 06:45:56AM +0000, Gonglei (Arei) wrote:
> [...]
> > > > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's
> pointer
> > > to
> > > > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a
> > > function
> > > > named is_same_fw_dev_path() to handle this situation.
> > >
> > > When hot-unplugging virtio-net-pci I'd expect we free both
> > > virtio-net-pci and virtio-net-device (and therefore call
> > > del_boot_device_path twice, once for each device).  Can you check that?
> > >
> > Yes, I can.
> >
> > The del_boot_device_path() is called only once, just for virtio-net-pci.
> > For its child, virtio-net-devcie's resource is cleaned by qbus->child unrealizing
> > process, will not call device_finalize().
> 
> Then we need to fix this to make sure there's a corresponding
> del_boot_device_path() call (with the same pointer) to every
> add_boot_device_path() call, instead of adding a hack to
> del_boot_device_path().
> 
Good idea. We can add functions named $device_finalize_bootindex(), such as:

static void virtio_net_set_bootindex(Object *obj, Visitor *v, void *opaque,
                                     const char *name, Error **errp)
{
    VirtIONet *n = VIRTIO_NET(obj);
    
    set_bootindex(&n->nic_conf, v, name, errp);
    add_boot_device_path(n->nic_conf.bootindex,
                         DEVICE(obj), "/ethernet-phy@0"); 
}

static void virtio_net_finalize_bootindex(Object *obj, const char *name,
                                          void *opaque)
{
    del_boot_device_path(DEVICE(obj));
}
...
object_property_add(obj, "bootindex", "int",
                        virtio_net_get_bootindex, 
                        virtio_net_set_bootindex,
                        virtio_net_finalize_bootindex, dev, NULL);

as the previous email, we lay add_boot_device_path() in $device_set_bootindex,
and lay del_boot_device_path() in $device_finalize_bootindex is a good idea IMO.

Do you like it? Thanks.

Best regards,
-Gonglei
Gonglei (Arei) Sept. 4, 2014, 6:15 a.m. UTC | #10
Hi,

> > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> >
> > On Wed, Sep 03, 2014 at 06:45:56AM +0000, Gonglei (Arei) wrote:
> > [...]
> > > > > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's
> > pointer
> > > > to
> > > > > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a
> > > > function
> > > > > named is_same_fw_dev_path() to handle this situation.
> > > >
> > > > When hot-unplugging virtio-net-pci I'd expect we free both
> > > > virtio-net-pci and virtio-net-device (and therefore call
> > > > del_boot_device_path twice, once for each device).  Can you check that?
> > > >
> > > Yes, I can.
> > >
> > > The del_boot_device_path() is called only once, just for virtio-net-pci.
> > > For its child, virtio-net-devcie's resource is cleaned by qbus->child
> unrealizing
> > > process, will not call device_finalize().
> >
> > Then we need to fix this to make sure there's a corresponding
> > del_boot_device_path() call (with the same pointer) to every
> > add_boot_device_path() call, instead of adding a hack to
> > del_boot_device_path().
> >
> Good idea. We can add functions named $device_finalize_bootindex(), such as:
> 
> static void virtio_net_set_bootindex(Object *obj, Visitor *v, void *opaque,
>                                      const char *name, Error **errp)
> {
>     VirtIONet *n = VIRTIO_NET(obj);
> 
>     set_bootindex(&n->nic_conf, v, name, errp);
>     add_boot_device_path(n->nic_conf.bootindex,
>                          DEVICE(obj), "/ethernet-phy@0");
> }
> 
> static void virtio_net_finalize_bootindex(Object *obj, const char *name,
>                                           void *opaque)
> {
>     del_boot_device_path(DEVICE(obj));
> }
> ...

Oops, I found an issue that when we hot-unplug a virtio-net-pci device,
the virtio_net_finalize_bootindex function will not be called too.
Maybe this is a potential *bug* which will cause the virtio-net-device's property
resource leak. 

Paolo, would you have a look at it? Thanks a lot!

Backtrace as below:

#0  object_unref (obj=0x7f207c640468) at qom/object.c:711
#1  0x00007f207b9850da in bus_remove_child (bus=0x7f207c6403f0, child=0x7f207c640468) at hw/core/qdev.c:76
#2  0x00007f207b987cbc in device_unparent (obj=0x7f207c640468) at hw/core/qdev.c:1013
#3  0x00007f207ba9c5a3 in object_finalize_child_property (obj=0x7f207c63fa90, name=0x7f207c6027c0 "virtio-backend", opaque=
    0x7f207c640468) at qom/object.c:1036
#4  0x00007f207ba9b9a9 in object_property_del (obj=0x7f207c63fa90, name=0x7f207c6027c0 "virtio-backend", errp=0x0)
    at qom/object.c:778
#5  0x00007f207ba9a9ca in object_property_del_child (obj=0x7f207c63fa90, child=0x7f207c640468, errp=0x0) at qom/object.c:382
#6  0x00007f207ba9aa83 in object_unparent (obj=0x7f207c640468) at qom/object.c:391
#7  0x00007f207b986666 in bus_unparent (obj=0x7f207c6403f0) at hw/core/qdev.c:548
#8  0x00007f207ba9c5a3 in object_finalize_child_property (obj=0x7f207c63fa90, name=0x7f207c5db7a0 "virtio-bus", opaque=
    0x7f207c6403f0) at qom/object.c:1036
#9  0x00007f207ba9b9a9 in object_property_del (obj=0x7f207c63fa90, name=0x7f207c5db7a0 "virtio-bus", errp=0x0) at qom/object.c:778
#10 0x00007f207ba9a9ca in object_property_del_child (obj=0x7f207c63fa90, child=0x7f207c6403f0, errp=0x0) at qom/object.c:382
#11 0x00007f207ba9aa83 in object_unparent (obj=0x7f207c6403f0) at qom/object.c:391
#12 0x00007f207b987c8f in device_unparent (obj=0x7f207c63fa90) at hw/core/qdev.c:1010
#13 0x00007f207ba9c5a3 in object_finalize_child_property (obj=0x7f207c4f2f90, name=0x7f207c604580 "nic1", opaque=0x7f207c63fa90)
    at qom/object.c:1036
#14 0x00007f207ba9b9a9 in object_property_del (obj=0x7f207c4f2f90, name=0x7f207c604580 "nic1", errp=0x0) at qom/object.c:778
#15 0x00007f207ba9a9ca in object_property_del_child (obj=0x7f207c4f2f90, child=0x7f207c63fa90, errp=0x0) at qom/object.c:382
#16 0x00007f207ba9aa83 in object_unparent (obj=0x7f207c63fa90) at qom/object.c:391
#17 0x00007f207b94f7c7 in acpi_pcihp_eject_slot (s=0x7f207c5801e8, bsel=0, slots=8) at hw/acpi/pcihp.c:139
#18 0x00007f207b94fe65 in pci_write (opaque=0x7f207c5801e8, addr=8, data=8, size=4) at hw/acpi/pcihp.c:277
#19 0x00007f207b818a38 in memory_region_write_accessor (mr=0x7f207c580df8, addr=8, value=0x7f2075886b38, size=4, shift=0, mask=
    4294967295) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:443
#20 0x00007f207b818b74 in access_with_adjusted_size (addr=8, value=0x7f2075886b38, size=4, access_size_min=1, access_size_max=4, 
    access=0x7f207b8189ab <memory_region_write_accessor>, mr=0x7f207c580df8) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:480
#21 0x00007f207b81c141 in memory_region_dispatch_write (mr=0x7f207c580df8, addr=8, data=8, size=4)
    at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1137
#22 0x00007f207b81fcf0 in io_mem_write (mr=0x7f207c580df8, addr=8, val=8, size=4) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1973
#23 0x00007f207b7ca289 in address_space_rw (as=0x7f207bfeb1e0 <address_space_io>, addr=44552, buf=0x7f207b719000 "\b", len=4, 
    is_write=true) at /mnt/sdb/gonglei/qemu.git/qemu/exec.c:2042
#24 0x00007f207b815266 in kvm_handle_io (port=44552, data=0x7f207b719000, direction=1, size=4, count=1)
    at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1597
#25 0x00007f207b8158a4 in kvm_cpu_exec (cpu=0x7f207c4f6610) at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1748
#26 0x00007f207b7fcd68 in qemu_kvm_cpu_thread_fn (arg=0x7f207c4f6610) at /mnt/sdb/gonglei/qemu.git/qemu/cpus.c:940
#27 0x00007f2078a357f6 in start_thread () from /lib64/libpthread.so.0
#28 0x00007f207879109d in clone () from /lib64/libc.so.6
#29 0x0000000000000000 in ?? ()
(gdb) p *obj
$64 = {class = 0x7f207c4bc5f0, free = 0x0, properties = {tqh_first = 0x7f207c591fd0, tqh_last = 0x7f207c6027a8}, ref = 3, parent = 
    0x7f207c63fa90}
(gdb) n
712         if (!obj) {
(gdb) 
715         g_assert(obj->ref > 0);
(gdb) 
718         if (atomic_fetch_dec(&obj->ref) == 1) {
(gdb) 
721     }
(gdb) p *obj
$65 = {class = 0x7f207c4bc5f0, free = 0x0, properties = {tqh_first = 0x7f207c591fd0, tqh_last = 0x7f207c6027a8}, ref = 2, parent = 
    0x7f207c63fa90}

Because of the virtio-net-device object's ref is 3, will not enter object_finailze(), *leak resource*. Am I wrong?

Best regards,
-Gonglei

> object_property_add(obj, "bootindex", "int",
>                         virtio_net_get_bootindex,
>                         virtio_net_set_bootindex,
>                         virtio_net_finalize_bootindex, dev, NULL);
> 
> as the previous email, we lay add_boot_device_path() in
> $device_set_bootindex,
> and lay del_boot_device_path() in $device_finalize_bootindex is a good idea
> IMO.
> 
> Do you like it? Thanks.
> 
> Best regards,
> -Gonglei
Gonglei (Arei) Sept. 4, 2014, 11:48 a.m. UTC | #11
> Subject: RE: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> 
> Hi,
> 
> > > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path
> function
> > >
> > > On Wed, Sep 03, 2014 at 06:45:56AM +0000, Gonglei (Arei) wrote:
> > > [...]
> > > > > > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's
> > > pointer
> > > > > to
> > > > > > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add
> a
> > > > > function
> > > > > > named is_same_fw_dev_path() to handle this situation.
> > > > >
> > > > > When hot-unplugging virtio-net-pci I'd expect we free both
> > > > > virtio-net-pci and virtio-net-device (and therefore call
> > > > > del_boot_device_path twice, once for each device).  Can you check
> that?
> > > > >
> > > > Yes, I can.
> > > >
> > > > The del_boot_device_path() is called only once, just for virtio-net-pci.
> > > > For its child, virtio-net-devcie's resource is cleaned by qbus->child
> > unrealizing
> > > > process, will not call device_finalize().
> > >
> > > Then we need to fix this to make sure there's a corresponding
> > > del_boot_device_path() call (with the same pointer) to every
> > > add_boot_device_path() call, instead of adding a hack to
> > > del_boot_device_path().
> > >
> > Good idea. We can add functions named $device_finalize_bootindex(), such
> as:
> >
> > static void virtio_net_set_bootindex(Object *obj, Visitor *v, void *opaque,
> >                                      const char *name, Error
> **errp)
> > {
> >     VirtIONet *n = VIRTIO_NET(obj);
> >
> >     set_bootindex(&n->nic_conf, v, name, errp);
> >     add_boot_device_path(n->nic_conf.bootindex,
> >                          DEVICE(obj), "/ethernet-phy@0");
> > }
> >
> > static void virtio_net_finalize_bootindex(Object *obj, const char *name,
> >                                           void *opaque)
> > {
> >     del_boot_device_path(DEVICE(obj));
> > }
> > ...
> 
> Oops, I found an issue that when we hot-unplug a virtio-net-pci device,
> the virtio_net_finalize_bootindex function will not be called too.
> Maybe this is a potential *bug* which will cause the virtio-net-device's property
> resource leak.
> 
> Paolo, would you have a look at it? Thanks a lot!
> 
> Backtrace as below:
> 
> #0  object_unref (obj=0x7f207c640468) at qom/object.c:711
> #1  0x00007f207b9850da in bus_remove_child (bus=0x7f207c6403f0,
> child=0x7f207c640468) at hw/core/qdev.c:76
> #2  0x00007f207b987cbc in device_unparent (obj=0x7f207c640468) at
> hw/core/qdev.c:1013
> #3  0x00007f207ba9c5a3 in object_finalize_child_property
> (obj=0x7f207c63fa90, name=0x7f207c6027c0 "virtio-backend", opaque=
>     0x7f207c640468) at qom/object.c:1036
> #4  0x00007f207ba9b9a9 in object_property_del (obj=0x7f207c63fa90,
> name=0x7f207c6027c0 "virtio-backend", errp=0x0)
>     at qom/object.c:778
> #5  0x00007f207ba9a9ca in object_property_del_child (obj=0x7f207c63fa90,
> child=0x7f207c640468, errp=0x0) at qom/object.c:382
> #6  0x00007f207ba9aa83 in object_unparent (obj=0x7f207c640468) at
> qom/object.c:391
> #7  0x00007f207b986666 in bus_unparent (obj=0x7f207c6403f0) at
> hw/core/qdev.c:548
> #8  0x00007f207ba9c5a3 in object_finalize_child_property
> (obj=0x7f207c63fa90, name=0x7f207c5db7a0 "virtio-bus", opaque=
>     0x7f207c6403f0) at qom/object.c:1036
> #9  0x00007f207ba9b9a9 in object_property_del (obj=0x7f207c63fa90,
> name=0x7f207c5db7a0 "virtio-bus", errp=0x0) at qom/object.c:778
> #10 0x00007f207ba9a9ca in object_property_del_child (obj=0x7f207c63fa90,
> child=0x7f207c6403f0, errp=0x0) at qom/object.c:382
> #11 0x00007f207ba9aa83 in object_unparent (obj=0x7f207c6403f0) at
> qom/object.c:391
> #12 0x00007f207b987c8f in device_unparent (obj=0x7f207c63fa90) at
> hw/core/qdev.c:1010
> #13 0x00007f207ba9c5a3 in object_finalize_child_property
> (obj=0x7f207c4f2f90, name=0x7f207c604580 "nic1", opaque=0x7f207c63fa90)
>     at qom/object.c:1036
> #14 0x00007f207ba9b9a9 in object_property_del (obj=0x7f207c4f2f90,
> name=0x7f207c604580 "nic1", errp=0x0) at qom/object.c:778
> #15 0x00007f207ba9a9ca in object_property_del_child (obj=0x7f207c4f2f90,
> child=0x7f207c63fa90, errp=0x0) at qom/object.c:382
> #16 0x00007f207ba9aa83 in object_unparent (obj=0x7f207c63fa90) at
> qom/object.c:391
> #17 0x00007f207b94f7c7 in acpi_pcihp_eject_slot (s=0x7f207c5801e8, bsel=0,
> slots=8) at hw/acpi/pcihp.c:139
> #18 0x00007f207b94fe65 in pci_write (opaque=0x7f207c5801e8, addr=8,
> data=8, size=4) at hw/acpi/pcihp.c:277
> #19 0x00007f207b818a38 in memory_region_write_accessor
> (mr=0x7f207c580df8, addr=8, value=0x7f2075886b38, size=4, shift=0, mask=
>     4294967295) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:443
> #20 0x00007f207b818b74 in access_with_adjusted_size (addr=8,
> value=0x7f2075886b38, size=4, access_size_min=1, access_size_max=4,
>     access=0x7f207b8189ab <memory_region_write_accessor>,
> mr=0x7f207c580df8) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:480
> #21 0x00007f207b81c141 in memory_region_dispatch_write
> (mr=0x7f207c580df8, addr=8, data=8, size=4)
>     at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1137
> #22 0x00007f207b81fcf0 in io_mem_write (mr=0x7f207c580df8, addr=8, val=8,
> size=4) at /mnt/sdb/gonglei/qemu.git/qemu/memory.c:1973
> #23 0x00007f207b7ca289 in address_space_rw (as=0x7f207bfeb1e0
> <address_space_io>, addr=44552, buf=0x7f207b719000 "\b", len=4,
>     is_write=true) at /mnt/sdb/gonglei/qemu.git/qemu/exec.c:2042
> #24 0x00007f207b815266 in kvm_handle_io (port=44552,
> data=0x7f207b719000, direction=1, size=4, count=1)
>     at /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1597
> #25 0x00007f207b8158a4 in kvm_cpu_exec (cpu=0x7f207c4f6610) at
> /mnt/sdb/gonglei/qemu.git/qemu/kvm-all.c:1748
> #26 0x00007f207b7fcd68 in qemu_kvm_cpu_thread_fn (arg=0x7f207c4f6610)
> at /mnt/sdb/gonglei/qemu.git/qemu/cpus.c:940
> #27 0x00007f2078a357f6 in start_thread () from /lib64/libpthread.so.0
> #28 0x00007f207879109d in clone () from /lib64/libc.so.6
> #29 0x0000000000000000 in ?? ()
> (gdb) p *obj
> $64 = {class = 0x7f207c4bc5f0, free = 0x0, properties = {tqh_first =
> 0x7f207c591fd0, tqh_last = 0x7f207c6027a8}, ref = 3, parent =
>     0x7f207c63fa90}
> (gdb) n
> 712         if (!obj) {
> (gdb)
> 715         g_assert(obj->ref > 0);
> (gdb)
> 718         if (atomic_fetch_dec(&obj->ref) == 1) {
> (gdb)
> 721     }
> (gdb) p *obj
> $65 = {class = 0x7f207c4bc5f0, free = 0x0, properties = {tqh_first =
> 0x7f207c591fd0, tqh_last = 0x7f207c6027a8}, ref = 2, parent =
>     0x7f207c63fa90}
> 
> Because of the virtio-net-device object's ref is 3, will not enter object_finailze(),
> *leak resource*. Am I wrong?
> 

I've confirmed that this is a bug, and have posted a patch fix it. 

Please review, thanks!

[PATCH] virtio-pci: fix virtio-net child refcount in transports

Best regards,
-Gonglei
Gonglei (Arei) Sept. 4, 2014, 12:06 p.m. UTC | #12
Hi,

> 
> I've confirmed that this is a bug, and have posted a patch fix it.
> 
> Please review, thanks!
> 
> [PATCH] virtio-pci: fix virtio-net child refcount in transports
> 
> Best regards,
> -Gonglei

So, about Gerd's previous question:

> When hot-unplugging virtio-net-pci I'd expect we free both
> virtio-net-pci and virtio-net-device (and therefore call
> del_boot_device_path twice, once for each device).  Can you check that?
>  
After fix that bug, you are right. The del_boot_device_path will be called twice.
I can rework del_boot_device_path(), remove is_same_fw_dev_path().

The new del_boot_device_path function as below:

void del_boot_device_path(DeviceState *dev)
{
    FWBootEntry *i;

    assert(dev != NULL);

    /* remove all entries of the assigned device */
    QTAILQ_FOREACH(i, &fw_boot_order, link) {
        if (i->dev == NULL) {
            continue;
        }
        if (i->dev == dev) {
            QTAILQ_REMOVE(&fw_boot_order, i, link);
            g_free(i->suffix);
            g_free(i);

            break;
        }
    }
}

Best regards,
-Gonglei
Eduardo Habkost Sept. 4, 2014, 1:22 p.m. UTC | #13
On Thu, Sep 04, 2014 at 03:01:41AM +0000, Gonglei (Arei) wrote:
> Hi,
> 
> > From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> > Sent: Thursday, September 04, 2014 2:13 AM
> > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> > 
> > On Wed, Sep 03, 2014 at 06:45:56AM +0000, Gonglei (Arei) wrote:
> > [...]
> > > > > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's
> > pointer
> > > > to
> > > > > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add a
> > > > function
> > > > > named is_same_fw_dev_path() to handle this situation.
> > > >
> > > > When hot-unplugging virtio-net-pci I'd expect we free both
> > > > virtio-net-pci and virtio-net-device (and therefore call
> > > > del_boot_device_path twice, once for each device).  Can you check that?
> > > >
> > > Yes, I can.
> > >
> > > The del_boot_device_path() is called only once, just for virtio-net-pci.
> > > For its child, virtio-net-devcie's resource is cleaned by qbus->child unrealizing
> > > process, will not call device_finalize().
> > 
> > Then we need to fix this to make sure there's a corresponding
> > del_boot_device_path() call (with the same pointer) to every
> > add_boot_device_path() call, instead of adding a hack to
> > del_boot_device_path().
> > 
> Good idea. We can add functions named $device_finalize_bootindex(), such as:
> 
> static void virtio_net_set_bootindex(Object *obj, Visitor *v, void *opaque,
>                                      const char *name, Error **errp)
> {
>     VirtIONet *n = VIRTIO_NET(obj);
>     
>     set_bootindex(&n->nic_conf, v, name, errp);
>     add_boot_device_path(n->nic_conf.bootindex,
>                          DEVICE(obj), "/ethernet-phy@0"); 
> }
> 
> static void virtio_net_finalize_bootindex(Object *obj, const char *name,
>                                           void *opaque)
> {
>     del_boot_device_path(DEVICE(obj));
> }
> ...
> object_property_add(obj, "bootindex", "int",
>                         virtio_net_get_bootindex, 
>                         virtio_net_set_bootindex,
>                         virtio_net_finalize_bootindex, dev, NULL);
> 
> as the previous email, we lay add_boot_device_path() in $device_set_bootindex,
> and lay del_boot_device_path() in $device_finalize_bootindex is a good idea IMO.

Whatever the approach we use, can we have a wrapper to reduce code
duplication? e.g. a:
  void device_add_bootindex_property(DeviceState *dev, int32_t *field, const char *suffix)
function.

Then instead of reimplementing set/get/finalize functions, device code
could just call something like:
  device_add_bootindex_property(dev, &n->nic_conf.bootindex,
                                "/ethernet-phy@0");
Gonglei (Arei) Sept. 5, 2014, 12:44 a.m. UTC | #14
> Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> 
> On Thu, Sep 04, 2014 at 03:01:41AM +0000, Gonglei (Arei) wrote:
> > Hi,
> >
> > > From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> > > Sent: Thursday, September 04, 2014 2:13 AM
> > > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path
> function
> > >
> > > On Wed, Sep 03, 2014 at 06:45:56AM +0000, Gonglei (Arei) wrote:
> > > [...]
> > > > > > 4. When we hotplug the virtio-net-pci device, only pass virtio-net-pci's
> > > pointer
> > > > > to
> > > > > > del_boot_device_path(). But virtio-net-pci != virtio-net-device, so I add
> a
> > > > > function
> > > > > > named is_same_fw_dev_path() to handle this situation.
> > > > >
> > > > > When hot-unplugging virtio-net-pci I'd expect we free both
> > > > > virtio-net-pci and virtio-net-device (and therefore call
> > > > > del_boot_device_path twice, once for each device).  Can you check
> that?
> > > > >
> > > > Yes, I can.
> > > >
> > > > The del_boot_device_path() is called only once, just for virtio-net-pci.
> > > > For its child, virtio-net-devcie's resource is cleaned by qbus->child
> unrealizing
> > > > process, will not call device_finalize().
> > >
> > > Then we need to fix this to make sure there's a corresponding
> > > del_boot_device_path() call (with the same pointer) to every
> > > add_boot_device_path() call, instead of adding a hack to
> > > del_boot_device_path().
> > >
> > Good idea. We can add functions named $device_finalize_bootindex(), such
> as:
> >
> > static void virtio_net_set_bootindex(Object *obj, Visitor *v, void *opaque,
> >                                      const char *name, Error
> **errp)
> > {
> >     VirtIONet *n = VIRTIO_NET(obj);
> >
> >     set_bootindex(&n->nic_conf, v, name, errp);
> >     add_boot_device_path(n->nic_conf.bootindex,
> >                          DEVICE(obj), "/ethernet-phy@0");
> > }
> >
> > static void virtio_net_finalize_bootindex(Object *obj, const char *name,
> >                                           void *opaque)
> > {
> >     del_boot_device_path(DEVICE(obj));
> > }
> > ...
> > object_property_add(obj, "bootindex", "int",
> >                         virtio_net_get_bootindex,
> >                         virtio_net_set_bootindex,
> >                         virtio_net_finalize_bootindex, dev, NULL);
> >
> > as the previous email, we lay add_boot_device_path() in
> $device_set_bootindex,
> > and lay del_boot_device_path() in $device_finalize_bootindex is a good idea
> IMO.
> 
> Whatever the approach we use, 

Hmm... we just call this function in device_finalize() as I have used.
My above explanation is wrong because of a virtio-net's bug. Thanks.

> can we have a wrapper to reduce code
> duplication? e.g. a:
>   void device_add_bootindex_property(DeviceState *dev, int32_t *field, const
> char *suffix)
> function.
> 
> Then instead of reimplementing set/get/finalize functions, device code
> could just call something like:
>   device_add_bootindex_property(dev, &n->nic_conf.bootindex,
>                                 "/ethernet-phy@0");
> 

This way we cannot attach our target that changing bootindex and take effect
during vm rebooting.

> --
> Eduardo

Best regards,
-Gonglei
Eduardo Habkost Sept. 5, 2014, 2:20 a.m. UTC | #15
On Fri, Sep 05, 2014 at 12:44:56AM +0000, Gonglei (Arei) wrote:
[...]
> > can we have a wrapper to reduce code
> > duplication? e.g. a:
> >   void device_add_bootindex_property(DeviceState *dev, int32_t *field, const
> > char *suffix)
> > function.
> > 
> > Then instead of reimplementing set/get/finalize functions, device code
> > could just call something like:
> >   device_add_bootindex_property(dev, &n->nic_conf.bootindex,
> >                                 "/ethernet-phy@0");
> > 
> 
> This way we cannot attach our target that changing bootindex and take effect
> during vm rebooting.

I don't understand what you mean, here. Whatever you are planning to do on the
device-specific setter/getters, if they all look the same, you can write a
common getter/setter to do exactly the same steps, and register it inside
device_add_bootindex_property(). This way, patches 08/27 to 26/27 can become
one-liners, instead of adding more 20 lines each.

I mean something like this:

typedef struct BootIndexProperty
{
    int32_t *field;
    const char *suffix;
} BootIndexProperty;

static void device_get_bootindex(Object *obj, Visitor *v, void *opaque,
                                 const char *name, Error **errp)
{
    BootIndexProperty *prop = opaque;
    visit_type_int32(v, prop->field, name, errp);
}

static void device_set_bootindex(Object *obj, Visitor *v, void *opaque,
                                 const char *name, Error **errp)
{
    DeviceState *dev = DEVICE(obj);
    BootIndexProperty *prop = opaque;
    int32_t boot_index;
    Error *local_err = NULL;

    visit_type_int32(v, &boot_index, name, &local_err);
    if (local_err) {
        goto out;
    }

    check_boot_index(boot_index, &local_err);
    if (local_err) {
        goto out;
    }

    *prop->field = boot_index;
    add_boot_device_path(boot_index, dev, prop->suffix);

out:
    if (local_err) {
        error_propagate(errp, local_err);
    }
}

static void property_release_bootindex(Object *obj, const char *name,
                                       void *opaque)

{
    BootIndexProperty *prop = opaque;
    DeviceState *dev = DEVICE(obj);
    del_boot_device_path(dev);
    g_free(prop);
}

void device_add_bootindex_property(DeviceState *dev, int32_t *field,
                                   const char *suffix, Error **errp)
{
    Error *local_err = NULL;
    BootIndexProperty *prop = g_malloc0(sizeof(*prop));

    prop->field = field;
    prop->suffix = suffix;
    object_property_add(OBJECT(dev), "bootindex", "int32",
                        device_get_bootindex,
                        device_set_bootindex,
                        property_release_bootindex,
                        prop, &local_err);

    if (local_err) {
        error_propagate(errp, local_err);
        g_free(prop);
    }
}
Gonglei (Arei) Sept. 5, 2014, 2:42 a.m. UTC | #16
> From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> Sent: Friday, September 05, 2014 10:20 AM
> Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> 
> On Fri, Sep 05, 2014 at 12:44:56AM +0000, Gonglei (Arei) wrote:
> [...]
> > > can we have a wrapper to reduce code
> > > duplication? e.g. a:
> > >   void device_add_bootindex_property(DeviceState *dev, int32_t *field,
> const
> > > char *suffix)
> > > function.
> > >
> > > Then instead of reimplementing set/get/finalize functions, device code
> > > could just call something like:
> > >   device_add_bootindex_property(dev, &n->nic_conf.bootindex,
> > >                                 "/ethernet-phy@0");
> > >
> >
> > This way we cannot attach our target that changing bootindex and take
> effect
> > during vm rebooting.
> 
> I don't understand what you mean, here. Whatever you are planning to do on
> the
> device-specific setter/getters, if they all look the same, you can write a
> common getter/setter to do exactly the same steps, and register it inside
> device_add_bootindex_property(). This way, patches 08/27 to 26/27 can
> become
> one-liners, instead of adding more 20 lines each.
> 
> I mean something like this:
> 
OK. Thanks for explanation. :)

I have two questions:

1) virtio-net-pci device do need special handle in device_set_bootindex(). 
2) isa-fdc' property is bootindexA and bootindexB, maybe we
can add more a parameter for device_add_bootindex_property()?

Best regards,
-Gonglei

> typedef struct BootIndexProperty
> {
>     int32_t *field;
>     const char *suffix;
> } BootIndexProperty;
> 
> static void device_get_bootindex(Object *obj, Visitor *v, void *opaque,
>                                  const char *name, Error **errp)
> {
>     BootIndexProperty *prop = opaque;
>     visit_type_int32(v, prop->field, name, errp);
> }
> 
> static void device_set_bootindex(Object *obj, Visitor *v, void *opaque,
>                                  const char *name, Error **errp)
> {
>     DeviceState *dev = DEVICE(obj);
>     BootIndexProperty *prop = opaque;
>     int32_t boot_index;
>     Error *local_err = NULL;
> 
>     visit_type_int32(v, &boot_index, name, &local_err);
>     if (local_err) {
>         goto out;
>     }
> 
>     check_boot_index(boot_index, &local_err);
>     if (local_err) {
>         goto out;
>     }
> 
>     *prop->field = boot_index;
>     add_boot_device_path(boot_index, dev, prop->suffix);
> 
> out:
>     if (local_err) {
>         error_propagate(errp, local_err);
>     }
> }
> 
> static void property_release_bootindex(Object *obj, const char *name,
>                                        void *opaque)
> 
> {
>     BootIndexProperty *prop = opaque;
>     DeviceState *dev = DEVICE(obj);
>     del_boot_device_path(dev);
>     g_free(prop);
> }
> 
> void device_add_bootindex_property(DeviceState *dev, int32_t *field,
>                                    const char *suffix, Error **errp)
> {
>     Error *local_err = NULL;
>     BootIndexProperty *prop = g_malloc0(sizeof(*prop));
> 
>     prop->field = field;
>     prop->suffix = suffix;
>     object_property_add(OBJECT(dev), "bootindex", "int32",
>                         device_get_bootindex,
>                         device_set_bootindex,
>                         property_release_bootindex,
>                         prop, &local_err);
> 
>     if (local_err) {
>         error_propagate(errp, local_err);
>         g_free(prop);
>     }
> }
> 
> --
> Eduardo
Eduardo Habkost Sept. 5, 2014, 2:56 p.m. UTC | #17
On Fri, Sep 05, 2014 at 02:42:48AM +0000, Gonglei (Arei) wrote:
> > From: Eduardo Habkost [mailto:ehabkost@redhat.com]
> > Sent: Friday, September 05, 2014 10:20 AM
> > Subject: Re: [PATCH v6 02/27] bootindex: add del_boot_device_path function
> > 
> > On Fri, Sep 05, 2014 at 12:44:56AM +0000, Gonglei (Arei) wrote:
> > [...]
> > > > can we have a wrapper to reduce code
> > > > duplication? e.g. a:
> > > >   void device_add_bootindex_property(DeviceState *dev, int32_t *field,
> > const
> > > > char *suffix)
> > > > function.
> > > >
> > > > Then instead of reimplementing set/get/finalize functions, device code
> > > > could just call something like:
> > > >   device_add_bootindex_property(dev, &n->nic_conf.bootindex,
> > > >                                 "/ethernet-phy@0");
> > > >
> > >
> > > This way we cannot attach our target that changing bootindex and take
> > effect
> > > during vm rebooting.
> > 
> > I don't understand what you mean, here. Whatever you are planning to do on
> > the
> > device-specific setter/getters, if they all look the same, you can write a
> > common getter/setter to do exactly the same steps, and register it inside
> > device_add_bootindex_property(). This way, patches 08/27 to 26/27 can
> > become
> > one-liners, instead of adding more 20 lines each.
> > 
> > I mean something like this:
> > 
> OK. Thanks for explanation. :)
> 
> I have two questions:
> 
> 1) virtio-net-pci device do need special handle in device_set_bootindex(). 
> 2) isa-fdc' property is bootindexA and bootindexB, maybe we
> can add more a parameter for device_add_bootindex_property()?

I see that you added two extra parameters to
device_add_bootindex_property(). Looks like a good solution, to me.
diff mbox

Patch

diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index b343df5..672984c 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -208,6 +208,7 @@  void do_usb_del(Monitor *mon, const QDict *qdict);
 void usb_info(Monitor *mon, const QDict *qdict);
 
 void check_boot_index(int32_t bootindex, Error **errp);
+void del_boot_device_path(DeviceState *dev);
 void add_boot_device_path(int32_t bootindex, DeviceState *dev,
                           const char *suffix);
 char *get_boot_devices_list(size_t *size, bool ignore_suffixes);
diff --git a/vl.c b/vl.c
index 76fb008..5e57541 100644
--- a/vl.c
+++ b/vl.c
@@ -1252,6 +1252,42 @@  void check_boot_index(int32_t bootindex, Error **errp)
     }
 }
 
+static bool is_same_fw_dev_path(DeviceState *src, DeviceState *dst)
+{
+    bool ret = false;
+    char *devpath_src = qdev_get_fw_dev_path(src);
+    char *devpath_dst = qdev_get_fw_dev_path(dst);
+
+    if (!strcmp(devpath_src, devpath_dst)) {
+        ret = true;
+    }
+
+    g_free(devpath_src);
+    g_free(devpath_dst);
+    return ret;
+}
+
+void del_boot_device_path(DeviceState *dev)
+{
+    FWBootEntry *i;
+
+    assert(dev != NULL);
+
+    /* remove all entries of the assigned device */
+    QTAILQ_FOREACH(i, &fw_boot_order, link) {
+        if (i->dev == NULL) {
+            continue;
+        }
+        if ((i->dev == dev || is_same_fw_dev_path(i->dev, dev))) {
+            QTAILQ_REMOVE(&fw_boot_order, i, link);
+            g_free(i->suffix);
+            g_free(i);
+
+            break;
+        }
+    }
+}
+
 void add_boot_device_path(int32_t bootindex, DeviceState *dev,
                           const char *suffix)
 {