diff mbox

qdev: Skip non-existing properties when setting globals

Message ID 20140606201429.GK15000@otherpad.lan.raisama.net
State New
Headers show

Commit Message

Eduardo Habkost June 6, 2014, 8:14 p.m. UTC
This avoids QEMU from aborting on cases like this:

  $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
  qemu-system-x86_64: Property '.foobar' not found
  Aborted (core dumped)

The code sets dev->not_used if the property is not found as an effort to
to allow errors to be reported even if the device is hotpluggable, but
it won't catch all errors. We can't know the property is not going to be
available for hotpluggable devices, unless we actually try to create the
device.

Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
---
 hw/core/qdev-properties.c      | 10 +++++++++-
 tests/test-qdev-global-props.c | 14 ++++++++++++--
 2 files changed, 21 insertions(+), 3 deletions(-)

Comments

Don Slutz June 6, 2014, 9:06 p.m. UTC | #1
On 06/06/14 16:14, Eduardo Habkost wrote:
> This avoids QEMU from aborting on cases like this:
>
>    $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
>    qemu-system-x86_64: Property '.foobar' not found
>    Aborted (core dumped)
>
> The code sets dev->not_used if the property is not found as an effort to
> to allow errors to be reported even if the device is hotpluggable, but
> it won't catch all errors. We can't know the property is not going to be
> available for hotpluggable devices, unless we actually try to create the
> device.
>
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
>   hw/core/qdev-properties.c      | 10 +++++++++-
>   tests/test-qdev-global-props.c | 14 ++++++++++++--
>   2 files changed, 21 insertions(+), 3 deletions(-)
>
> diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
> index 3d12560..8cd7c2a 100644
> --- a/hw/core/qdev-properties.c
> +++ b/hw/core/qdev-properties.c
> @@ -976,6 +976,7 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
>                                       Error **errp)
>   {
>       GlobalProperty *prop;
> +    Object *obj = OBJECT(dev);
>   
>       QTAILQ_FOREACH(prop, &global_props, next) {
>           Error *err = NULL;
> @@ -983,8 +984,15 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
>           if (strcmp(typename, prop->driver) != 0) {
>               continue;
>           }
> +        if (!object_property_find(obj, prop->property, &err)) {
> +            /* not_used doesn't default to true for hotpluggable devices,
> +             * but in this case we know the property wasn't used when it could.
> +             */
> +            prop->not_used = true;
> +            continue;
> +        }
>           prop->not_used = false;
> -        object_property_parse(OBJECT(dev), prop->value, prop->property, &err);
> +        object_property_parse(obj, prop->value, prop->property, &err);
>           if (err != NULL) {
>               error_propagate(errp, err);
>               return;
> diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
> index 2bef04c..1ccc3e5 100644
> --- a/tests/test-qdev-global-props.c
> +++ b/tests/test-qdev-global-props.c
> @@ -148,8 +148,9 @@ static void test_dynamic_globalprop(void)
>   {
>       MyType *mt;
>       static GlobalProperty props[] = {
> -        { TYPE_DYNAMIC_PROPS, "prop1", "101" },
> -        { TYPE_DYNAMIC_PROPS, "prop2", "102" },
> +        { TYPE_DYNAMIC_PROPS, "prop1", "101", true },
> +        { TYPE_DYNAMIC_PROPS, "prop2", "102", true },
> +        { TYPE_DYNAMIC_PROPS, "prop3", "103", true },

I think the test would be better if this started out as false.

     -Don Slutz

>           { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true },
>           {}
>       };
> @@ -164,6 +165,15 @@ static void test_dynamic_globalprop(void)
>       g_assert_cmpuint(mt->prop2, ==, 102);
>       all_used = qdev_prop_check_global();
>       g_assert_cmpuint(all_used, ==, 1);
> +
> +    /* prop1 */
> +    g_assert(!props[0].not_used);
> +    /* prop2 */
> +    g_assert(!props[1].not_used);
> +    /* TYPE_DYNAMIC_PROPS.prop3: non-existing property */
> +    g_assert(props[2].not_used);
> +    /* TYPE_DYNAMIC_PROPS-bad.prop3: non-existing class */
> +    g_assert(props[3].not_used);
>   }
>   
>   int main(int argc, char **argv)
Igor Mammedov June 6, 2014, 9:38 p.m. UTC | #2
On Fri, 6 Jun 2014 17:14:29 -0300
Eduardo Habkost <ehabkost@redhat.com> wrote:

> This avoids QEMU from aborting on cases like this:
> 
>   $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
>   qemu-system-x86_64: Property '.foobar' not found
>   Aborted (core dumped)
That is expected behavior.

> 
> The code sets dev->not_used if the property is not found as an effort to
> to allow errors to be reported even if the device is hotpluggable, but
> it won't catch all errors. We can't know the property is not going to be
> available for hotpluggable devices, unless we actually try to create the
> device.
Instead of ignoring users errors, DeviceState should have async_error
field which could be set by device_post_init() instead of aborting and
later device_add could gracefully fail hotadd operation if error is set.

PS:
initfn-s could also reuse this, instead of ignoring errors as they do now.

> 
> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
> ---
>  hw/core/qdev-properties.c      | 10 +++++++++-
>  tests/test-qdev-global-props.c | 14 ++++++++++++--
>  2 files changed, 21 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
> index 3d12560..8cd7c2a 100644
> --- a/hw/core/qdev-properties.c
> +++ b/hw/core/qdev-properties.c
> @@ -976,6 +976,7 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
>                                      Error **errp)
>  {
>      GlobalProperty *prop;
> +    Object *obj = OBJECT(dev);
>  
>      QTAILQ_FOREACH(prop, &global_props, next) {
>          Error *err = NULL;
> @@ -983,8 +984,15 @@ void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
>          if (strcmp(typename, prop->driver) != 0) {
>              continue;
>          }
> +        if (!object_property_find(obj, prop->property, &err)) {
> +            /* not_used doesn't default to true for hotpluggable devices,
> +             * but in this case we know the property wasn't used when it could.
> +             */
> +            prop->not_used = true;
> +            continue;
> +        }
>          prop->not_used = false;
> -        object_property_parse(OBJECT(dev), prop->value, prop->property, &err);
> +        object_property_parse(obj, prop->value, prop->property, &err);
>          if (err != NULL) {
>              error_propagate(errp, err);
>              return;
> diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
> index 2bef04c..1ccc3e5 100644
> --- a/tests/test-qdev-global-props.c
> +++ b/tests/test-qdev-global-props.c
> @@ -148,8 +148,9 @@ static void test_dynamic_globalprop(void)
>  {
>      MyType *mt;
>      static GlobalProperty props[] = {
> -        { TYPE_DYNAMIC_PROPS, "prop1", "101" },
> -        { TYPE_DYNAMIC_PROPS, "prop2", "102" },
> +        { TYPE_DYNAMIC_PROPS, "prop1", "101", true },
> +        { TYPE_DYNAMIC_PROPS, "prop2", "102", true },
> +        { TYPE_DYNAMIC_PROPS, "prop3", "103", true },
>          { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true },
>          {}
>      };
> @@ -164,6 +165,15 @@ static void test_dynamic_globalprop(void)
>      g_assert_cmpuint(mt->prop2, ==, 102);
>      all_used = qdev_prop_check_global();
>      g_assert_cmpuint(all_used, ==, 1);
> +
> +    /* prop1 */
> +    g_assert(!props[0].not_used);
> +    /* prop2 */
> +    g_assert(!props[1].not_used);
> +    /* TYPE_DYNAMIC_PROPS.prop3: non-existing property */
> +    g_assert(props[2].not_used);
> +    /* TYPE_DYNAMIC_PROPS-bad.prop3: non-existing class */
> +    g_assert(props[3].not_used);
>  }
>  
>  int main(int argc, char **argv)
> -- 
> 1.9.0
> 
>
Eduardo Habkost June 6, 2014, 10:21 p.m. UTC | #3
On Fri, Jun 06, 2014 at 11:38:58PM +0200, Igor Mammedov wrote:
> On Fri, 6 Jun 2014 17:14:29 -0300
> Eduardo Habkost <ehabkost@redhat.com> wrote:
> 
> > This avoids QEMU from aborting on cases like this:
> > 
> >   $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
> >   qemu-system-x86_64: Property '.foobar' not found
> >   Aborted (core dumped)
> That is expected behavior.

Why?

QEMU should never dump core due to user error.

QEMU should not abort when handling a device_add command due to user
error.

> 
> > 
> > The code sets dev->not_used if the property is not found as an effort to
> > to allow errors to be reported even if the device is hotpluggable, but
> > it won't catch all errors. We can't know the property is not going to be
> > available for hotpluggable devices, unless we actually try to create the
> > device.
> Instead of ignoring users errors, DeviceState should have async_error
> field which could be set by device_post_init() instead of aborting and
> later device_add could gracefully fail hotadd operation if error is set.
> 
> PS:
> initfn-s could also reuse this, instead of ignoring errors as they do now.

Your proposal sounds good, and would allow reporting error without
creating an object_new() variation that accepts Error**.

But I believe we need to choose what to do in the meantime, while we
don't have that mechanism implemented. Dumping core is not acceptable.
Exiting QEMU while handling device_add doesn't seem acceptable to me,
either.
Igor Mammedov June 6, 2014, 11:22 p.m. UTC | #4
On Fri, 6 Jun 2014 19:21:36 -0300
Eduardo Habkost <ehabkost@redhat.com> wrote:

> On Fri, Jun 06, 2014 at 11:38:58PM +0200, Igor Mammedov wrote:
> > On Fri, 6 Jun 2014 17:14:29 -0300
> > Eduardo Habkost <ehabkost@redhat.com> wrote:
> > 
> > > This avoids QEMU from aborting on cases like this:
> > > 
> > >   $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
> > >   qemu-system-x86_64: Property '.foobar' not found
> > >   Aborted (core dumped)
> > That is expected behavior.
> 
> Why?
> 
> QEMU should never dump core due to user error.
> 
> QEMU should not abort when handling a device_add command due to user
> error.
I've meant QEMU shouldn't start if CLI has error. whether it's abort or
exit(FAIL) doesn't matter much.

> 
> > 
> > > 
> > > The code sets dev->not_used if the property is not found as an effort to
> > > to allow errors to be reported even if the device is hotpluggable, but
> > > it won't catch all errors. We can't know the property is not going to be
> > > available for hotpluggable devices, unless we actually try to create the
> > > device.
> > Instead of ignoring users errors, DeviceState should have async_error
> > field which could be set by device_post_init() instead of aborting and
> > later device_add could gracefully fail hotadd operation if error is set.
> > 
> > PS:
> > initfn-s could also reuse this, instead of ignoring errors as they do now.
> 
> Your proposal sounds good, and would allow reporting error without
> creating an object_new() variation that accepts Error**.
> 
> But I believe we need to choose what to do in the meantime, while we
> don't have that mechanism implemented. Dumping core is not acceptable.
> Exiting QEMU while handling device_add doesn't seem acceptable to me,
> either.
Exiting at startup is fine and allows to filter out user errors early.

During hotplug exiting is certainly not an option, that's why I'm
suggesting add async_error so that hotplug operation could fail gracefully.
It's a cleaner approach and not much more complex.

Ignoring errors on the other side would introduce relaxed CLI interface that
we would have to support forever for compatibility reasons.

> 
> -- 
> Eduardo
Peter Maydell June 6, 2014, 11:45 p.m. UTC | #5
On 7 June 2014 00:22, Igor Mammedov <imammedo@redhat.com> wrote:
> Eduardo Habkost <ehabkost@redhat.com> wrote:
>> On Fri, Jun 06, 2014 at 11:38:58PM +0200, Igor Mammedov wrote:
>> > Eduardo Habkost <ehabkost@redhat.com> wrote:
>> > >   $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
>> > >   qemu-system-x86_64: Property '.foobar' not found
>> > >   Aborted (core dumped)
>> > That is expected behavior.
>>
>> Why?
>>
>> QEMU should never dump core due to user error.
>>
>> QEMU should not abort when handling a device_add command due to user
>> error.
> I've meant QEMU shouldn't start if CLI has error. whether it's abort or
> exit(FAIL) doesn't matter much.

I'm with Eduardo on this one -- if the user passes us a bad
command line we should diagnose it helpfully and exit with
a failure code; abort() is for programming errors. (If nothing
else, using abort() for user-triggerable conditions tends to
mean we get bug reports about core dumps, so it's in our
own interest to not do that :-) )

thanks
-- PMM
Eduardo Habkost June 7, 2014, 1:09 a.m. UTC | #6
On Sat, Jun 07, 2014 at 12:45:26AM +0100, Peter Maydell wrote:
> On 7 June 2014 00:22, Igor Mammedov <imammedo@redhat.com> wrote:
> > Eduardo Habkost <ehabkost@redhat.com> wrote:
> >> On Fri, Jun 06, 2014 at 11:38:58PM +0200, Igor Mammedov wrote:
> >> > Eduardo Habkost <ehabkost@redhat.com> wrote:
> >> > >   $ ./install/bin/qemu-system-x86_64 -global cpu.foobar=5
> >> > >   qemu-system-x86_64: Property '.foobar' not found
> >> > >   Aborted (core dumped)
> >> > That is expected behavior.
> >>
> >> Why?
> >>
> >> QEMU should never dump core due to user error.
> >>
> >> QEMU should not abort when handling a device_add command due to user
> >> error.
> > I've meant QEMU shouldn't start if CLI has error. whether it's abort or
> > exit(FAIL) doesn't matter much.
> 
> I'm with Eduardo on this one -- if the user passes us a bad
> command line we should diagnose it helpfully and exit with
> a failure code; abort() is for programming errors. (If nothing
> else, using abort() for user-triggerable conditions tends to
> mean we get bug reports about core dumps, so it's in our
> own interest to not do that :-) )

So you are agreeing with Igor, as well. He is just suggesting we keep
terminating QEMU on that case, and we can use exit() for that. My patch,
on the other hand, makes QEMU not terminate on those cases.

Note that we have two different bugs:

1) QEMU core dumps in case of user error. This is a regression
   introduced in 1.7.0. It can be fixed easily by changing existing code
   to use exit() instead of aborting.
2) QEMU terminating on device_add in case of user error. This bug was
   always present. It can be addressed using the async_error approach
   suggested by Igor.

I will submit a new patch to address (1), first.
Peter Maydell June 7, 2014, 8:55 a.m. UTC | #7
On 7 June 2014 02:09, Eduardo Habkost <ehabkost@redhat.com> wrote:
> On Sat, Jun 07, 2014 at 12:45:26AM +0100, Peter Maydell wrote:
>> I'm with Eduardo on this one -- if the user passes us a bad
>> command line we should diagnose it helpfully and exit with
>> a failure code; abort() is for programming errors. (If nothing
>> else, using abort() for user-triggerable conditions tends to
>> mean we get bug reports about core dumps, so it's in our
>> own interest to not do that :-) )
>
> So you are agreeing with Igor, as well. He is just suggesting we keep
> terminating QEMU on that case, and we can use exit() for that. My patch,
> on the other hand, makes QEMU not terminate on those cases.

