
- when the guest is turned off, a multi function pci device must be attached using mutifunction attribute in the pci address. Otherwise the pci won't be attached correctly. Libvirt doesn't support multifunction hotplug, so we don't change anything if the guest is running. - after any recent libvirt update, the device dettachment must be done just before attaching it into the guest because they query /sys to retrieve some device state. Signed-off-by: Jose Ricardo Ziviani <joserz@linux.vnet.ibm.com> --- src/kimchi/model/vmhostdevs.py | 63 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/src/kimchi/model/vmhostdevs.py b/src/kimchi/model/vmhostdevs.py index 0a263e8..c5ae560 100644 --- a/src/kimchi/model/vmhostdevs.py +++ b/src/kimchi/model/vmhostdevs.py @@ -23,6 +23,7 @@ import os import libvirt from lxml import etree, objectify from lxml.builder import E +from operator import itemgetter from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError from kimchi.exception import OperationFailed @@ -62,6 +63,9 @@ class VMHostDevsModel(object): self._passthrough_device_validate(dev_name) dev_info = DeviceModel(conn=self.conn).lookup(dev_name) + if dev_info['device_type'] == 'pci': + return self._attach_pci_device(vmid, dev_info) + with RollbackContext() as rollback: try: dev = self.conn.get().nodeDeviceLookupByName(dev_name) @@ -79,7 +83,7 @@ class VMHostDevsModel(object): return info - def _get_pci_device_xml(self, dev_info): + def _get_pci_device_xml(self, dev_info, slot, is_multifunction): if 'detach_driver' not in dev_info: dev_info['detach_driver'] = 'kvm' @@ -88,8 +92,29 @@ class VMHostDevsModel(object): slot=str(dev_info['slot']), function=str(dev_info['function']))) driver = E.driver(name=dev_info['detach_driver']) - host_dev = E.hostdev(source, driver, - mode='subsystem', type='pci', managed='yes') + + if is_multifunction: + multi = E.address(type='pci', + domain='0', + bus='0', + slot=str(slot), + function=str(dev_info['function'])) + + if dev_info['function'] == 0: + multi = E.address(type='pci', + domain='0', + bus='0', + slot=str(slot), + function=str(dev_info['function']), + multifunction='on') + + + host_dev = E.hostdev(source, driver, multi, + mode='subsystem', type='pci', managed='yes') + + else: + host_dev = E.hostdev(source, driver, + mode='subsystem', type='pci', managed='yes') return etree.tostring(host_dev) @@ -132,6 +157,27 @@ class VMHostDevsModel(object): return False + def _available_slot(self, dom): + xmlstr = dom.XMLDesc(0) + root = objectify.fromstring(xmlstr) + slots = [] + try: + devices = root.devices + slots = [DeviceModel._toint(dev.attrib['slot']) + for dev in devices.findall('.//address') + if 'slot' in dev.attrib] + + except AttributeError: + return 1 + + slots = sorted(slots) + + for free, slot in enumerate(slots, start=1): + if free < slot: + return free + + return free+1 + def _attach_pci_device(self, vmid, dev_info): self._validate_pci_passthrough_env() @@ -163,6 +209,10 @@ class VMHostDevsModel(object): pci_infos = [dev_model.lookup(dev_name) for dev_name in group_names] pci_infos.append(dev_info) + is_multifunction = len(pci_infos) > 1 and \ + DOM_STATE_MAP[dom.info()[0]] == "shutoff" + pci_infos = sorted(pci_infos, key=itemgetter('name')) + # all devices in the group that is going to be attached to the vm # must be detached from the host first with RollbackContext() as rollback: @@ -181,10 +231,15 @@ class VMHostDevsModel(object): device_flags = get_vm_config_flag(dom, mode='all') + slot = 0 + if is_multifunction: + slot = self._available_slot(dom) with RollbackContext() as rollback: for pci_info in pci_infos: pci_info['detach_driver'] = driver - xmlstr = self._get_pci_device_xml(pci_info) + xmlstr = self._get_pci_device_xml(pci_info, + slot, + is_multifunction) try: dom.attachDeviceFlags(xmlstr, device_flags) except libvirt.libvirtError: -- 1.9.1