diff mbox

qdev: Keep global allocation counter per bus

Message ID 1386188688-6480-1-git-send-email-agraf@suse.de
State New
Headers show

Commit Message

Alexander Graf Dec. 4, 2013, 8:24 p.m. UTC
When we have 2 separate qdev devices that both create a qbus of the
same type without specifying a bus name or device name, we end up
with two buses of the same name, such as ide.0 on the Mac machines:

  dev: macio-ide, id ""
    bus: ide.0
      type IDE
  dev: macio-ide, id ""
    bus: ide.0
      type IDE

If we now spawn a device that connects to a ide.0 the last created
bus gets the device, with the first created bus inaccessible to the
command line.

After some discussion on IRC we concluded that the best quick fix way
forward for this is to make automated bus-class type based allocation
count a global counter. That's what this patch implements. With this
we instead get

  dev: macio-ide, id ""
    bus: ide.1
      type IDE
  dev: macio-ide, id ""
    bus: ide.0
      type IDE

on the example mentioned above.

CC: Paolo Bonzini <pbonzini@redhat.com>
CC: Markus Armbruster <armbru@redhat.com>
CC: Anthony Liguori <aliguori@amazon.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
---
 hw/core/qdev.c         | 20 +++++++++++++-------
 include/hw/qdev-core.h |  2 ++
 2 files changed, 15 insertions(+), 7 deletions(-)

Comments