No, I'm happy with not terminating and handling the case
correctly if that makes more sense for this case; I'm just
saying that dumping core is definitely not OK.

thanks
-- PMM
diff mbox

Patch

diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index 3d12560..8cd7c2a 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -976,6 +976,7 @@  void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
                                     Error **errp)
 {
     GlobalProperty *prop;
+    Object *obj = OBJECT(dev);
 
     QTAILQ_FOREACH(prop, &global_props, next) {
         Error *err = NULL;
@@ -983,8 +984,15 @@  void qdev_prop_set_globals_for_type(DeviceState *dev, const char *typename,
         if (strcmp(typename, prop->driver) != 0) {
             continue;
         }
+        if (!object_property_find(obj, prop->property, &err)) {
+            /* not_used doesn't default to true for hotpluggable devices,
+             * but in this case we know the property wasn't used when it could.
+             */
+            prop->not_used = true;
+            continue;
+        }
         prop->not_used = false;
-        object_property_parse(OBJECT(dev), prop->value, prop->property, &err);
+        object_property_parse(obj, prop->value, prop->property, &err);
         if (err != NULL) {
             error_propagate(errp, err);
             return;
diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
index 2bef04c..1ccc3e5 100644
--- a/tests/test-qdev-global-props.c
+++ b/tests/test-qdev-global-props.c
@@ -148,8 +148,9 @@  static void test_dynamic_globalprop(void)
 {
     MyType *mt;
     static GlobalProperty props[] = {
-        { TYPE_DYNAMIC_PROPS, "prop1", "101" },
-        { TYPE_DYNAMIC_PROPS, "prop2", "102" },
+        { TYPE_DYNAMIC_PROPS, "prop1", "101", true },
+        { TYPE_DYNAMIC_PROPS, "prop2", "102", true },
+        { TYPE_DYNAMIC_PROPS, "prop3", "103", true },
         { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true },
         {}
     };
@@ -164,6 +165,15 @@  static void test_dynamic_globalprop(void)
     g_assert_cmpuint(mt->prop2, ==, 102);
     all_used = qdev_prop_check_global();
     g_assert_cmpuint(all_used, ==, 1);
+
+    /* prop1 */
+    g_assert(!props[0].not_used);
+    /* prop2 */
+    g_assert(!props[1].not_used);
+    /* TYPE_DYNAMIC_PROPS.prop3: non-existing property */
+    g_assert(props[2].not_used);
+    /* TYPE_DYNAMIC_PROPS-bad.prop3: non-existing class */
+    g_assert(props[3].not_used);
 }
 
 int main(int argc, char **argv)