qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v3 1/9] hw: eliminate qdev_try_new, isa_try_new & usb_try_new


From: Daniel P . Berrangé
Subject: Re: [PATCH v3 1/9] hw: eliminate qdev_try_new, isa_try_new & usb_try_new
Date: Thu, 5 Dec 2024 16:21:47 +0000
User-agent: Mutt/2.2.13 (2024-03-09)

On Tue, Dec 03, 2024 at 04:30:06PM +0100, Markus Armbruster wrote:
> Daniel P. Berrangé <berrange@redhat.com> writes:
> 
> > These functions all return NULL rather than asserting, if the requested
> > type is not registered and also cannot be dynamically loaded.
> >
> > In several cases their usage is pointless, since the caller then just
> > reports an error & exits anyway. Easier to just let qdev_new fail
> > with &error_fatal.
> 
> Uh, this sounds as if you'd turn assertion failures by fatal errors,
> which could be fine, but more than just "eliminate qdev_try_new...".
> 
> Turns out you aren't.  qdev_new(), isa_new() and usb_new() are all thin
> wrappers around object_new(), which does not assert, but treats errors
> as fatal:
> 
>     Object *object_new(const char *typename)
>     {
>         TypeImpl *ti = type_get_or_load_by_name(typename, &error_fatal);
> 
>         return object_new_with_type(ti);
>     }
> 
> type_get_or_load_by_name() succeeds if
> 
> * type @typename is compiled into this binary, or
> 
> * exactly one module providing it is known to this binary, and loading
>   it succeeds.
> 
> Put a pin into this for later.
> 
> Suggest something like
> 
>   The difference between qdev_try_new() and qdev_try() is that the
>   former returns failure, while the latter treats it as fatal and
>   terminates the process.  Same for isa_try_new() and usb_try_new().
> 
> A comment in hw/i2c/i2c.h mentions i2c_slave_try_new(), but it doesn't
> exist, and never has.  Suggest to eliminate that, too.
> 
> > In other cases, the desired semantics are clearer to understand if the
> > caller directly checks module_object_class_by_name(), before calling
> > the regular qdev_new (or specialized equiv) function.
> 
> This tacitly assumes qdev_try_new() & friends fail exactly when
> module_object_class_by_name() fails.  True, but not obvious at this
> point.
> 
> It's true, because it also fails exactly when type_get_or_load_by_name()
> returns null:
> 
>     ObjectClass *object_class_by_name(const char *typename)
>     {
>         TypeImpl *type = type_get_by_name_noload(typename);
> 
>         if (!type) {
>             return NULL;
>         }
> 
>         type_initialize(type);
> 
>         return type->class;
>     }
> 


> > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > index f9147fecbd..d668970bee 100644
> > --- a/hw/i386/pc.c
> > +++ b/hw/i386/pc.c
> > @@ -596,9 +596,11 @@ static gboolean pc_init_ne2k_isa(ISABus *bus, NICInfo 
> > *nd, Error **errp)
> >                     "maximum number of ISA NE2000 devices exceeded");
> >          return false;
> >      }
> > -    isa_ne2000_init(bus, ne2000_io[nb_ne2k],
> > -                    ne2000_irq[nb_ne2k], nd);
> > -    nb_ne2k++;
> > +    if (module_object_class_by_name(TYPE_ISA_NE2000)) {
> > +        isa_ne2000_init(bus, ne2000_io[nb_ne2k],
> > +                        ne2000_irq[nb_ne2k], nd);
> > +        nb_ne2k++;
> > +    }
> 
> This gave me pause until I saw the change to isa_ne2000_init() below.
> There, you replace isa_try_new() by isa_new().  Before the patch,
> isa_ne2000_init() can fail, afterwards it treats errors as fatal.  And
> that's why you need to guard against failure here.
> 
> In other words, you lifted the guard out of isa_ne2000_init() into its
> sole caller.  Fine, just less than obvious in review.

Yeah, actually this is a pre-existing bug I should fix in a
preceeding patch.  isa_ne2000_init can fail today, but we
don't check the return value, and unconditionally do
"nb_ne2k++". So nb_ne2k is wrong if isa_ne2000_init ever
fails. Not sure if this has any bad functional effect,
but conceptually it is clearly a bug.