Paolo Bonzini Dec. 5, 2013, 8:58 a.m. UTC | #1
Il 04/12/2013 21:24, Alexander Graf ha scritto:
> When we have 2 separate qdev devices that both create a qbus of the
> same type without specifying a bus name or device name, we end up
> with two buses of the same name, such as ide.0 on the Mac machines:
> 
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
> 
> If we now spawn a device that connects to a ide.0 the last created
> bus gets the device, with the first created bus inaccessible to the
> command line.
> 
> After some discussion on IRC we concluded that the best quick fix way
> forward for this is to make automated bus-class type based allocation
> count a global counter. That's what this patch implements. With this
> we instead get
> 
>   dev: macio-ide, id ""
>     bus: ide.1
>       type IDE
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
> 
> on the example mentioned above.
> 
> CC: Paolo Bonzini <pbonzini@redhat.com>
> CC: Markus Armbruster <armbru@redhat.com>
> CC: Anthony Liguori <aliguori@amazon.com>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  hw/core/qdev.c         | 20 +++++++++++++-------
>  include/hw/qdev-core.h |  2 ++
>  2 files changed, 15 insertions(+), 7 deletions(-)
> 
> diff --git a/hw/core/qdev.c b/hw/core/qdev.c
> index e374a93..959130c 100644
> --- a/hw/core/qdev.c
> +++ b/hw/core/qdev.c
> @@ -409,27 +409,33 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
>  static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
>  {
>      const char *typename = object_get_typename(OBJECT(bus));
> +    BusClass *bc;
>      char *buf;
> -    int i,len;
> +    int i, len, bus_id;
>  
>      bus->parent = parent;
>  
>      if (name) {
>          bus->name = g_strdup(name);
>      } else if (bus->parent && bus->parent->id) {
> -        /* parent device has id -> use it for bus name */
> +        /* parent device has id -> use it plus parent-bus-id for bus name */
> +        bus_id = bus->parent->num_child_bus;
> +
>          len = strlen(bus->parent->id) + 16;
>          buf = g_malloc(len);
> -        snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus);
> +        snprintf(buf, len, "%s.%d", bus->parent->id, bus_id);
>          bus->name = buf;
>      } else {
> -        /* no id -> use lowercase bus type for bus name */
> +        /* no id -> use lowercase bus type plus global bus-id for bus name */
> +        bc = BUS_GET_CLASS(bus);
> +        bus_id = bc->automatic_ids++;
> +
>          len = strlen(typename) + 16;
>          buf = g_malloc(len);
> -        len = snprintf(buf, len, "%s.%d", typename,
> -                       bus->parent ? bus->parent->num_child_bus : 0);
> -        for (i = 0; i < len; i++)
> +        len = snprintf(buf, len, "%s.%d", typename, bus_id);
> +        for (i = 0; i < len; i++) {
>              buf[i] = qemu_tolower(buf[i]);
> +        }
>          bus->name = buf;
>      }
>  
> diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
> index f2043a6..09f8527 100644
> --- a/include/hw/qdev-core.h
> +++ b/include/hw/qdev-core.h
> @@ -161,6 +161,8 @@ struct BusClass {
>      int (*reset)(BusState *bus);
>      /* maximum devices allowed on the bus, 0: no limit. */
>      int max_dev;
> +    /* number of automatically allocated bus ids (e.g. ide.0) */
> +    int automatic_ids;
>  };
>  
>  typedef struct BusChild {
> 

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Markus Armbruster Dec. 5, 2013, 9:44 a.m. UTC | #2
Alexander Graf <agraf@suse.de> writes:

> When we have 2 separate qdev devices that both create a qbus of the
> same type without specifying a bus name or device name, we end up
> with two buses of the same name, such as ide.0 on the Mac machines:
>
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>
> If we now spawn a device that connects to a ide.0 the last created
> bus gets the device, with the first created bus inaccessible to the
> command line.

isapc has the same issue: two onboard isa-ide devices, each providing a
bus, both buses named ide.0.

> After some discussion on IRC we concluded that the best quick fix way
> forward for this is to make automated bus-class type based allocation
> count a global counter. That's what this patch implements. With this
> we instead get
>
>   dev: macio-ide, id ""
>     bus: ide.1
>       type IDE
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>
> on the example mentioned above.

Commit message should explain more clearly how and when this affects bus
names.

Patch breaks isapc:

    $ qemu -nodefaults -S -display none -monitor stdio -M isapc -drive if=none,id=drive0 -device ide-cd,drive=drive0
    (qemu) Segmentation fault (core dumped)

Debugging a bit:

    (gdb) bt
    #0  0x000055555572e745 in ide_get_geometry (bus=0x0, unit=0, cyls=
        0x7fffffffdb8a, heads=0x7fffffffdb88 "\210\271qU", secs=
        0x7fffffffdb89 "\271qU") at /home/armbru/work/qemu/hw/ide/qdev.c:129
    #1  0x00005555558f1fed in pc_cmos_init_late (opaque=0x55555628b420 <arg.29452>)
        at /home/armbru/work/qemu/hw/i386/pc.c:336
    #2  0x0000555555898abc in qemu_devices_reset ()
        at /home/armbru/work/qemu/vl.c:1836
    #3  0x0000555555898b28 in qemu_system_reset (report=false)
        at /home/armbru/work/qemu/vl.c:1845
    #4  0x00005555558a0640 in main (argc=13, argv=0x7fffffffe048, envp=
        0x7fffffffe0b8) at /home/armbru/work/qemu/vl.c:4344
    (gdb) p arg->idebus
    $1 = {0x555556322e10, 0x0}
    (gdb) p i
    $2 = 2

Looks like your patch kills the second isa-ide somehow.

Your commit message doesn't state your command line, so I had to figure
out a PPC example myself:

    $ qemu-system-ppc -M mac99 -nodefaults -S -display none -monitor stdio -drive if=none,id=drive0 -device ide-cd,drive=drive0,bus=ide.0

"info qtree" before your patch:

      dev: macio-ide, id ""
        irq 2
        mmio ffffffffffffffff/0000000000001000
        bus: ide.0
          type IDE
          dev: ide-cd, id ""
            drive = drive0
            logical_block_size = 512
            physical_block_size = 512
            min_io_size = 0
            opt_io_size = 0
            bootindex = -1
            discard_granularity = 512
            ver = "1.7.50"
            wwn = 0x0
            serial = "QM00003"
            model = <null>
            unit = 0
      dev: macio-ide, id ""
        irq 2
        mmio ffffffffffffffff/0000000000001000
        bus: ide.0
          type IDE

