qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] hw: Delay timer_new() from init to realize to avoid memleaks
@ 2020-02-15 15:47 Philippe Mathieu-Daudé
  2020-02-15 15:47 ` [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() " Philippe Mathieu-Daudé
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-15 15:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pan Nengyuan, Philippe Mathieu-Daudé, Corey Minyard

After reviewing various patches from Pan Nengyuan fixing errors
reported Huawei's Euler Robot, I wrote this tiny coccinelle script
to find all occurences of this pattern:

    @ match @
    identifier instance_init;
    typedef Object;
    identifier obj;
    expression val, scale;
    identifier clock_type, callback, opaque;
    position pos;
    @@
    static void instance_init(Object *obj)
    {
      <...
    (
      val = timer_new@pos(clock_type, scale, callback, opaque);
    |
      val = timer_new_ns@pos(clock_type, callback, opaque);
    |
      val = timer_new_us@pos(clock_type, callback, opaque);
    |
      val = timer_new_ms@pos(clock_type, callback, opaque);
    )
      ...>
    }

    @ script:python @
    f << match.instance_init;
    p << match.pos;
    @@
    print "check %s:%s:%s in %s()" % (p[0].file, p[0].line, p[0].column, f)

The script produces:

  $ docker run --rm -v $PWD:$PWD -w $PWD philmd/coccinelle \
     --macro-file scripts/cocci-macro-file.h \
     --sp-file scripts/coccinelle/init_timer_new.cocci
  init_defs_builtins: /usr/lib/coccinelle/standard.h
  init_defs: scripts/cocci-macro-file.h
  check hw/ipmi/ipmi_bmc_extern.c:505:24 in ipmi_bmc_extern_init()
  check hw/misc/mos6522.c:489:25 in mos6522_init()
  check hw/rtc/pl031.c:194:15 in pl031_init()
  check hw/arm/pxa2xx.c:1137:19 in pxa2xx_rtc_init()
  check target/s390x/cpu.c:283:8 in s390_cpu_initfn()
  check hw/sd/sd.c:2061:26 in sd_instance_init()
  check hw/arm/spitz.c:527:18 in spitz_keyboard_init()
  check hw/arm/strongarm.c:402:19 in strongarm_rtc_init()
  check hw/arm/strongarm.c:1244:26 in strongarm_uart_init()

Pan fixed most of the occurences. This series fixes the last two.

Philippe Mathieu-Daudé (2):
  hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid
    memleaks
  hw/sd/sd: Delay timer_new_ns() from init to realize to avoid memleaks

 hw/ipmi/ipmi_bmc_extern.c | 12 ++++++++++--
 hw/sd/sd.c                | 12 ++++++++++--
 2 files changed, 20 insertions(+), 4 deletions(-)

-- 
2.21.1



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-15 15:47 [PATCH 0/2] hw: Delay timer_new() from init to realize to avoid memleaks Philippe Mathieu-Daudé
@ 2020-02-15 15:47 ` Philippe Mathieu-Daudé
  2020-02-16 19:43   ` Corey Minyard
  2020-02-17 13:25   ` Peter Maydell
  2020-02-15 15:47 ` [PATCH 2/2] hw/sd/sd: " Philippe Mathieu-Daudé
  2020-02-16  2:10 ` [PATCH 0/2] hw: Delay timer_new() " Richard Henderson
  2 siblings, 2 replies; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-15 15:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pan Nengyuan, Philippe Mathieu-Daudé, Corey Minyard

In commit f3a508eb4e the Euler Robot reported calling timer_new()
in instance_init() can leak heap memory. The easier fix is to
delay the timer creation at instance realize(). Similarly move
timer_del() into a new instance unrealize() method.

This case was found with the following coccinelle script:

    @ match @
    identifier instance_init;
    typedef Object;
    identifier obj;
    expression val, scale;
    identifier clock_type, callback, opaque;
    position pos;
    @@
    static void instance_init(Object *obj)
    {
      <...
    (
      val = timer_new@pos(clock_type, scale, callback, opaque);
    |
      val = timer_new_ns@pos(clock_type, callback, opaque);
    |
      val = timer_new_us@pos(clock_type, callback, opaque);
    |
      val = timer_new_ms@pos(clock_type, callback, opaque);
    )
      ...>
    }

    @ script:python @
    f << match.instance_init;
    p << match.pos;
    @@
    print "check %s:%s:%s in %s()" % (p[0].file, p[0].line, p[0].column, f)

Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
Cc: Pan Nengyuan <pannengyuan@huawei.com>
---
 hw/ipmi/ipmi_bmc_extern.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index f9a13e0a44..9144ac6c38 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -463,6 +463,15 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
 
     qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
                              chr_event, NULL, ibe, NULL, true);
+
+    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
+}
+
+static void ipmi_bmc_extern_unrealize(DeviceState *dev, Error **errp)
+{
+    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
+
+    timer_del(ibe->extern_timer);
 }
 
 static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
@@ -502,7 +511,6 @@ static void ipmi_bmc_extern_init(Object *obj)
 {
     IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
 
-    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
     vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
 }
 
@@ -510,7 +518,6 @@ static void ipmi_bmc_extern_finalize(Object *obj)
 {
     IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
 
-    timer_del(ibe->extern_timer);
     timer_free(ibe->extern_timer);
 }
 
@@ -528,6 +535,7 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
     bk->handle_reset = ipmi_bmc_extern_handle_reset;
     dc->hotpluggable = false;
     dc->realize = ipmi_bmc_extern_realize;
+    dc->unrealize = ipmi_bmc_extern_unrealize;
     device_class_set_props(dc, ipmi_bmc_extern_properties);
 }
 
-- 
2.21.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/2] hw/sd/sd: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-15 15:47 [PATCH 0/2] hw: Delay timer_new() from init to realize to avoid memleaks Philippe Mathieu-Daudé
  2020-02-15 15:47 ` [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() " Philippe Mathieu-Daudé
@ 2020-02-15 15:47 ` Philippe Mathieu-Daudé
  2020-02-17 13:26   ` Peter Maydell
  2020-02-16  2:10 ` [PATCH 0/2] hw: Delay timer_new() " Richard Henderson
  2 siblings, 1 reply; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-15 15:47 UTC (permalink / raw)
  To: qemu-devel; +Cc: Pan Nengyuan, Philippe Mathieu-Daudé, Corey Minyard

In commit f3a508eb4e the Euler Robot reported calling timer_new()
in instance_init() can leak heap memory. The easier fix is to
delay the timer creation at instance realize(). Similarly move
timer_del() into a new instance unrealize() method.

This case was found with the following coccinelle script:

    @ match @
    identifier instance_init;
    typedef Object;
    identifier obj;
    expression val, scale;
    identifier clock_type, callback, opaque;
    position pos;
    @@
    static void instance_init(Object *obj)
    {
      <...
    (
      val = timer_new@pos(clock_type, scale, callback, opaque);
    |
      val = timer_new_ns@pos(clock_type, callback, opaque);
    |
      val = timer_new_us@pos(clock_type, callback, opaque);
    |
      val = timer_new_ms@pos(clock_type, callback, opaque);
    )
      ...>
    }

    @ script:python @
    f << match.instance_init;
    p << match.pos;
    @@
    print "check %s:%s:%s in %s()" % (p[0].file, p[0].line, p[0].column, f)

Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
Cc: Pan Nengyuan <pannengyuan@huawei.com>
---
 hw/sd/sd.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 71a9af09ab..d72cf3de2a 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -2058,14 +2058,12 @@ static void sd_instance_init(Object *obj)
     SDState *sd = SD_CARD(obj);
 
     sd->enable = true;
