[libvirt] [PATCH v4 12/25] qemu: new functions to calculate/set device pciConnectFlags
Andrea Bolognani
abologna at redhat.com
Tue Oct 25 13:06:49 UTC 2016
On Fri, 2016-10-14 at 15:54 -0400, Laine Stump wrote:
> The lowest level function of this trio
> (qemuDomainDeviceCalculatePCIConnectFlags()) aims to be the single
> authority for the virDomainPCIConnectFlags to use for any given device
> using a particular arch/machinetype/qemu-binary.
>
> qemuDomainFillDevicePCIConnectFlags() sets info->pciConnectFlags in a
> single device (unless it has no virDomainDeviceInfo, in which case
> it's a NOP).
>
> qemuDomainFillAllPCIConnectFlags() sets info->pciConnectFlags in all
> devices that have a virDomainDeviceInfo
>
> The latter two functions aren't called anywhere yet. This commit is
> just making them available. Later patches will replace all the current
> hodge-podge of flag settings with calls to this single authority.
> ---
>
> Change: re-implemented as a giant two-level compound switch statement
> with no default cases, as suggested by Andrea. Also start off
> with the function setting the flags for *all* devices to
> PCI_DEVICE|HOTPLUGGABLE, which is what's currently used when
> assigning addresses (as opposed to merely validating addresses,
> which is much less strict).
>
> src/conf/device_conf.h | 5 +
> src/qemu/qemu_domain_address.c | 379 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 384 insertions(+)
>
> diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h
> index 8443de6..f435fb5 100644
> --- a/src/conf/device_conf.h
> +++ b/src/conf/device_conf.h
> @@ -142,6 +142,11 @@ typedef struct _virDomainDeviceInfo {
> /* bootIndex is only used for disk, network interface, hostdev
> * and redirdev devices */
> unsigned int bootIndex;
> +
> + /* pciConnectFlags is only used internally during address
> + * assignment, never saved and never reported.
> + */
> + int pciConnectFlags; /* enum virDomainPCIConnectFlags */
> } virDomainDeviceInfo, *virDomainDeviceInfoPtr;
>
>
> diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
> index b4ea906..457eae6 100644
> --- a/src/qemu/qemu_domain_address.c
> +++ b/src/qemu/qemu_domain_address.c
> @@ -405,6 +405,385 @@ qemuDomainAssignARMVirtioMMIOAddresses(virDomainDefPtr def,
> }
>
>
> +/**
> + * qemuDomainDeviceCalculatePCIConnectFlags:
> + *
> + * Lowest level function to determine PCI connectFlags for a
> + * device. This function relies on the next higher-level function
> + * determining the value for pcieFlags and virtioFlags in advance -
> + * this is to make it more efficient to call multiple times.
> + *
> + * @dev: The device to be checked
> + *
> + * @pcieFlags: flags to use for a known PCI Express device
> + *
> + * @virtioFlags: flags to use for a virtio device (properly vetted
> + * for the current qemu binary and arch/machinetype)
> + *
> + * returns appropriate virDomainPCIConnectFlags for this device in
> + * this domain, or 0 if the device doesn't connect using PCI. There
> + * is no failure.
> + */
Please adjust the formatting of this and later comments to
match the guidelines we discussed for earlier patches.
> +static virDomainPCIConnectFlags
> +qemuDomainDeviceCalculatePCIConnectFlags(virDomainDeviceDefPtr dev,
> + virDomainPCIConnectFlags pcieFlags
> + ATTRIBUTE_UNUSED,
> + virDomainPCIConnectFlags virtioFlags
> + ATTRIBUTE_UNUSED)
> +{
> + virDomainPCIConnectFlags pciFlags = (VIR_PCI_CONNECT_TYPE_PCI_DEVICE |
Double space.
> + VIR_PCI_CONNECT_HOTPLUGGABLE);
> +
> + switch ((virDomainDeviceType)dev->type) {
We use both '(type)expr' and '(type) expr', but the latter
seems to be more popular. Up to you whether or not you want
to conform :)
> + case VIR_DOMAIN_DEVICE_CONTROLLER: {
> + virDomainControllerDefPtr cont = dev->data.controller;
> +
> + switch ((virDomainControllerType)cont->type) {
> + case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
> + return virDomainPCIControllerModelToConnectType(cont->model);
> +
> + case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
> + return pciFlags;
> +
> + case VIR_DOMAIN_CONTROLLER_TYPE_USB:
> + switch ((virDomainControllerModelUSB)cont->model) {
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI:
> + return pciFlags;
> +
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI:
> + return pciFlags;
> +
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1: /* xen only */
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2: /* xen only */
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE:
> + case VIR_DOMAIN_CONTROLLER_MODEL_USB_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
> + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
> + case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
> + return pciFlags;
> +
> + case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
> + case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
> + case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> + }
> +
> + case VIR_DOMAIN_DEVICE_FS:
> + /* the only type of filesystem so far is virtio-9p-pci */
> + return pciFlags;
> +
> + case VIR_DOMAIN_DEVICE_NET: {
> + virDomainNetDefPtr net = dev->data.net;
> +
> + /* NB: a type='hostdev' will use PCI, but its
> + * address is assigned when we're assigning the
> + * addresses for other hostdev devices.
> + */
> + if (net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV ||
> + STREQ(net->model, "usb-net")) {
> + /* should be 0 */
> + return pciFlags;
> + }
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_SOUND:
> + switch ((virDomainSoundModel)dev->data.sound->model) {
> + case VIR_DOMAIN_SOUND_MODEL_ES1370:
> + case VIR_DOMAIN_SOUND_MODEL_AC97:
> + case VIR_DOMAIN_SOUND_MODEL_ICH6:
> + case VIR_DOMAIN_SOUND_MODEL_ICH9:
> + return pciFlags;
> +
> + case VIR_DOMAIN_SOUND_MODEL_SB16:
> + case VIR_DOMAIN_SOUND_MODEL_PCSPK:
> + case VIR_DOMAIN_SOUND_MODEL_USB:
> + case VIR_DOMAIN_SOUND_MODEL_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_DISK:
> + switch ((virDomainDiskBus)dev->data.disk->bus) {
> + case VIR_DOMAIN_DISK_BUS_VIRTIO:
> + return pciFlags; /* only virtio disks use PCI */
> +
> + case VIR_DOMAIN_DISK_BUS_IDE:
> + case VIR_DOMAIN_DISK_BUS_FDC:
> + case VIR_DOMAIN_DISK_BUS_SCSI:
> + case VIR_DOMAIN_DISK_BUS_XEN:
> + case VIR_DOMAIN_DISK_BUS_USB:
> + case VIR_DOMAIN_DISK_BUS_UML:
> + case VIR_DOMAIN_DISK_BUS_SATA:
> + case VIR_DOMAIN_DISK_BUS_SD:
> + case VIR_DOMAIN_DISK_BUS_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_HOSTDEV:
> + return pciFlags;
> +
> + case VIR_DOMAIN_DEVICE_MEMBALLOON:
> + switch ((virDomainMemBalloonModel)dev->data.memballoon->model) {
You will of course need to s/MemBalloon/Memballoon/ here now.
> + case VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO:
> + return pciFlags;
> +
> + case VIR_DOMAIN_MEMBALLOON_MODEL_XEN:
> + case VIR_DOMAIN_MEMBALLOON_MODEL_NONE:
> + case VIR_DOMAIN_MEMBALLOON_MODEL_LAST:
> + /* should be 0 (not PCI) */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_RNG:
> + switch ((virDomainRNGModel)dev->data.rng->model) {
> + case VIR_DOMAIN_RNG_MODEL_VIRTIO:
> + return pciFlags;
> +
> + case VIR_DOMAIN_RNG_MODEL_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_WATCHDOG:
> + /* only one model connects using PCI */
> + switch ((virDomainWatchdogModel)dev->data.watchdog->model) {
> + case VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB:
> + return pciFlags;
> +
> + case VIR_DOMAIN_WATCHDOG_MODEL_IB700:
> + case VIR_DOMAIN_WATCHDOG_MODEL_DIAG288:
> + case VIR_DOMAIN_WATCHDOG_MODEL_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_VIDEO:
> + switch ((virDomainVideoType)dev->data.video->type) {
> + case VIR_DOMAIN_VIDEO_TYPE_VIRTIO:
> + case VIR_DOMAIN_VIDEO_TYPE_VGA:
> + case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
> + case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
> + case VIR_DOMAIN_VIDEO_TYPE_XEN:
> + case VIR_DOMAIN_VIDEO_TYPE_VBOX:
> + case VIR_DOMAIN_VIDEO_TYPE_QXL:
> + case VIR_DOMAIN_VIDEO_TYPE_PARALLELS:
> + return pciFlags;
> +
> + case VIR_DOMAIN_VIDEO_TYPE_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> +
> + case VIR_DOMAIN_DEVICE_SHMEM:
> + return pciFlags;
> +
> + case VIR_DOMAIN_DEVICE_INPUT:
> + switch ((virDomainInputBus)dev->data.input->bus) {
> + case VIR_DOMAIN_INPUT_BUS_VIRTIO:
> + return pciFlags;
> +
> + case VIR_DOMAIN_INPUT_BUS_PS2:
> + case VIR_DOMAIN_INPUT_BUS_USB:
> + case VIR_DOMAIN_INPUT_BUS_XEN:
> + case VIR_DOMAIN_INPUT_BUS_PARALLELS:
> + case VIR_DOMAIN_INPUT_BUS_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + case VIR_DOMAIN_DEVICE_CHR:
> + switch ((virDomainChrSerialTargetType)dev->data.chr->targetType) {
> + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
> + return pciFlags;
> +
> + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
> + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
> + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + /* These devices don't ever connect with PCI */
> + case VIR_DOMAIN_DEVICE_NVRAM:
> + case VIR_DOMAIN_DEVICE_TPM:
> + case VIR_DOMAIN_DEVICE_PANIC:
> + case VIR_DOMAIN_DEVICE_MEMORY:
> + case VIR_DOMAIN_DEVICE_HUB:
> + case VIR_DOMAIN_DEVICE_REDIRDEV:
> + case VIR_DOMAIN_DEVICE_SMARTCARD:
> + /* These devices don't even have a DeviceInfo */
> + case VIR_DOMAIN_DEVICE_LEASE:
> + case VIR_DOMAIN_DEVICE_GRAPHICS:
> + case VIR_DOMAIN_DEVICE_IOMMU:
> + case VIR_DOMAIN_DEVICE_LAST:
> + case VIR_DOMAIN_DEVICE_NONE:
> + /* should be 0 */
> + return pciFlags;
> + }
> +
> + /* We can never get here, because all cases are covered in the
> + * switch, and they all return, but the compiler will still
> + * complain "control reaches end of non-void function" unless
> + * we add the following return.
> + */
> + return 0;
> +}
> +
> +
> +typedef struct {
> + virDomainPCIConnectFlags virtioFlags;
> + virDomainPCIConnectFlags pcieFlags;
> +} qemuDomainFillDevicePCIConnectFlagsIterData;
> +
Add one more empty line here.
> +/**
> + * qemuDomainFillDevicePCIConnectIterInit:
Should be qemuDomainFillDevicePCIConnectFlagsIterInit(), even
though it's already quite a mouthful as it is.
> + *
> + * Initialize the iterator data that is used when calling
> + * qemuDomainCalculateDevicePCIConnectFlags().
> + *
Remove this empty line.
> + */
> +static void
> +qemuDomainFillDevicePCIConnectIterInit(virDomainDefPtr def,
> + virQEMUCapsPtr qemuCaps,
> + qemuDomainFillDevicePCIConnectFlagsIterData *data)
> +{
> + if (qemuDomainMachineHasPCIeRoot(def)) {
> + data->pcieFlags = (VIR_PCI_CONNECT_TYPE_PCIE_DEVICE |
> + VIR_PCI_CONNECT_HOTPLUGGABLE);
Double space here...
> + } else {
> + data->pcieFlags = (VIR_PCI_CONNECT_TYPE_PCI_DEVICE |
> + VIR_PCI_CONNECT_HOTPLUGGABLE);
... and again here.
> + }
> +
> + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_PCI_DISABLE_LEGACY)) {
> + data->virtioFlags = data->pcieFlags;
> + } else {
> + data->virtioFlags = (VIR_PCI_CONNECT_TYPE_PCI_DEVICE |
> + VIR_PCI_CONNECT_HOTPLUGGABLE);
> + }
> +}
> +
> +
> +/**
> + * qemuDomainFillDevicePCIConnectFlagsIter:
> + *
> + * The version of the function to call with
> + * virDomainDeviceInfoIterate()
Please change this to something along the lines of
Sets PCI connect flags for @dev. For use with
virDomainDeviceInfoIterate().
> + *
> + * @def: the entire DomainDef
> + *
> + * @dev: The device to be checked
> + *
> + * @info: virDomainDeviceInfo within the device
> + *
> + * @opaque: points to iterator data setup beforehand.
> + *
> + * Sets @info->pciConnectFlags. Always returns 0 - there
> + * is no failure.
> + *
> + */
> +static int
> +qemuDomainFillDevicePCIConnectFlagsIter(virDomainDefPtr def ATTRIBUTE_UNUSED,
> + virDomainDeviceDefPtr dev,
> + virDomainDeviceInfoPtr info,
> + void *opaque)
> +{
> + qemuDomainFillDevicePCIConnectFlagsIterData *data = opaque;
> +
> + info->pciConnectFlags
> + = qemuDomainDeviceCalculatePCIConnectFlags(dev, data->pcieFlags,
> + data->virtioFlags);
> + return 0;
> +}
> +
> +
> +/**
> + * qemuDomainFillAllPCIConnectFlags:
> + *
> + * Set the info->pciConnectFlags for all devices in the domain.
> + *
> + * @def: the entire DomainDef
> + *
> + * @qemuCaps: as you'd expect
> + *
> + * sets info->pciConnectFlags for all devices as appropriate. returns
> + * 0 on success or -1 on failure (the only possibility of failure would
> + * be some internal problem with virDomainDeviceInfoIterate())
> + */
> +static int ATTRIBUTE_UNUSED
> +qemuDomainFillAllPCIConnectFlags(virDomainDefPtr def,
> + virQEMUCapsPtr qemuCaps)
> +{
> + qemuDomainFillDevicePCIConnectFlagsIterData data;
> +
> + qemuDomainFillDevicePCIConnectIterInit(def, qemuCaps, &data);
> +
> + return virDomainDeviceInfoIterate(def,
> + qemuDomainFillDevicePCIConnectFlagsIter,
> + &data);
> +}
> +
> +
> +/**
> + * qemuDomainFillDevicePCIConnectFlags:
> + *
> + * The version of the function to call if it will be called just
> + * once.
The description for qemuDomainFillAllPCIConnectFlags() is
pretty good, please change this one to be along those lines.
> + *
> + * @def: the entire DomainDef
> + *
> + * @dev: The device to be checked
> + *
> + * @qemuCaps: as you'd expect
> + *
> + * sets device's info->pciConnectFlags when appropriate.
> + *
> + */
> +static void ATTRIBUTE_UNUSED
> +qemuDomainFillDevicePCIConnectFlags(virDomainDefPtr def,
> + virDomainDeviceDefPtr dev,
> + virQEMUCapsPtr qemuCaps)
> +{
> + virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(dev);
> +
> + if (info) {
> + /* qemuDomainDeviceCalculatePCIConnectFlags() is called with
> + * the data setup in the ...IterData by ...IterInit() rather
> + * than setting the values directly here. It may seem like
> + * pointless posturing, but it's done this way to eliminate
> + * duplicated setup code while allowing more efficient
> + * operation when it's being done repeatedly with the device
> + * iterator (since qemuDomainFillAllPCIConnectFlags() only
> + * calls ...IterInit() once for all devices).
> + */
I feel like this comment would be a better fit for
qemuDomainFillDevicePCIConnectIterInit().
> + qemuDomainFillDevicePCIConnectFlagsIterData data;
> +
> + qemuDomainFillDevicePCIConnectIterInit(def, qemuCaps, &data);
> +
> + info->pciConnectFlags
> + = qemuDomainDeviceCalculatePCIConnectFlags(dev, data.pcieFlags,
> + data.virtioFlags);
> + }
> +}
> +
> +
> static int
> qemuDomainPCIAddressReserveNextAddr(virDomainPCIAddressSetPtr addrs,
> virDomainDeviceInfoPtr dev,
ACK after you take care of the stuff mentioned.
--
Andrea Bolognani / Red Hat / Virtualization
More information about the libvir-list
mailing list