On Mon, Jun 23, 2025 at 21:59:14 +0200, Peter Krempa wrote:
> From: Peter Krempa <pkre...@redhat.com>
> 
> While 'usb-bot' and 'usb-storage' are ABI and migration compatible for
> disks it's not the case for cdroms. When migrating from a new config
> using 'usb-bot' to an older daemon which would use 'usb-storage' the
> guest os will get I/O errors.
> 
> Thus we must properly fill in models for 'usb' disks so that cdroms can
> be handled properly.
> 
> When parsing XML fill in the models and drop the appropriate models when
> formatting migratable XML.
> 
> The logic is explained in comments in the code.
> 
> Signed-off-by: Peter Krempa <pkre...@redhat.com>
> ---
>  src/qemu/qemu_domain.c                        | 21 ++++++++
>  src/qemu/qemu_postparse.c                     | 49 ++++++++++++++++++-
>  src/qemu/qemu_postparse.h                     |  4 +-
>  tests/qemublocktest.c                         | 13 +++--
>  .../qemuhotplug-base-live+cdrom-usb.xml       |  2 +-
>  .../qemuhotplug-base-live+disk-usb.xml        |  2 +-
>  .../disk-cache.x86_64-latest.xml              |  2 +-
>  .../disk-usb-device-model.x86_64-latest.xml   |  4 +-
>  ...test.QEMU_CAPS_DEVICE_USB_BOT-disabled.xml | 24 ++++-----
>  ...date.QEMU_CAPS_DEVICE_USB_BOT-disabled.xml | 24 ++++-----
>  ...sk-usb-device.x86_64-latest.abi-update.xml | 24 ++++-----
>  .../disk-usb-device.x86_64-latest.xml         | 24 ++++-----
>  12 files changed, 132 insertions(+), 61 deletions(-)
> 
> diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
> index ace42b516a..6e147563f3 100644
> --- a/src/qemu/qemu_domain.c
> +++ b/src/qemu/qemu_domain.c
> @@ -5342,6 +5342,27 @@ qemuDomainDefFormatBufInternal(virQEMUDriver *driver,
>              }
>          }
> 
> +        for (i = 0; i < def->ndisks; i++) {
> +            virDomainDiskDef *disk = def->disks[i];
> +
> +            /* The 'mode' property for USB disks was introduced long after 
> USB

s/mode/model/

> +             * disks to allow switching between 'usb-storage' and 'usb-bot'
> +             * device. Despite sharing identical implementation 'usb-bot' 
> allows
> +             * proper configuration of USB cdroms. Unfortunately it is not 
> ABI
> +             * compatible.
> +             *
> +             * To preserve migration to older daemons we can strip the model 
> to
> +             * the default if:
> +             * - it's a normal disk (not cdrom) as both are identical
> +             * - for a usb-cdrom strip the model if it's not 'usb-bot' as 
> that
> +             *   was the old configuration
> +             */
> +            if (disk->bus == VIR_DOMAIN_DISK_BUS_USB &&
> +                (disk->model == VIR_DOMAIN_DISK_MODEL_USB_STORAGE ||
> +                 disk->device == VIR_DOMAIN_DISK_DEVICE_DISK))
> +                disk->model = VIR_DOMAIN_DISK_MODEL_DEFAULT;
> +        }
> +
>          /* Replace the CPU definition updated according to QEMU with the one
>           * used for starting the domain. The updated def will be sent
>           * separately for backward compatibility.
> diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c
> index 8150dffac6..7db378c5ce 100644
> --- a/src/qemu/qemu_postparse.c
> +++ b/src/qemu/qemu_postparse.c
> @@ -202,7 +202,8 @@ 
> qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk,
> 
>  int
>  qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
> -                                 unsigned int parseFlags)
> +                                 unsigned int parseFlags,
> +                                 virQEMUCaps *qemuCaps)
>  {
>      virStorageSource *n;
> 
> @@ -220,6 +221,50 @@ qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
>          disk->mirror->format == VIR_STORAGE_FILE_NONE)
>          disk->mirror->format = VIR_STORAGE_FILE_RAW;
> 
> +    /* default USB disk model:
> +     *
> +     * Historically we didn't use model for USB disks. It became necessary 
> once
> +     * it turned out that 'usb-storage' doesn't properly expose CDROM devices
> +     * with -blockdev. Additionally 'usb-bot' which does properly handle 
> them,
> +     * while having identical implementation in qemu and driver in guest, are
> +     * not in fact ABI compatible. Thus the logic is as follows:
> +     *
> +     * If ABI update is not allowed:
> +     *  - use 'usb-storage' for either (unless only 'usb-bot' is supported)
> +     *
> +     * If ABI update is possible
> +     * - for VIR_DOMAIN_DISK_DEVICE_DISK use 'usb-storage' as it doesn't 
> matter
> +     *    (it is identical with 'usb-bot' ABI wise)
> +     * - for VIR_DOMAIN_DISK_DEVICE_CDROM use 'usb-bot' if available
> +     *    (as it properly exposes cdrom)
> +     *
> +     * When formatting migratable XML the code strips 'usb-storage' to 
> preserve
> +     * migration to older daemons. If a new definition with 'usb-bot' cdrom 
> is
> +     * created via new start or hotplug it will fail migrating. Users must
> +     * explicitly set the broken config in XML or unplug the device.
> +     */
> +    if (qemuCaps &&
> +        disk->bus == VIR_DOMAIN_DISK_BUS_USB &&
> +        disk->model == VIR_DOMAIN_DISK_MODEL_DEFAULT) {
> +
> +        if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
> +            parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) {
> +
> +            if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_BOT)) {
> +                disk->model = VIR_DOMAIN_DISK_MODEL_USB_BOT;
> +            } else if (virQEMUCapsGet(qemuCaps, 
> QEMU_CAPS_DEVICE_USB_STORAGE)) {
> +                disk->model = VIR_DOMAIN_DISK_MODEL_USB_STORAGE;
> +            }
> +
> +        } else {
> +            if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) {

I would merge this with the else above to reduce nesting:

    if (DEVICE_CDROM && ABI_UPDATE) {
    } else if (MODEL_USB_STORAGE) {
        ...
    } else if (MODEL_USB_BOT) {
        ...
    }

> +                disk->model = VIR_DOMAIN_DISK_MODEL_USB_STORAGE;
> +            } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_BOT)) {
> +                disk->model = VIR_DOMAIN_DISK_MODEL_USB_BOT;
> +            }
> +        }
> +    }
> +

The logic is OK. I guess my comment in the previous patch was actually
about using VIR_DOMAIN_DEF_PARSE_ABI_UPDATE flag. But you're not
touching this part here so I guess everything should be fine. Although
I'm still surprised we'd allow ABI update when starting a domain, I
thought this was only allowed when defining it...

>      /* default disk encryption engine */
>      for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
>          if (n->encryption && n->encryption->engine == 
> VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT)

Jirka

Reply via email to