-    sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
 }
 
 static void sd_instance_finalize(Object *obj)
 {
     SDState *sd = SD_CARD(obj);
 
-    timer_del(sd->ocr_power_timer);
     timer_free(sd->ocr_power_timer);
 }
 
@@ -2098,6 +2096,15 @@ static void sd_realize(DeviceState *dev, Error **errp)
         }
         blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
     }
+
+    sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
+}
+
+static void sd_unrealize(DeviceState *dev, Error **errp)
+{
+    SDState *sd = SD_CARD(dev);
+
+    timer_del(sd->ocr_power_timer);
 }
 
 static Property sd_properties[] = {
@@ -2118,6 +2125,7 @@ static void sd_class_init(ObjectClass *klass, void *data)
     SDCardClass *sc = SD_CARD_CLASS(klass);
 
     dc->realize = sd_realize;
+    dc->unrealize = sd_unrealize;
     device_class_set_props(dc, sd_properties);
     dc->vmsd = &sd_vmstate;
     dc->reset = sd_reset;
-- 
2.21.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 0/2] hw: Delay timer_new() from init to realize to avoid memleaks
  2020-02-15 15:47 [PATCH 0/2] hw: Delay timer_new() from init to realize to avoid memleaks Philippe Mathieu-Daudé
  2020-02-15 15:47 ` [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() " Philippe Mathieu-Daudé
  2020-02-15 15:47 ` [PATCH 2/2] hw/sd/sd: " Philippe Mathieu-Daudé
@ 2020-02-16  2:10 ` Richard Henderson
  2 siblings, 0 replies; 19+ messages in thread
From: Richard Henderson @ 2020-02-16  2:10 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, qemu-devel; +Cc: Pan Nengyuan, Corey Minyard

On 2/15/20 7:47 AM, Philippe Mathieu-Daudé wrote:
> Philippe Mathieu-Daudé (2):
>   hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid
>     memleaks
>   hw/sd/sd: Delay timer_new_ns() from init to realize to avoid memleaks

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-15 15:47 ` [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() " Philippe Mathieu-Daudé
@ 2020-02-16 19:43   ` Corey Minyard
  2020-02-17 13:25   ` Peter Maydell
  1 sibling, 0 replies; 19+ messages in thread
From: Corey Minyard @ 2020-02-16 19:43 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: Pan Nengyuan, qemu-devel

On Sat, Feb 15, 2020 at 04:47:05PM +0100, Philippe Mathieu-Daudé wrote:
> In commit f3a508eb4e the Euler Robot reported calling timer_new()
> in instance_init() can leak heap memory. The easier fix is to
> delay the timer creation at instance realize(). Similarly move
> timer_del() into a new instance unrealize() method.
> 
> This case was found with the following coccinelle script:
> 
>     @ match @
>     identifier instance_init;
>     typedef Object;
>     identifier obj;
>     expression val, scale;
>     identifier clock_type, callback, opaque;
>     position pos;
>     @@
>     static void instance_init(Object *obj)
>     {
>       <...
>     (
>       val = timer_new@pos(clock_type, scale, callback, opaque);
>     |
>       val = timer_new_ns@pos(clock_type, callback, opaque);
>     |
>       val = timer_new_us@pos(clock_type, callback, opaque);
>     |
>       val = timer_new_ms@pos(clock_type, callback, opaque);
>     )
>       ...>
>     }
> 
>     @ script:python @
>     f << match.instance_init;
>     p << match.pos;
>     @@
>     print "check %s:%s:%s in %s()" % (p[0].file, p[0].line, p[0].column, f)
> 
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>

This looks ok to me:

Acked-by: Corey Minyard <cminyard@mvista.com>

I can take it into my tree, if you like.

-corey

> ---
> Cc: Pan Nengyuan <pannengyuan@huawei.com>
> ---
>  hw/ipmi/ipmi_bmc_extern.c | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
> index f9a13e0a44..9144ac6c38 100644
> --- a/hw/ipmi/ipmi_bmc_extern.c
> +++ b/hw/ipmi/ipmi_bmc_extern.c
> @@ -463,6 +463,15 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
>  
>      qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
>                               chr_event, NULL, ibe, NULL, true);
> +
> +    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
> +}
> +
> +static void ipmi_bmc_extern_unrealize(DeviceState *dev, Error **errp)
> +{
> +    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
> +
> +    timer_del(ibe->extern_timer);
>  }
>  
>  static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
> @@ -502,7 +511,6 @@ static void ipmi_bmc_extern_init(Object *obj)
>  {
>      IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
>  
> -    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
>      vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
>  }
>  
> @@ -510,7 +518,6 @@ static void ipmi_bmc_extern_finalize(Object *obj)
>  {
>      IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
>  
> -    timer_del(ibe->extern_timer);
>      timer_free(ibe->extern_timer);
>  }
>  
> @@ -528,6 +535,7 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
>      bk->handle_reset = ipmi_bmc_extern_handle_reset;
>      dc->hotpluggable = false;
>      dc->realize = ipmi_bmc_extern_realize;
> +    dc->unrealize = ipmi_bmc_extern_unrealize;
>      device_class_set_props(dc, ipmi_bmc_extern_properties);
>  }
>  
> -- 
> 2.21.1
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-15 15:47 ` [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() " Philippe Mathieu-Daudé
  2020-02-16 19:43   ` Corey Minyard
