Peter Xu <pet...@redhat.com> writes:

> On Fri, Oct 25, 2024 at 05:55:59PM -0400, Peter Xu wrote:
>> On Fri, Oct 25, 2024 at 11:25:23AM +0200, Markus Armbruster wrote:
>> > Peter Xu <pet...@redhat.com> writes:
>> > 
>> > > X86 IOMMUs cannot be created more than one on a system yet.  Make it a
>> > > singleton so it guards the system from accidentally create yet another
>> > > IOMMU object when one already presents.
>> > >
>> > > Now if someone tries to create more than one, e.g., via:
>> > >
>> > >   ./qemu -M q35 -device intel-iommu -device intel-iommu
>> > >
>> > > The error will change from:
>> > >
>> > >   qemu-system-x86_64: -device intel-iommu: QEMU does not support 
>> > > multiple vIOMMUs for x86 yet.
>> > >
>> > > To:
>> > >
>> > >   qemu-system-x86_64: -device intel-iommu: Class 'intel-iommu' only 
>> > > supports one instance
>> > >
>> > > Unfortunately, yet we can't remove the singleton check in the machine
>> > > hook (pc_machine_device_pre_plug_cb), because there can also be
>> > > virtio-iommu involved, which doesn't share a common parent class yet.
>> > >
>> > > But with this, it should be closer to reach that goal to check singleton 
>> > > by
>> > > QOM one day.
>> > >
>> > > Signed-off-by: Peter Xu <pet...@redhat.com>
>> > 
>> > $ qemu-system-x86_64 -device amd-iommu,help
>> > /work/armbru/qemu/include/hw/boards.h:24:MACHINE: Object 0x56473906f960 is 
>> > not an instance of type machine
>> > Aborted (core dumped)

[...]

>> Thanks for the report!
>> 
>> It turns out that qdev_get_machine() cannot be invoked too early, and the
>> singleton code can make it earlier..
>> 
>> We may want a pre-requisite patch to allow qdev_get_machine() to be invoked
>> anytime, like:
>> 
>> ===8<===
>> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
>> index db36f54d91..7ceae47139 100644
>> --- a/hw/core/qdev.c
>> +++ b/hw/core/qdev.c
>> @@ -831,6 +831,16 @@ Object *qdev_get_machine(void)
>>  {
>>      static Object *dev;
>>  
>> +    if (!phase_check(PHASE_MACHINE_CREATED)) {
>> +        /*
>> +         * When the machine is not created, below can wrongly create
>> +         * /machine to be a container.. this enables qdev_get_machine() to
>> +         * be used at any time and return NULL properly when machine is not
>> +         * created.
>> +         */
>> +        return NULL;
>> +    }
>> +
>>      if (dev == NULL) {
>>          dev = container_get(object_get_root(), "/machine");
>>      }
>> ===8<===
>> 
>> I hope it makes sense on its own.
>
> My apologies, spoke too soon here.  This helper is used too after machine
> is created, but right before switching to PHASE_MACHINE_CREATE stage..

container_get() is a trap.

When the object to be gotten is always "container", it merely
complicates container creation: it's implicitly created on first get.
Which of the calls creates may be less than obvious.

When the object to be gotten is something else, such as a machine,
container_get() before creation is *wrong*, and will lead to trouble
later.

In my opinion:

* Hiding creation in getters is a bad idea unless creation has no
  material side effects.

* Getting anything but a container with container_get() is in bad taste.


> So we need another way, like:
>
> ===8<===
>
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index db36f54d91..36a9fdb428 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -832,7 +832,13 @@ Object *qdev_get_machine(void)
>      static Object *dev;
>  
>      if (dev == NULL) {
> -        dev = container_get(object_get_root(), "/machine");
> +        /*
> +         * NOTE: dev can keep being NULL if machine is not yet created!
> +         * In which case the function will properly return NULL.
> +         *
> +         * Whenever machine object is created and found once, we cache it.
> +         */
> +        dev = object_resolve_path_component(object_get_root(), "machine");
>      }
>  
>      return dev;

Now returns null instead of a bogus container when called before machine
creation.  Improvement of sorts.  But none of the callers expect null...
shouldn't we assert(dev) here?

Hmm, below you add a caller that checks for null.

Another nice mess.

> ===8<===
>
> The idea is still the same.  Meanwhile I'll test more to see whether it has
> other issues.
>
> Thanks,
>
>> Then callers who can be invoked earlier
>> could then handle NULL properly, in this case..
>> 
>> ===8<===
>> diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c
>> index 4bfeb08705..fceb7adfe0 100644
>> --- a/hw/i386/x86-iommu.c
>> +++ b/hw/i386/x86-iommu.c
>> @@ -80,9 +80,15 @@ void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, 
>> MSIMessage *msg_out)
>>  
>>  X86IOMMUState *x86_iommu_get_default(void)
>>  {
>> -    MachineState *ms = MACHINE(qdev_get_machine());
>> -    PCMachineState *pcms =
>> -        PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE));
>> +    Object *machine = qdev_get_machine();
>> +    PCMachineState *pcms;
>> +
>> +    /* If machine has not been created, so is the vIOMMU */
>> +    if (!machine) {
>> +        return NULL;
>> +    }
>> +
>> +    pcms = PC_MACHINE(object_dynamic_cast(machine, TYPE_PC_MACHINE));
>>  
>>      if (pcms &&
>>          object_dynamic_cast(OBJECT(pcms->iommu), TYPE_X86_IOMMU_DEVICE)) {
>> ===8<===
>> 
>> I'll make sure this works if I'll repost.
>> 
>> Thanks,
>> 
>> -- 
>> Peter Xu


Reply via email to