[libvirt] [PATCH 4/5] qemu: set/validate slot/connection type when assigning slots for PCI devices

Laine Stump laine at laine.org
Tue Jul 23 20:37:20 UTC 2013


On 07/23/2013 11:31 AM, Daniel P. Berrange wrote:
> On Tue, Jul 23, 2013 at 10:44:54AM -0400, Laine Stump wrote:
>> diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
>> index 059aa6a..64787b6 100644
>> --- a/src/qemu/qemu_command.c
>> +++ b/src/qemu/qemu_command.c
>> @@ -1412,7 +1412,15 @@ cleanup:
>>  #define QEMU_PCI_ADDRESS_FUNCTION_LAST 7
>>  
>>  typedef struct {
>> -    /* Each bit in a slot represents one function on that slot */
>> +    virDomainControllerModelPCI model;
>> +    /* flags an min/max can be computed from model, but
>> +     * having them ready makes life easier.
>> +     */
>> +    qemuDomainPCIConnectFlags flags;
>> +    size_t minSlot, maxSlot; /* usually 0,0 or 1,31 */
>> +    /* Each bit in a slot represents one function on that slot. If the
>> +     * bit is set, that function is in use by a device.
>> +     */
>>      uint8_t slots[QEMU_PCI_ADDRESS_SLOT_LAST + 1];
>>  } qemuDomainPCIAddressBus;
>>  typedef qemuDomainPCIAddressBus *qemuDomainPCIAddressBusPtr;
>> @@ -1430,9 +1438,13 @@ struct _qemuDomainPCIAddressSet {
>>   * Check that the PCI address is valid for use
>>   * with the specified PCI address set.
>>   */
>> -static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UNUSED,
>> -                                   virDevicePCIAddressPtr addr)
>> +static bool
>> +qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UNUSED,
>> +                       virDevicePCIAddressPtr addr,
>> +                       qemuDomainPCIConnectFlags flags)
>>  {
>> +    qemuDomainPCIAddressBusPtr bus;
>> +
>>      if (addrs->nbuses == 0) {
>>          virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses available"));
>>          return false;
>> @@ -1448,32 +1460,81 @@ static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UN
>>                         addrs->nbuses - 1);
>>          return false;
>>      }
>> -    if (addr->function > QEMU_PCI_ADDRESS_FUNCTION_LAST) {
>> +
>> +    bus = &addrs->buses[addr->bus];
>> +
>> +    /* assure that at least one of the requested connection types is
>> +     * provided by this bus
>> +     */
>> +    if (!(flags & bus->flags & QEMU_PCI_CONNECT_TYPES_MASK)) {
>>          virReportError(VIR_ERR_XML_ERROR,
>> -                       _("Invalid PCI address: function must be <= %u"),
>> -                       QEMU_PCI_ADDRESS_FUNCTION_LAST);
>> +                       _("Invalid PCI address: The PCI controller "
>> +                         "providing bus %04x doesn't support "
>> +                         "connections appropriate for the device "
>> +                         "(%x required vs. %x provided by bus)"),
>> +                       addr->bus, flags & QEMU_PCI_CONNECT_TYPES_MASK,
>> +                       bus->flags & QEMU_PCI_CONNECT_TYPES_MASK);
>>          return false;
>>      }
>> -    if (addr->slot > QEMU_PCI_ADDRESS_SLOT_LAST) {
>> +    /* make sure this bus allows hot-plug if the caller demands it */
>> +    if ((flags & QEMU_PCI_CONNECT_HOTPLUGGABLE) &&
>> +        !(bus->flags &  QEMU_PCI_CONNECT_HOTPLUGGABLE)) {
>>          virReportError(VIR_ERR_XML_ERROR,
>> -                       _("Invalid PCI address: slot must be <= %u"),
>> -                       QEMU_PCI_ADDRESS_SLOT_LAST);
>> +                       _("Invalid PCI address: hot-pluggable slot requested, "
>> +                         "but bus %04x doesn't support hot-plug"), addr->bus);
>>          return false;
>>      }
>> -    if (addr->slot == 0) {
>> -        if (addr->bus) {
>> -            virReportError(VIR_ERR_XML_ERROR, "%s",
>> -                           _("Slot 0 is unusable on PCI bridges"));
>> -        } else {
>> -            virReportError(VIR_ERR_XML_ERROR, "%s",
>> -                           _("Slot 0 on bus 0 is reserved for the host bridge"));
>> -        }
>> +    /* some "buses" are really just a single port */
>> +    if (bus->minSlot && addr->slot < bus->minSlot) {
>> +        virReportError(VIR_ERR_XML_ERROR,
>> +                       _("Invalid PCI address: slot must be >= %zu"),
>> +                       bus->minSlot);
>> +        return false;
>> +    }
>> +    if (addr->slot > bus->maxSlot) {
>> +        virReportError(VIR_ERR_XML_ERROR,
>> +                       _("Invalid PCI address: slot must be <= %zu"),
>> +                       bus->maxSlot);
>> +        return false;
>> +    }
>> +    if (addr->function > QEMU_PCI_ADDRESS_FUNCTION_LAST) {
>> +        virReportError(VIR_ERR_XML_ERROR,
>> +                       _("Invalid PCI address: function must be <= %u"),
>> +                       QEMU_PCI_ADDRESS_FUNCTION_LAST);
>
>> +
>> +static int
>> +qemuDomainPCIAddressBusSetModel(qemuDomainPCIAddressBusPtr bus,
>> +                                virDomainControllerModelPCI model)
>> +{
>> +    switch (model) {
>> +    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
>> +    case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
>> +        bus->flags = (QEMU_PCI_CONNECT_HOTPLUGGABLE |
>> +                      QEMU_PCI_CONNECT_TYPE_PCI);
>> +        bus->minSlot = 1;
>> +        bus->maxSlot = QEMU_PCI_ADDRESS_SLOT_LAST;
>> +        break;
>> +    default:
>> +        virReportError(VIR_ERR_INTERNAL_ERROR,
>> +                       _("Invalid PCI controller model %d"), model);
>> +        return -1;
>> +    }
>> +
>> +    bus->model = model;
>> +    return 0;
>> +}
>> +
>> +
>>  /* Ensure addr fits in the address set, by expanding it if needed.
>> + * This will only grow if the flags say that we need a normal
>> + * hot-pluggable PCI slot. If we need a different type of slot, it
>> + * will fail.
>> + *
>>   * Return value:
>>   * -1 = OOM
>>   *  0 = no action performed
>> @@ -1481,7 +1542,8 @@ static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UN
>>   */
>>  static int
>>  qemuDomainPCIAddressSetGrow(qemuDomainPCIAddressSetPtr addrs,
>> -                            virDevicePCIAddressPtr addr)
>> +                            virDevicePCIAddressPtr addr,
>> +                            qemuDomainPCIConnectFlags flags)
>>  {
>>      int add;
>>      size_t i;
>> @@ -1490,16 +1552,33 @@ qemuDomainPCIAddressSetGrow(qemuDomainPCIAddressSetPtr addrs,
>>      i = addrs->nbuses;
>>      if (add <= 0)
>>          return 0;
>> +
>> +    /* auto-grow only works when we're adding plain PCI devices */
>> +    if (!(flags & QEMU_PCI_CONNECT_TYPE_PCI)) {
>> +        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> +                       _("Cannot automatically add a new PCI bus for a "
>> +                         "device requiring a slot other than standard PCI."));
>> +        return -1;
>> +    }
>> +
>>      if (VIR_EXPAND_N(addrs->buses, addrs->nbuses, add) < 0)
>>          return -1;
>> -    /* reserve slot 0 on the new buses */
>> -    for (; i < addrs->nbuses; i++)
>> -        addrs->buses[i].slots[0] = 0xFF;
>> +
>> +    for (; i < addrs->nbuses; i++) {
>> +        /* Any time we auto-add a bus, we will want a multi-slot
>> +         * bus. Currently the only type of bus we will auto-add is a
>> +         * pci-bridge, which is hot-pluggable and provides standard
>> +         * PCI slots.
>> +         */
>> +        qemuDomainPCIAddressBusSetModel(&addrs->buses[i],
>> +                                        VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE);
>> +    }
>>      return add;
> In this old code we're setting slot 0 to 0xFF to reserve it,
> and qemuDomainPCIAddressBusSetModel does not do that. I
> think that's ok, since qemuPCIAddressValidate uses minSlot
> now, but wanted to check that this was correct

Yes, that's correct. I had originally left that code in (setting slot 0
to 0xFF) just to be paranoid, but later convinced myself I could remove
it immediately rather than leaving it around to confound someone else in
6 months.

>
>
> ACK if you can answer that q.
>
> Definitely could do with some test coverage in the XML -> ARGV convertor
> for complex cases with multiple bridges in the XML

Yes. I'm going to add a slightly more compact version of the "287 virtio
disk devices" test case from the BZ for pci-bridge.

Also I think we need more test cases where the original XML has no
guest-side PCI addresses specified, and we put an "xmlout" version in
qemuxml2xmloutdata with pci addresses filled in; that way we can verify
that the auto-allocation of slots doesn't regress.

Thanks for the review. I'll add some test cases and push later tonight.




More information about the libvir-list mailing list