@ 2020-02-17 13:25   ` Peter Maydell
  2020-02-17 13:48     ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 19+ messages in thread
From: Peter Maydell @ 2020-02-17 13:25 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: Pan Nengyuan, QEMU Developers, Corey Minyard

On Sat, 15 Feb 2020 at 15:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>
> In commit f3a508eb4e the Euler Robot reported calling timer_new()
> in instance_init() can leak heap memory. The easier fix is to
> delay the timer creation at instance realize(). Similarly move
> timer_del() into a new instance unrealize() method.
>
> This case was found with the following coccinelle script:
>
>     @ match @
>     identifier instance_init;
>     typedef Object;
>     identifier obj;
>     expression val, scale;
>     identifier clock_type, callback, opaque;
>     position pos;
>     @@
>     static void instance_init(Object *obj)
>     {
>       <...
>     (
>       val = timer_new@pos(clock_type, scale, callback, opaque);
>     |
>       val = timer_new_ns@pos(clock_type, callback, opaque);
>     |
>       val = timer_new_us@pos(clock_type, callback, opaque);
>     |
>       val = timer_new_ms@pos(clock_type, callback, opaque);
>     )
>       ...>
>     }
>
>     @ script:python @
>     f << match.instance_init;
>     p << match.pos;
>     @@
>     print "check %s:%s:%s in %s()" % (p[0].file, p[0].line, p[0].column, f)
>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
> Cc: Pan Nengyuan <pannengyuan@huawei.com>
> ---
>  hw/ipmi/ipmi_bmc_extern.c | 12 ++++++++++--
>  1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
> index f9a13e0a44..9144ac6c38 100644
> --- a/hw/ipmi/ipmi_bmc_extern.c
> +++ b/hw/ipmi/ipmi_bmc_extern.c
> @@ -463,6 +463,15 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
>
>      qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
>                               chr_event, NULL, ibe, NULL, true);
> +
> +    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
> +}
> +
> +static void ipmi_bmc_extern_unrealize(DeviceState *dev, Error **errp)
> +{
> +    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
> +
> +    timer_del(ibe->extern_timer);
>  }
>
>  static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
> @@ -502,7 +511,6 @@ static void ipmi_bmc_extern_init(Object *obj)
>  {
>      IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
>
> -    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
>      vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
>  }
>
> @@ -510,7 +518,6 @@ static void ipmi_bmc_extern_finalize(Object *obj)
>  {
>      IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
>
> -    timer_del(ibe->extern_timer);
>      timer_free(ibe->extern_timer);
>  }

So we now call timer_new in realize, and timer_del in unrealize,
but timer_free in finalize. This seems unbalanced -- why not
call timer_free in unrealize too, if we're moving things?

Also, this is a case of code that's actually doing things right:
we free the memory that we allocated in init in finalize. So
we're not fixing any leak here, we're just moving code around
(which is reasonable if we're trying to standardize on "call
timer_new in realize, not init", but should be noted in the
commit message).

thanks
-- PMM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] hw/sd/sd: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-15 15:47 ` [PATCH 2/2] hw/sd/sd: " Philippe Mathieu-Daudé
@ 2020-02-17 13:26   ` Peter Maydell
  2020-06-05  5:05     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 19+ messages in thread
From: Peter Maydell @ 2020-02-17 13:26 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé; +Cc: Pan Nengyuan, QEMU Developers, Corey Minyard

On Sat, 15 Feb 2020 at 15:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>
> In commit f3a508eb4e the Euler Robot reported calling timer_new()
> in instance_init() can leak heap memory. The easier fix is to
> delay the timer creation at instance realize(). Similarly move
> timer_del() into a new instance unrealize() method.

> diff --git a/hw/sd/sd.c b/hw/sd/sd.c
> index 71a9af09ab..d72cf3de2a 100644
> --- a/hw/sd/sd.c
> +++ b/hw/sd/sd.c
> @@ -2058,14 +2058,12 @@ static void sd_instance_init(Object *obj)
>      SDState *sd = SD_CARD(obj);
>
>      sd->enable = true;
> -    sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
>  }
>
>  static void sd_instance_finalize(Object *obj)
>  {
>      SDState *sd = SD_CARD(obj);
>
> -    timer_del(sd->ocr_power_timer);
>      timer_free(sd->ocr_power_timer);
>  }
>
> @@ -2098,6 +2096,15 @@ static void sd_realize(DeviceState *dev, Error **errp)
>          }
>          blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
>      }
> +
> +    sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
> +}
> +
> +static void sd_unrealize(DeviceState *dev, Error **errp)
> +{
> +    SDState *sd = SD_CARD(dev);
> +
> +    timer_del(sd->ocr_power_timer);
>  }

Here too the old code was doing things correctly in that
it does a timer_del/timer_free on the timer it allocates
in instance_init, and the new code has weirdly split the
freeing between unrealize and finalize.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 13:25   ` Peter Maydell
@ 2020-02-17 13:48     ` Philippe Mathieu-Daudé
  2020-02-17 14:06       ` Peter Maydell
  0 siblings, 1 reply; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-17 13:48 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On 2/17/20 2:25 PM, Peter Maydell wrote:
> On Sat, 15 Feb 2020 at 15:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>>
>> In commit f3a508eb4e the Euler Robot reported calling timer_new()
>> in instance_init() can leak heap memory. The easier fix is to
>> delay the timer creation at instance realize(). Similarly move
>> timer_del() into a new instance unrealize() method.
>>
>> This case was found with the following coccinelle script:
>>
>>      @ match @
>>      identifier instance_init;
>>      typedef Object;
>>      identifier obj;
>>      expression val, scale;
>>      identifier clock_type, callback, opaque;
>>      position pos;
>>      @@
>>      static void instance_init(Object *obj)
>>      {
>>        <...
>>      (
>>        val = timer_new@pos(clock_type, scale, callback, opaque);
>>      |
>>        val = timer_new_ns@pos(clock_type, callback, opaque);
>>      |
>>        val = timer_new_us@pos(clock_type, callback, opaque);
>>      |
>>        val = timer_new_ms@pos(clock_type, callback, opaque);
>>      )
>>        ...>
>>      }
>>
>>      @ script:python @
>>      f << match.instance_init;
>>      p << match.pos;
>>      @@
>>      print "check %s:%s:%s in %s()" % (p[0].file, p[0].line, p[0].column, f)
>>
>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>> ---
>> Cc: Pan Nengyuan <pannengyuan@huawei.com>
>> ---
>>   hw/ipmi/ipmi_bmc_extern.c | 12 ++++++++++--
>>   1 file changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
>> index f9a13e0a44..9144ac6c38 100644
>> --- a/hw/ipmi/ipmi_bmc_extern.c
>> +++ b/hw/ipmi/ipmi_bmc_extern.c
>> @@ -463,6 +463,15 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
>>
>>       qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
>>                                chr_event, NULL, ibe, NULL, true);
>> +
>> +    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
>> +}
>> +
>> +static void ipmi_bmc_extern_unrealize(DeviceState *dev, Error **errp)
>> +{
>> +    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
>> +
>> +    timer_del(ibe->extern_timer);
>>   }
>>
>>   static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
>> @@ -502,7 +511,6 @@ static void ipmi_bmc_extern_init(Object *obj)
>>   {
>>       IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
>>
>> -    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
>>       vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
>>   }
>>
>> @@ -510,7 +518,6 @@ static void ipmi_bmc_extern_finalize(Object *obj)
>>   {
>>       IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
>>
>> -    timer_del(ibe->extern_timer);
>>       timer_free(ibe->extern_timer);
>>   }
> 
> So we now call timer_new in realize, and timer_del in unrealize,
> but timer_free in finalize. This seems unbalanced -- why not
> call timer_free in unrealize too, if we're moving things?
> 
> Also, this is a case of code that's actually doing things right:
> we free the memory that we allocated in init in finalize. So
> we're not fixing any leak here, we're just moving code around
> (which is reasonable if we're trying to standardize on "call
> timer_new in realize, not init", but should be noted in the
> commit message).

While I understand your point, I am confused because the documentation 
on unrealize() and finalize() is rather scarce (and not obvious for 
non-native english speaker). I think I'm not understanding QOM instance 
lifetime well (in particular hotplug devices) so I will let this series go.

Similar confusions:

* https://lists.gnu.org/archive/html/qemu-devel/2020-02/msg03079.html

Eduardo Habkost wrote:
 > Philippe Mathieu-Daudé wrote:
 >> IIUC when we use both init() and realize(), realize() should
 >> only contains on code that consumes the object properties...
 >> But maybe the design is not clear. Then why not move all the
 >> init() code to realize()?
 >
 > Normally I would recommend the opposite: delay as much as
 > possible to realize(), to avoid unwanted side effects when
 > (e.g.) running qom-list-properties.

* https://lists.gnu.org/archive/html/qemu-devel/2020-02/msg04143.html

David Hildenbrand wrote:
 >> Philippe Mathieu-Daudé wrote:
 >> Similarly to your other cleanups, shouldn't we move the
 >> timer_new_ns() into a realize() function, then do the
 >> timer_del() in unrealize()?
 >
 > include/hw/qdev-core.h
 >
 > "Trivial field initializations should go into
 > #TypeInfo.instance_init. Operations depending on @props
 > static properties should go into @realize."