After:

      dev: macio-ide, id ""
        irq 2
        mmio ffffffffffffffff/0000000000001000
        bus: ide.1
          type IDE
      dev: macio-ide, id ""
        irq 2
        mmio ffffffffffffffff/0000000000001000
        bus: ide.0
          type IDE
          dev: ide-cd, id ""
            drive = drive0
            logical_block_size = 512
            physical_block_size = 512
            min_io_size = 0
            opt_io_size = 0
            bootindex = -1
            discard_granularity = 512
            ver = "1.7.50"
            wwn = 0x0
            serial = "QM00001"
            model = <null>
            unit = 0

Incompatible change: device ide-cd moved to a different controller.
Great fun when you try to live migrate across your patch.

I'd expect isapc to have the same issue once its crash bug is fixed.

First law of QEMU hacking: if your patch looks simple, it's probably
wrong ;)
Markus Armbruster Dec. 5, 2013, 10:23 a.m. UTC | #3
Alexander Graf <agraf@suse.de> writes:

> When we have 2 separate qdev devices that both create a qbus of the
> same type without specifying a bus name or device name, we end up
> with two buses of the same name, such as ide.0 on the Mac machines:
>
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>
> If we now spawn a device that connects to a ide.0 the last created
> bus gets the device, with the first created bus inaccessible to the
> command line.
>
> After some discussion on IRC we concluded that the best quick fix way
> forward for this is to make automated bus-class type based allocation
> count a global counter. That's what this patch implements. With this
> we instead get
>
>   dev: macio-ide, id ""
>     bus: ide.1
>       type IDE
>   dev: macio-ide, id ""
>     bus: ide.0
>       type IDE
>
> on the example mentioned above.

What I don't like about the global counter: we define the board's ABI
implicitly by device initialization order.  Bad taste and fragile, but
we do this elsewhere, too, e.g. pci_create_simple(bugs, -1, ...).

Wanted: tests to catch accidental ABI changes, covering at least the
parts we define implicitly.
Paolo Bonzini Dec. 5, 2013, 10:33 a.m. UTC | #4
Il 05/12/2013 10:44, Markus Armbruster ha scritto:
> Incompatible change: device ide-cd moved to a different controller.

Yes, it should be stated in the commit message but it's expected as
discussed yesterday on IRC.  The solution is not to use "-device" (which
was broken) if you care about backwards compatibility; use "-drive if=ide".

> Great fun when you try to live migrate across your patch.
> 
> I'd expect isapc to have the same issue once its crash bug is fixed.
> 
> First law of QEMU hacking: if your patch looks simple, it's probably
> wrong ;)

Yes, the question is how wrong and how the wrong balances the right.

Paolo
Markus Armbruster Dec. 5, 2013, 11:20 a.m. UTC | #5
Paolo Bonzini <pbonzini@redhat.com> writes:

> Il 05/12/2013 10:44, Markus Armbruster ha scritto:
>> Incompatible change: device ide-cd moved to a different controller.
>
> Yes, it should be stated in the commit message but it's expected as
> discussed yesterday on IRC.  The solution is not to use "-device" (which
> was broken) if you care about backwards compatibility; use "-drive if=ide".

-device is broken for the *other* controller.  It works just fine for
this one.

>> Great fun when you try to live migrate across your patch.
>> 
>> I'd expect isapc to have the same issue once its crash bug is fixed.
>> 
>> First law of QEMU hacking: if your patch looks simple, it's probably
>> wrong ;)
>
> Yes, the question is how wrong and how the wrong balances the right.

Is it really too much bother to change the ide.0 name for the
controllers that bus=ide.0 doesn't use, and keep it for the one it does
use?