> 
> >      return true;
> >  }
> >  
> > @@ -1087,7 +1089,7 @@ static void pc_superio_init(ISABus *isa_bus, bool 
> > create_fdctrl,
> >      int i;
> >      DriveInfo *fd[MAX_FD];
> >      qemu_irq *a20_line;
> > -    ISADevice *i8042, *port92, *vmmouse;
> > +    ISADevice *i8042, *port92, *vmmouse = NULL;
> >  
> >      serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS);
> >      parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
> > @@ -1117,9 +1119,9 @@ static void pc_superio_init(ISABus *isa_bus, bool 
> > create_fdctrl,
> >      i8042 = isa_create_simple(isa_bus, TYPE_I8042);
> >      if (!no_vmport) {
> >          isa_create_simple(isa_bus, TYPE_VMPORT);
> > -        vmmouse = isa_try_new("vmmouse");
> > -    } else {
> > -        vmmouse = NULL;
> > +        if (module_object_class_by_name("vmmouse")) {
> > +            vmmouse = isa_new("vmmouse");
> > +        }
> >      }
> >      if (vmmouse) {
> >          object_property_set_link(OBJECT(vmmouse), TYPE_I8042, 
> > OBJECT(i8042),
> 
> This is now like
> 
>        vmmouse = NULL;
>        if (...) {
>            if (module_object_class_by_name("vmmouse")) {
>                vmmouse = isa_new("vmmouse");
>            }
>        }
>        if (vmmouse) {
>            object_property_set_link(OBJECT(vmmouse), TYPE_I8042, 
> OBJECT(i8042),
>                                     &error_abort);
>            isa_realize_and_unref(vmmouse, isa_bus, &error_fatal);
>        }
> 
> We could straighten control flow like this:
> 
>        if (...) {
>            if (module_object_class_by_name("vmmouse")) {
>                vmmouse = isa_new("vmmouse");
>                object_property_set_link(OBJECT(vmmouse), TYPE_I8042,
>                                         OBJECT(i8042), &error_abort);
>                isa_realize_and_unref(vmmouse, isa_bus, &error_fatal);
>            }
>        }
> 
> But there is a more fundamental issue.
> 
> pc_superio_init() creates onboard devices.
> 
> With CONFIG_MODULES off, it creates a "vmmouse" device exactly when the
> type is compiled into this binary.  This makes the guest machine type
> depend on build configuration.  I consider this questionable; I'd prefer
> such things to be explicit in the C code.  But let's ignore this.

Yeah, I had the same horrified realization that we'd made machine ABI
vary based on installed pkgs :-( Not sure how to get us out of that
mess easily.

> Silently not creating it just because the machine is in a funny state,
> say temporarily lacks the resources to load a DSO, is plainly wrong.
> 
> Not this patch's fault.  Doesn't make it less wrong :)

Agreed, we definitely need to distinguish "module not installed",
from all other types of failure to load a module.

> > @@ -1163,11 +1165,7 @@ void pc_basic_device_init(struct PCMachineState 
> > *pcms,
> >      if (pcms->hpet_enabled) {
> >          qemu_irq rtc_irq;
> >  
> > -        hpet = qdev_try_new(TYPE_HPET);
> > -        if (!hpet) {
> > -            error_report("couldn't create HPET device");
> > -            exit(1);
> > -        }
> > +        hpet = qdev_new(TYPE_HPET);
> 
> This replaces the error message "couldn't create HPET device" by one
> provided by QOM.  These are:
> 
> * When the type is not known to this binary: "unknown type 'hpet'".
> 
> * When the type is known, but not compiled in, and the module can't be
>   loaded for whatever reason: "could not load a module for type 'hpet':
>   MORE", where MORE is the error message provided by module_load_qom().
> 
> Worth at least hinting at this in the commit message?

Sure.




> > diff --git a/include/hw/usb.h b/include/hw/usb.h
> > index d46d96779a..bb778cb844 100644
> > --- a/include/hw/usb.h
> > +++ b/include/hw/usb.h
> > @@ -584,11 +584,6 @@ static inline USBDevice *usb_new(const char *name)
> >      return USB_DEVICE(qdev_new(name));
> >  }
> >  
> > -static inline USBDevice *usb_try_new(const char *name)
> > -{
> > -    return USB_DEVICE(qdev_try_new(name));
> > -}
> > -
> >  static inline bool usb_realize_and_unref(USBDevice *dev, USBBus *bus, 
> > Error **errp)
> >  {
> >      return qdev_realize_and_unref(&dev->qdev, &bus->qbus, errp);
> 
> Maybe I'm having another scatter-brained day, but I found the patch
> somewhat confusing in review.  Happy to suggest a possible split if
> you're interested.

I can have another think about changing it. Mostly I was just working
backwards when creating this, by deleting the methods I wanted to
remove and the patching up the build failures, so there wasn't much
thought put into the split of this one.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|




reply via email to

[Prev in Thread] Current Thread [Next in Thread]