> 
> thanks
> -- PMM
> 



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 13:48     ` Philippe Mathieu-Daudé
@ 2020-02-17 14:06       ` Peter Maydell
  2020-02-17 16:15         ` Philippe Mathieu-Daudé
  2020-02-17 19:33         ` Markus Armbruster
  0 siblings, 2 replies; 19+ messages in thread
From: Peter Maydell @ 2020-02-17 14:06 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On Mon, 17 Feb 2020 at 13:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>
> On 2/17/20 2:25 PM, Peter Maydell wrote:

> > So we now call timer_new in realize, and timer_del in unrealize,
> > but timer_free in finalize. This seems unbalanced -- why not
> > call timer_free in unrealize too, if we're moving things?
> >
> > Also, this is a case of code that's actually doing things right:
> > we free the memory that we allocated in init in finalize. So
> > we're not fixing any leak here, we're just moving code around
> > (which is reasonable if we're trying to standardize on "call
> > timer_new in realize, not init", but should be noted in the
> > commit message).
>
> While I understand your point, I am confused because the documentation
> on unrealize() and finalize() is rather scarce (and not obvious for
> non-native english speaker). I think I'm not understanding QOM instance
> lifetime well (in particular hotplug devices) so I will let this series go.

Yes, the documentation is really not good at all. The
basic structure as I understand it is that we have two-part
creation and two-part destruction:
 * instance_init is creation part 1
 * realize is creation part 2
 * unrealize is destruction part 1 and is the opposite of realize
 * instance_finalize is destruction part 2 and is the
   opposite of instance_init

(Base QOM objects only have instance_init/instance_finalize;
realize/unrealize exists only for DeviceState objects
and their children.)

ASCII-art state diagram:

  [start] --instance_init-> [inited] --realize-> [realized]
     ^                       |   ^                     |
     \---instance_finalize---/   \-----unrealize-------/

In practice the only sequences we really care about are:
 instance_init; realize; unrealize; instance_finalize
   (a full object creation-use-destruction cycle;
    even if realize fails, unrealize will be called)
 instance_init; realize
   (a subset of the above: for non-hot-pluggable devices
    we will never try to unrealize them, so this is
    as far as it goes for most devices unless they
    returned an error from their realize function)
 instance_init; instance_finalize
   (the monitor does this for introspection of an object
    without actually wanting to create and use it; it's
    also the basic lifecycle for non-DeviceState objects)

The difference between hot-pluggable and not is just
whether it's valid to try to unrealize the device.

We should definitely be clearer about what belongs in
instance_init vs what belongs in realize. But where we
have both a "do thing" and a "clean up that thing" task,
we should put the cleanup code in the operation that is
the pair of the operation we put the "do thing" code in
(i.e. do thing in instance_init, clean it up in finalize;
or do thing in realize, clean it up in unrealize).

thanks
-- PMM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 14:06       ` Peter Maydell
@ 2020-02-17 16:15         ` Philippe Mathieu-Daudé
  2020-02-17 16:32           ` Peter Maydell
  2020-02-17 19:33         ` Markus Armbruster
  1 sibling, 1 reply; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-17 16:15 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On 2/17/20 3:06 PM, Peter Maydell wrote:
> On Mon, 17 Feb 2020 at 13:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>>
>> On 2/17/20 2:25 PM, Peter Maydell wrote:
> 
>>> So we now call timer_new in realize, and timer_del in unrealize,
>>> but timer_free in finalize. This seems unbalanced -- why not
>>> call timer_free in unrealize too, if we're moving things?
>>>
>>> Also, this is a case of code that's actually doing things right:
>>> we free the memory that we allocated in init in finalize. So
>>> we're not fixing any leak here, we're just moving code around
>>> (which is reasonable if we're trying to standardize on "call
>>> timer_new in realize, not init", but should be noted in the
>>> commit message).
>>
>> While I understand your point, I am confused because the documentation
>> on unrealize() and finalize() is rather scarce (and not obvious for
>> non-native english speaker). I think I'm not understanding QOM instance
>> lifetime well (in particular hotplug devices) so I will let this series go.
> 
> Yes, the documentation is really not good at all. The
> basic structure as I understand it is that we have two-part
> creation and two-part destruction:
>   * instance_init is creation part 1
>   * realize is creation part 2
>   * unrealize is destruction part 1 and is the opposite of realize
>   * instance_finalize is destruction part 2 and is the
>     opposite of instance_init
> 
> (Base QOM objects only have instance_init/instance_finalize;
> realize/unrealize exists only for DeviceState objects
> and their children.)
> 
> ASCII-art state diagram:
> 
>    [start] --instance_init-> [inited] --realize-> [realized]
>       ^                       |   ^                     |
>       \---instance_finalize---/   \-----unrealize-------/
> 
> In practice the only sequences we really care about are:
>   instance_init; realize; unrealize; instance_finalize
>     (a full object creation-use-destruction cycle;
>      even if realize fails, unrealize will be called)
>   instance_init; realize
>     (a subset of the above: for non-hot-pluggable devices
>      we will never try to unrealize them, so this is
>      as far as it goes for most devices unless they
>      returned an error from their realize function)

Per this comment in qdev.c, unrealize() is the expected default:

     /* by default all devices were considered as hotpluggable,
      * so with intent to check it in generic qdev_unplug() /
      * device_set_realized() functions make every device
      * hotpluggable. Devices that shouldn't be hotpluggable,
      * should override it in their class_init()
      */
     dc->hotpluggable = true;

>   instance_init; instance_finalize
>     (the monitor does this for introspection of an object
>      without actually wanting to create and use it; it's
>      also the basic lifecycle for non-DeviceState objects)
> 
> The difference between hot-pluggable and not is just
> whether it's valid to try to unrealize the device.
> 
> We should definitely be clearer about what belongs in
> instance_init vs what belongs in realize. But where we
> have both a "do thing" and a "clean up that thing" task,
> we should put the cleanup code in the operation that is
> the pair of the operation we put the "do thing" code in
> (i.e. do thing in instance_init, clean it up in finalize;
> or do thing in realize, clean it up in unrealize).

With the following code snippet:

-- >8 --
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 3937d1eb1a..00d1e5c0e5 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -859,6 +859,16 @@ static void device_set_realized(Object *obj, bool 
value, Error **errp)
      bool unattached_parent = false;
      static int unattached_count;

+    if (!dc->hotpluggable && dc->unrealize) {
+        fprintf(stderr,
+                "type '%s' is not hotpluggable and implements 
unrealize()\n",
+                object_get_typename(obj));
+    }
+    if (dc->hotpluggable && !dc->unrealize) {
+        fprintf(stderr, "type '%s' is hotpluggable and misses 
unrealize()\n",
+                object_get_typename(obj));
+    }
+
      if (dev->hotplugged && !dc->hotpluggable) {
          error_setg(errp, QERR_DEVICE_NO_HOTPLUG, 
object_get_typename(obj));
          return;
diff --git a/qom/object.c b/qom/object.c
index 555c8b9d07..2d8d166cba 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -347,6 +347,16 @@ static void type_initialize(TypeImpl *ti)

              type_initialize_interface(ti, t, t);
          }