If yes, the incompatible change needs to be documented much more clearly
in the commit message.
Paolo Bonzini Dec. 5, 2013, 11:38 a.m. UTC | #6
Il 05/12/2013 12:20, Markus Armbruster ha scritto:
> Paolo Bonzini <pbonzini@redhat.com> writes:
> 
>> Il 05/12/2013 10:44, Markus Armbruster ha scritto:
>>> Incompatible change: device ide-cd moved to a different controller.
>>
>> Yes, it should be stated in the commit message but it's expected as
>> discussed yesterday on IRC.  The solution is not to use "-device" (which
>> was broken) if you care about backwards compatibility; use "-drive if=ide".
> 
> -device is broken for the *other* controller.  It works just fine for
> this one.

It is broken in that "-device bus=ide.0" would access the second
controller, the one corresponding to "-drive if=ide,bus=1", or -hdc/-hdd.

>>> Great fun when you try to live migrate across your patch.
>>>
>>> I'd expect isapc to have the same issue once its crash bug is fixed.
>>>
>>> First law of QEMU hacking: if your patch looks simple, it's probably
>>> wrong ;)
>>
>> Yes, the question is how wrong and how the wrong balances the right.
> 
> Is it really too much bother to change the ide.0 name for the
> controllers that bus=ide.0 doesn't use, and keep it for the one it does
> use?

Yes, for two reasons:

(1) practical reason, the one mentioned above: it would mean that
"-device bus=ide.0" corresponds to "-drive if=ide,bus=1" and similarly
for ide.1/bus=0.  So we would make the cure worse than the disease, in
my opinion.  This IMO is a pretty strong sign that the
backwards-compatibility problem doesn't exist and no one ever used
"-device" for built-in devices on anything other than pc IDE and pseries
SCSI.

(2) technical reason: the two are inverted because bus names currently
have a "last wins" policy.  The policy is implemented by using
QTAILQ_INSERT_HEAD in bus_add_child.  So it is not possible to know the
correct bus names unless you know how many buses you will have (e.g. for
3 buses you'd start giving numbers from ide.2 and go down from there).
And implementing this would probably be really really ugly.

> If yes, the incompatible change needs to be documented much more clearly
> in the commit message.

And in the release notes.

Paolo
diff mbox

Patch

diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index e374a93..959130c 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -409,27 +409,33 @@  DeviceState *qdev_find_recursive(BusState *bus, const char *id)
 static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
 {
     const char *typename = object_get_typename(OBJECT(bus));
+    BusClass *bc;
     char *buf;
-    int i,len;
+    int i, len, bus_id;
 
     bus->parent = parent;
 
     if (name) {
         bus->name = g_strdup(name);
     } else if (bus->parent && bus->parent->id) {
-        /* parent device has id -> use it for bus name */
+        /* parent device has id -> use it plus parent-bus-id for bus name */
+        bus_id = bus->parent->num_child_bus;
+
         len = strlen(bus->parent->id) + 16;
         buf = g_malloc(len);
-        snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus);
+        snprintf(buf, len, "%s.%d", bus->parent->id, bus_id);
         bus->name = buf;
     } else {
-        /* no id -> use lowercase bus type for bus name */
+        /* no id -> use lowercase bus type plus global bus-id for bus name */
+        bc = BUS_GET_CLASS(bus);
+        bus_id = bc->automatic_ids++;
+
         len = strlen(typename) + 16;
         buf = g_malloc(len);
-        len = snprintf(buf, len, "%s.%d", typename,
-                       bus->parent ? bus->parent->num_child_bus : 0);
-        for (i = 0; i < len; i++)
+        len = snprintf(buf, len, "%s.%d", typename, bus_id);
+        for (i = 0; i < len; i++) {
             buf[i] = qemu_tolower(buf[i]);
+        }
         bus->name = buf;
     }
 
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index f2043a6..09f8527 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -161,6 +161,8 @@  struct BusClass {
     int (*reset)(BusState *bus);
     /* maximum devices allowed on the bus, 0: no limit. */
     int max_dev;
+    /* number of automatically allocated bus ids (e.g. ide.0) */
+    int automatic_ids;
 };
 
 typedef struct BusChild {