+
+        if (!type_is_ancestor(ti, type_get_by_name(TYPE_DEVICE))
+                && !type_is_ancestor(ti, type_get_by_name(TYPE_INTERFACE))
+                && !ti->instance_finalize
+                && !parent->instance_finalize
+                && !parent->abstract) {
+            fprintf(stderr,
+                    "type '%s' misses instance_finalize() [parent '%s']\n",
+                    ti->name, parent->name);
+        }
      } else {
          ti->class->properties = g_hash_table_new_full(
              g_str_hash, g_str_equal, NULL, object_property_free);

---

I get for qemu-system-aarch64:


- qdev objects missing instance_finalize():

     bcm2835-sdhost-bus
     PCIE
     pxa2xx-mmci-bus
     qtest-accel
     sdhci-bus
     tcg-accel

- non-hotpluggable devices implementing unrealize():

     VGA

- hotpluggable devices missing unrealize()

     a15mpcore_priv
     a9mpcore_priv
     a9-scu
     acpi-ged
     ads7846
     aer915
     allwinner-a10
     allwinner-a10-pic
     allwinner-A10-timer
     allwinner-ahci
     allwinner-emac
     arm11mpcore_priv
     arm11-scu
     ARM,bitband-memory
     arm.cortex-a9-global-timer
     arm_gic
     arm-gicv2m
     arm-gicv3
     arm_mptimer
     arm-smmuv3
     armsse-cpuid
     armsse-mhu
     armv7m
     armv7m_nvic
     armv7m_systick
     aspeed.fmc-ast2400
     aspeed.fmc-ast2500
     aspeed.fmc-ast2600
     aspeed.gpio-ast2400
     aspeed.gpio-ast2500
     aspeed.gpio-ast2600-1_8v
     aspeed.gpio-ast2600
     aspeed.i2c-ast2400
     aspeed.i2c-ast2500
     aspeed.i2c-ast2600
     aspeed-mmi
     aspeed.rtc
     aspeed.scu-ast2400
     aspeed.scu-ast2500
     aspeed.scu-ast2600
     aspeed.sdhci
     aspeed.sdmc-ast2400
     aspeed.sdmc-ast2500
     aspeed.sdmc-ast2600
     aspeed.spi1-ast2400
     aspeed.spi1-ast2500
     aspeed.spi1-ast2600
     aspeed.spi2-ast2500
     aspeed.spi2-ast2600
     aspeed.timer-ast2400
     aspeed.timer-ast2500
     aspeed.timer-ast2600
     aspeed.vic
     aspeed.wdt-ast2400
     aspeed.wdt-ast2500
     aspeed.wdt-ast2600
     aspeed.xdma
     ast2400-a1
     ast2500-a1
     ast2600-a0
     bcm2835-aux
     bcm2835-dma
     bcm2835-fb
     bcm2835_gpio
     bcm2835-ic
     bcm2835-mbox
     bcm2835-peripherals
     bcm2835-property
     bcm2835-rng
     bcm2835-sdhost
     bcm2835-sys-timer
     bcm2835-thermal
     bcm2836-control
     bcm2836
     bcm2837
     cadence_gem
     cadence_ttc
     cadence_uart
     cfi.pflash01
     cmsdk-apb-dualtimer
     cmsdk-apb-timer
     cmsdk-apb-uart
     cmsdk-apb-watchdog
     corgi-ssp
     cpu-cluster
     designware-pcie-host
     digic
     digic-timer
     digic-uart
     dpcd
     ds1338
     exynos4210.clk
     exynos4210.combiner
     exynos4210-ehci-usb
     exynos4210.fimd
     exynos4210.gic
     exynos4210.i2c
     exynos4210.irq_gate
     exynos4210
     exynos4210.mct
     exynos4210.pmu
     exynos4210.pwm
     exynos4210.rng
     exynos4210.rtc
     exynos4210.uart
     fsl,imx25
     fsl,imx31
     fsl,imx6
     fsl,imx6ul
     fsl,imx7
     ftgmac100
     fw_cfg_mem
     gpex-pcihost
     gpio_i2c
     gpio-key
     highbank-regs
     i2c-ddc
     icp-ctrl-regs
     imx25.ccm
     imx25.gpt
     imx2.wdt
     imx31.ccm
     imx31.gpt
     imx6.ccm
     imx6.gpt
     imx6.src
     imx6ul.ccm
     imx7.analog
     imx7.ccm
     imx7.gpr
     imx7.gpt
     imx7.snvs
     imx.avic
     imx.enet
     imx.epit
     imx.fec
     imx-gpcv2
     imx.gpio
     imx.i2c
     imx.rngc
     imx.serial
     imx.spi
     integrator_core
     integrator_debug
     integrator_pic
     integrator_pit
     iotkit
     iotkit-secctl
     iotkit-sysctl
     iotkit-sysinfo
     l2x0
     lan9118
     lm8323
     luminary-watchdog
     mainstone-fpga
     max1111
     max7310
     microbit.i2c
     mps2-fpgaio
     mps2-scc
     msf2-soc
     msf2-sysreg
     mss-spi
     mss-timer
     musicpal_gpio
     musicpal_key
     musicpal_lcd
     musicpal-misc
     mv88w8618_audio
     mv88w8618_eth
     mv88w8618_flashcfg
     mv88w8618_pic
     mv88w8618_pit
     mv88w8618_wlan
     mx25l25635e
     mx66l1g45g
     mx66u51235f
     n25q128
     n25q256a
     n25q512a11
     nand
     nrf51_soc.gpio
     nrf51-soc
     nrf51_soc.nvm
     nrf51_soc.rng
     nrf51_soc.timer
     nrf51_soc.uart
     omap2-gpio
     omap2-intc
     omap-gpio
     omap_i2c
     omap-intc
     onenand
     or-irq
     pca9552
     pl011
     pl011_luminary
     pl022
     pl031
     pl041
     pl050_keyboard
     pl050_mouse
     pl061
     pl061_luminary
     pl080
     pl081
     pl110
     pl110_versatile
     pl111
     pl181
     pl190
     pl330
     platform-bus-device
     platform-ehci-usb
     pxa25x-timer
     pxa27x-timer
     pxa2xx-dma
     pxa2xx-gpio
     pxa2xx_i2c
     pxa2xx-i2c-slave
     pxa2xx-pcmcia
     pxa2xx_pic
     pxa2xx_rtc
     pxa2xx-ssp
     realview_gic
     realview_mpcore
     realview_pci
     realview_sysctl
     s25sl12801
     scoop
     sd-card
     serial-mm
     sii9022
     sl-nand
     smbus-eeprom
     smc91c111
     sp804
     spitz-keyboard
     spitz-lcdtg
     split-irq
     ssd0303
     ssd0323
     sse-200
     ssi-sd
     sst25vf016b
     sst25wf080
     stellaris-adc
     stellaris_enet
     stellaris-gptm
     stellaris-i2c
     stm32f205-soc
     stm32f2xx-adc
     stm32f2xx-spi
     stm32f2xx-syscfg
     stm32f2xx-timer
     stm32f2xx-usart
     stm32f405-soc
     stm32f4xx-exti
     stm32f4xx-syscfg
     strongarm-gpio
     strongarm_pic
     strongarm-ppc
     strongarm-rtc
     strongarm-ssp
     strongarm-uart
     sysbus-ahci
     sysbus-ohci
     tmp105
     tmp423
     tosa_dac
     tosa-ssp
     twl92230
     tz-mpc
     tz-msc
     tz-ppc
     unimplemented-device
     usb-chipidea
     versatile_i2c
     versatilepb_sic
     versatile_pci
     virtio-mmio
     w25q256
     w25q512jv
     wm8750
     xgmac
     xilinx,zynq_slcr
     xlnx.dpdma
     xlnx.ps7-dev-cfg
     xlnx.ps7-qspi
     xlnx.ps7-spi
     xlnx,ps7-usb
     xlnx.usmp-gqspi
     xlnx.v-dp
     xlnx-versal
     xlnx.zdma
     xlnx-zynmp.rtc
     xlnx.zynqmp_ipi
     xlnx,zynqmp
     xlnx,zynq-xadc
     zipit-lcd

Most of these are sysbus devices. The list is big, I probably have 
something wrong.



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 16:15         ` Philippe Mathieu-Daudé
@ 2020-02-17 16:32           ` Peter Maydell
  2020-02-17 17:12             ` Philippe Mathieu-Daudé
  2020-02-18  9:21             ` Markus Armbruster
  0 siblings, 2 replies; 19+ messages in thread
From: Peter Maydell @ 2020-02-17 16:32 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On Mon, 17 Feb 2020 at 16:15, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
> Per this comment in qdev.c, unrealize() is the expected default:
>
>      /* by default all devices were considered as hotpluggable,
>       * so with intent to check it in generic qdev_unplug() /
>       * device_set_realized() functions make every device
>       * hotpluggable. Devices that shouldn't be hotpluggable,
>       * should override it in their class_init()
>       */
>      dc->hotpluggable = true;

This comment sounds like it's documenting what was the
previous default ("were considered") and making a minimal
change to avoid breaking existing code where a device
does want to be hotpluggable but isn't explicitly saying so.
I forget how exactly it works (the mechanism has changed
several times) but in practice a sysbus device is generally
not hotpluggable, and that's what most devices are.

> I get for qemu-system-aarch64:
>
>
> - qdev objects missing instance_finalize():
>
>      bcm2835-sdhost-bus
>      PCIE
>      pxa2xx-mmci-bus
>      qtest-accel
>      sdhci-bus
>      tcg-accel

Note that you don't need an instance_finalize() if it
would have nothing to do, which may be the case here.

> - non-hotpluggable devices implementing unrealize():
>
>      VGA

Not sure which device this is, I couldn't find a TYPE_
define with this name. Is it an abstract or common
device type used by the hotpluggable pci VGA card?

> - hotpluggable devices missing unrealize()
>
>      a15mpcore_priv
>      a9mpcore_priv

Most of these are not really hotpluggable. What is
confusing your test code is that sysbus devices get
the default 'hotpluggable = true' setting, but other
conditions usually prevent hotplugging. (The reason
hotpluggable is true is the default is because of
handling of 'dynamic sysbus' devices which live on
the 'platform bus'.) In particular, I think that
anything with !dc->user_creatable can't be hotplugged
unless board code specifically tries it, which would
be a bug for most of these devices.

Also, if there isn't anything for a device's unrealize
method to do, it doesn't need to provide one.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 16:32           ` Peter Maydell
@ 2020-02-17 17:12             ` Philippe Mathieu-Daudé
  2020-02-17 17:14               ` Peter Maydell
  2020-02-17 17:19               ` Philippe Mathieu-Daudé
  2020-02-18  9:21             ` Markus Armbruster
  1 sibling, 2 replies; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-17 17:12 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On 2/17/20 5:32 PM, Peter Maydell wrote:
> On Mon, 17 Feb 2020 at 16:15, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>> Per this comment in qdev.c, unrealize() is the expected default:
>>
>>       /* by default all devices were considered as hotpluggable,
>>        * so with intent to check it in generic qdev_unplug() /
>>        * device_set_realized() functions make every device
>>        * hotpluggable. Devices that shouldn't be hotpluggable,
>>        * should override it in their class_init()
>>        */
>>       dc->hotpluggable = true;
> 
> This comment sounds like it's documenting what was the
> previous default ("were considered") and making a minimal
> change to avoid breaking existing code where a device
> does want to be hotpluggable but isn't explicitly saying so.
> I forget how exactly it works (the mechanism has changed
> several times) but in practice a sysbus device is generally
> not hotpluggable, and that's what most devices are.
> 
>> I get for qemu-system-aarch64:
>>
>>
>> - qdev objects missing instance_finalize():
>>
>>       bcm2835-sdhost-bus
>>       PCIE
>>       pxa2xx-mmci-bus
>>       qtest-accel
>>       sdhci-bus
>>       tcg-accel
> 
> Note that you don't need an instance_finalize() if it
> would have nothing to do, which may be the case here.
> 
>> - non-hotpluggable devices implementing unrealize():
>>
>>       VGA
> 
> Not sure which device this is, I couldn't find a TYPE_
> define with this name. Is it an abstract or common
> device type used by the hotpluggable pci VGA card?

This is TYPE_PCI_VGA (hw/display/vga-pci.c).

> 
>> - hotpluggable devices missing unrealize()
>>
>>       a15mpcore_priv
>>       a9mpcore_priv
> 
> Most of these are not really hotpluggable. What is
> confusing your test code is that sysbus devices get
> the default 'hotpluggable = true' setting, but other
> conditions usually prevent hotplugging. (The reason
> hotpluggable is true is the default is because of
> handling of 'dynamic sysbus' devices which live on
> the 'platform bus'.) In particular, I think that
> anything with !dc->user_creatable can't be hotplugged
> unless board code specifically tries it, which would
> be a bug for most of these devices.

OK, checking '!dc->user_creatable' removes:

     ads7846
     aer915
     corgi-ssp
     dpcd
     ds1338
     i2c-ddc
     lm8323
     max1111
     max7310
     mx25l25635e
     mx66l1g45g
     mx66u51235f
     n25q128
     n25q256a
     n25q512a11
     nand
     pca9552
     pxa2xx-i2c-slave
     s25sl12801
     sd-card
     sii9022
     spitz-lcdtg
     ssd0303
     ssd0323
     sst25vf016b
     sst25wf080
     tmp105
     tmp423
     tosa_dac
     tosa-ssp
     twl92230
     w25q256
     w25q512jv
     wm8750
     zipit-lcd

Previous list only reduced from 300 to 265.

I noticed this function, maybe I need to check parent_bus too:

static bool device_get_hotpluggable(Object *obj, Error **errp)
{
     DeviceClass *dc = DEVICE_GET_CLASS(obj);
     DeviceState *dev = DEVICE(obj);

     return dc->hotpluggable && (dev->parent_bus == NULL ||
                                 qbus_is_hotpluggable(dev->parent_bus));
}

> 
> Also, if there isn't anything for a device's unrealize
> method to do, it doesn't need to provide one.

This point is hard to check with simply with qtest.

Thanks for your comments, it helped sorting few things out.

> 
> thanks
> -- PMM
> 



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 17:12             ` Philippe Mathieu-Daudé
@ 2020-02-17 17:14               ` Peter Maydell
  2020-02-17 17:19               ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 19+ messages in thread
From: Peter Maydell @ 2020-02-17 17:14 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On Mon, 17 Feb 2020 at 17:12, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
> OK, checking '!dc->user_creatable' removes:
[...]
> Previous list only reduced from 300 to 265.

You could look at what condition we check for that causes this:

$ ./build/x86/arm-softmmu/qemu-system-arm -M virt -device a15mpcore_priv
qemu-system-arm: -device a15mpcore_priv: Parameter 'driver' expects
pluggable device type

which will probably let you rule out some more devices.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 17:12             ` Philippe Mathieu-Daudé
  2020-02-17 17:14               ` Peter Maydell
@ 2020-02-17 17:19               ` Philippe Mathieu-Daudé
  2020-02-17 17:27                 ` Peter Maydell
  1 sibling, 1 reply; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-02-17 17:19 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On 2/17/20 6:12 PM, Philippe Mathieu-Daudé wrote:
> On 2/17/20 5:32 PM, Peter Maydell wrote:
>> On Mon, 17 Feb 2020 at 16:15, Philippe Mathieu-Daudé 
[...]
>>> - hotpluggable devices missing unrealize()
>>>
>>>       a15mpcore_priv
>>>       a9mpcore_priv
>>
>> Most of these are not really hotpluggable. What is
>> confusing your test code is that sysbus devices get
>> the default 'hotpluggable = true' setting, but other
>> conditions usually prevent hotplugging. (The reason
>> hotpluggable is true is the default is because of
>> handling of 'dynamic sysbus' devices which live on
>> the 'platform bus'.) In particular, I think that
>> anything with !dc->user_creatable can't be hotplugged
>> unless board code specifically tries it, which would
>> be a bug for most of these devices.
[...]
>> Also, if there isn't anything for a device's unrealize
>> method to do, it doesn't need to provide one.
> 
> This point is hard to check with simply with qtest.
> 
> Thanks for your comments, it helped sorting few things out.

Quick check with TYPE_BITBAND which is a SysBus device, we have:

static void bitband_realize(DeviceState *dev, Error **errp)
{
     BitBandState *s = BITBAND(dev);

     if (!s->source_memory) {
         error_setg(errp, "source-memory property not set");
         return;
     }

     address_space_init(&s->source_as, s->source_memory, "bitband-source");
}

Do we need the equivalent:

static void bitband_unrealize(DeviceState *dev, Error **errp)
{
     BitBandState *s = BITBAND(dev);

     address_space_destroy(&s->source_as);
}

Or instead mark the device user_creatable=false because of the link to a 
TYPE_MEMORY_REGION?

> 
>>
>> thanks
>> -- PMM
>>



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 17:19               ` Philippe Mathieu-Daudé
@ 2020-02-17 17:27                 ` Peter Maydell
  2020-02-18  9:29                   ` Markus Armbruster
  0 siblings, 1 reply; 19+ messages in thread
From: Peter Maydell @ 2020-02-17 17:27 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster

On Mon, 17 Feb 2020 at 17:20, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
> Quick check with TYPE_BITBAND which is a SysBus device, we have:
>
> static void bitband_realize(DeviceState *dev, Error **errp)
> {
>      BitBandState *s = BITBAND(dev);
>
>      if (!s->source_memory) {
>          error_setg(errp, "source-memory property not set");
>          return;
>      }
>
>      address_space_init(&s->source_as, s->source_memory, "bitband-source");
> }
>
> Do we need the equivalent:
>
> static void bitband_unrealize(DeviceState *dev, Error **errp)
> {
>      BitBandState *s = BITBAND(dev);
>
>      address_space_destroy(&s->source_as);
> }
>
> Or instead mark the device user_creatable=false because of the link to a
> TYPE_MEMORY_REGION?

I don't believe that this device is user-creatable. The
base class sysbus_device_class_init() sets user_creatable
to false by default for all sysbus devices, and a sysbus
device which wants to opt into being user-created has to
set it to true.

Also the device's type name string is "ARM,bitband-memory"
and the -device option at least does not like the comma
in the middle of the name, so I don't know how you'd
create it on the command line even if it wasn't marked
not user-creatable.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 14:06       ` Peter Maydell
  2020-02-17 16:15         ` Philippe Mathieu-Daudé
@ 2020-02-17 19:33         ` Markus Armbruster
  1 sibling, 0 replies; 19+ messages in thread
From: Markus Armbruster @ 2020-02-17 19:33 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Markus Armbruster, Philippe Mathieu-Daudé

Peter Maydell <peter.maydell@linaro.org> writes:

> On Mon, 17 Feb 2020 at 13:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>>
>> On 2/17/20 2:25 PM, Peter Maydell wrote:
>
>> > So we now call timer_new in realize, and timer_del in unrealize,
>> > but timer_free in finalize. This seems unbalanced -- why not
>> > call timer_free in unrealize too, if we're moving things?
>> >
>> > Also, this is a case of code that's actually doing things right:
>> > we free the memory that we allocated in init in finalize. So
>> > we're not fixing any leak here, we're just moving code around
>> > (which is reasonable if we're trying to standardize on "call
>> > timer_new in realize, not init", but should be noted in the
>> > commit message).
>>
>> While I understand your point, I am confused because the documentation
>> on unrealize() and finalize() is rather scarce (and not obvious for
>> non-native english speaker). I think I'm not understanding QOM instance
>> lifetime well (in particular hotplug devices) so I will let this series go.
>
> Yes, the documentation is really not good at all. The
> basic structure as I understand it is that we have two-part
> creation and two-part destruction:
>  * instance_init is creation part 1
>  * realize is creation part 2
>  * unrealize is destruction part 1 and is the opposite of realize
>  * instance_finalize is destruction part 2 and is the
>    opposite of instance_init
>
> (Base QOM objects only have instance_init/instance_finalize;
> realize/unrealize exists only for DeviceState objects
> and their children.)

The split exists so you can set property values between instance_init()
and realize().  It's how qdev has always worked.  It permits setting
properties one by one even when this results in intermediate states
where invariants involving multiple property values are violated: delay
checking them until realize(), rely on them only while the device is
realized.

Note that both realize() and unrealize() can fail.  instance_init() and
instance_finalize() can't.

> ASCII-art state diagram:
>
>   [start] --instance_init-> [inited] --realize-> [realized]
>      ^                       |   ^                     |
>      \---instance_finalize---/   \-----unrealize-------/
>
> In practice the only sequences we really care about are:
>  instance_init; realize; unrealize; instance_finalize
>    (a full object creation-use-destruction cycle;
>     even if realize fails, unrealize will be called)
>  instance_init; realize
>    (a subset of the above: for non-hot-pluggable devices
>     we will never try to unrealize them, so this is
>     as far as it goes for most devices unless they
>     returned an error from their realize function)
>  instance_init; instance_finalize
>    (the monitor does this for introspection of an object
>     without actually wanting to create and use it; it's
>     also the basic lifecycle for non-DeviceState objects)

In theory, you can realize + unrealize multiple times.  It might even
work in practice sometimes.

> The difference between hot-pluggable and not is just
> whether it's valid to try to unrealize the device.
>
> We should definitely be clearer about what belongs in
> instance_init vs what belongs in realize. But where we
> have both a "do thing" and a "clean up that thing" task,
> we should put the cleanup code in the operation that is
> the pair of the operation we put the "do thing" code in
> (i.e. do thing in instance_init, clean it up in finalize;
> or do thing in realize, clean it up in unrealize).

Not doing so risks introspection leaks or double-frees.



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 16:32           ` Peter Maydell
  2020-02-17 17:12             ` Philippe Mathieu-Daudé
@ 2020-02-18  9:21             ` Markus Armbruster
  1 sibling, 0 replies; 19+ messages in thread
From: Markus Armbruster @ 2020-02-18  9:21 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Philippe Mathieu-Daudé

Peter Maydell <peter.maydell@linaro.org> writes:

> On Mon, 17 Feb 2020 at 16:15, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>> Per this comment in qdev.c, unrealize() is the expected default:
>>
>>      /* by default all devices were considered as hotpluggable,
>>       * so with intent to check it in generic qdev_unplug() /
>>       * device_set_realized() functions make every device
>>       * hotpluggable. Devices that shouldn't be hotpluggable,
>>       * should override it in their class_init()
>>       */
>>      dc->hotpluggable = true;

Please note that "hot-pluggable" is *not* required for "need unrealize()
to work", at least in theory.  Cold plug exists, and cold unplug could
exist.

> This comment sounds like it's documenting what was the
> previous default ("were considered") and making a minimal
> change to avoid breaking existing code where a device
> does want to be hotpluggable but isn't explicitly saying so.

Commit 1a37eca107, six years ago:

    qdev: add "hotpluggable" property to Device
    
    Currently it's possible to make PCIDevice not hotpluggable
    by using no_hotplug field of PCIDeviceClass. However it
    limits this only to PCI devices and prevents from
    generalizing hotplug code.
    
    So add similar field to DeviceClass so it could be reused
    with other Devices and would allow to replace PCI specific
    hotplug callbacks with generic implementation. Following
    patches will replace PCIDeviceClass.no_hotplug with this
    new property.
    
    In addition expose field as "hotpluggable" readonly property,
    to make it possible to read its value via QOM interface.
    
    Make DeviceClass hotpluggable by default as it was assumed
    before.

> I forget how exactly it works (the mechanism has changed
> several times) but in practice a sysbus device is generally
> not hotpluggable, and that's what most devices are.

A device's "hot-pluggability" comes into play only when both bus and
machine support hot-plug.  And before we even get there, the device
needs to be "pluggable", i.e. dc->user_creatable.

Bus types supporting hot plug include PCI, SCSI, USB, virtio-serial-bus.
Grep for qbus_set_hotplug_handler().



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 17:27                 ` Peter Maydell
@ 2020-02-18  9:29                   ` Markus Armbruster
  0 siblings, 0 replies; 19+ messages in thread
From: Markus Armbruster @ 2020-02-18  9:29 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Eduardo Habkost, Corey Minyard, David Hildenbrand, Pan Nengyuan,
	QEMU Developers, Philippe Mathieu-Daudé

Peter Maydell <peter.maydell@linaro.org> writes:

> On Mon, 17 Feb 2020 at 17:20, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>> Quick check with TYPE_BITBAND which is a SysBus device, we have:
>>
>> static void bitband_realize(DeviceState *dev, Error **errp)
>> {
>>      BitBandState *s = BITBAND(dev);
>>
>>      if (!s->source_memory) {
>>          error_setg(errp, "source-memory property not set");
>>          return;
>>      }
>>
>>      address_space_init(&s->source_as, s->source_memory, "bitband-source");
>> }
>>
>> Do we need the equivalent:
>>
>> static void bitband_unrealize(DeviceState *dev, Error **errp)
>> {
>>      BitBandState *s = BITBAND(dev);
>>
>>      address_space_destroy(&s->source_as);
>> }
>>
>> Or instead mark the device user_creatable=false because of the link to a
>> TYPE_MEMORY_REGION?
>
> I don't believe that this device is user-creatable. The
> base class sysbus_device_class_init() sets user_creatable
> to false by default for all sysbus devices, and a sysbus
> device which wants to opt into being user-created has to
> set it to true.

As far as I can tell, you additionally have to
machine_class_allow_dynamic_sysbus_dev().  Sysbus is special.

> Also the device's type name string is "ARM,bitband-memory"
> and the -device option at least does not like the comma
> in the middle of the name, so I don't know how you'd
> create it on the command line even if it wasn't marked
> not user-creatable.

Double the comma.

If I remember correctly, the use of comma in type comes from IEEE-1275.
It's quite inappropriate for QEMU.



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 2/2] hw/sd/sd: Delay timer_new_ns() from init to realize to avoid memleaks
  2020-02-17 13:26   ` Peter Maydell
@ 2020-06-05  5:05     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 19+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-06-05  5:05 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Pan Nengyuan, QEMU Developers, Corey Minyard

On 2/17/20 2:26 PM, Peter Maydell wrote:
> On Sat, 15 Feb 2020 at 15:48, Philippe Mathieu-Daudé <philmd@redhat.com> wrote:
>>
>> In commit f3a508eb4e the Euler Robot reported calling timer_new()
>> in instance_init() can leak heap memory. The easier fix is to
>> delay the timer creation at instance realize(). Similarly move
>> timer_del() into a new instance unrealize() method.
> 
>> diff --git a/hw/sd/sd.c b/hw/sd/sd.c
>> index 71a9af09ab..d72cf3de2a 100644
>> --- a/hw/sd/sd.c
>> +++ b/hw/sd/sd.c
>> @@ -2058,14 +2058,12 @@ static void sd_instance_init(Object *obj)
>>      SDState *sd = SD_CARD(obj);
>>
>>      sd->enable = true;
>> -    sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
>>  }
>>
>>  static void sd_instance_finalize(Object *obj)
>>  {
>>      SDState *sd = SD_CARD(obj);
>>
>> -    timer_del(sd->ocr_power_timer);
>>      timer_free(sd->ocr_power_timer);
>>  }
>>
>> @@ -2098,6 +2096,15 @@ static void sd_realize(DeviceState *dev, Error **errp)
>>          }
>>          blk_set_dev_ops(sd->blk, &sd_block_ops, sd);
>>      }
>> +
>> +    sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd);
>> +}
>> +
>> +static void sd_unrealize(DeviceState *dev, Error **errp)
>> +{
>> +    SDState *sd = SD_CARD(dev);
>> +
>> +    timer_del(sd->ocr_power_timer);
>>  }
> 
> Here too the old code was doing things correctly in that
> it does a timer_del/timer_free on the timer it allocates
> in instance_init, and the new code has weirdly split the
> freeing between unrealize and finalize.

Indeed I now see it, thanks.

> 
> thanks
> -- PMM
> 



^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2020-06-05  5:06 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-15 15:47 [PATCH 0/2] hw: Delay timer_new() from init to realize to avoid memleaks Philippe Mathieu-Daudé
2020-02-15 15:47 ` [PATCH 1/2] hw/ipmi/bmc: Delay timer_new_ns() " Philippe Mathieu-Daudé
2020-02-16 19:43   ` Corey Minyard
2020-02-17 13:25   ` Peter Maydell
2020-02-17 13:48     ` Philippe Mathieu-Daudé
2020-02-17 14:06       ` Peter Maydell
2020-02-17 16:15         ` Philippe Mathieu-Daudé
2020-02-17 16:32           ` Peter Maydell
2020-02-17 17:12             ` Philippe Mathieu-Daudé
2020-02-17 17:14               ` Peter Maydell
2020-02-17 17:19               ` Philippe Mathieu-Daudé
2020-02-17 17:27                 ` Peter Maydell
2020-02-18  9:29                   ` Markus Armbruster
2020-02-18  9:21             ` Markus Armbruster
2020-02-17 19:33         ` Markus Armbruster
2020-02-15 15:47 ` [PATCH 2/2] hw/sd/sd: " Philippe Mathieu-Daudé
2020-02-17 13:26   ` Peter Maydell
2020-06-05  5:05     ` Philippe Mathieu-Daudé
2020-02-16  2:10 ` [PATCH 0/2] hw: Delay timer_new() " Richard Henderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).