Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
i18n.py | 2 +-
model/host.py | 4 +-
model/vmhostdevs.py | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 140 insertions(+), 7 deletions(-)
diff --git a/i18n.py b/i18n.py
index a953b55..008e327 100644
--- a/i18n.py
+++ b/i18n.py
@@ -144,7 +144,7 @@ messages = {
"For AMD CPU, add 'iommu=pt iommu=1'."),
"KCHVMHDEV0004E": _('"name" should be a device name
string'),
"KCHVMHDEV0005E": _('The device %(name)s is probably in use by the
host. Unable to attach it to the guest.'),
- "KCHVMHDEV0006E": _('Hot-plug of device %(name)s is not
supported.'),
+ "KCHVMHDEV0006E": _('Hot-(un)plug of device %(name)s is not
supported.'),
"KCHVMIF0001E": _("Interface %(iface)s does not exist in virtual
machine %(name)s"),
"KCHVMIF0002E": _("Network %(network)s specified for virtual machine
%(name)s does not exist"),
diff --git a/model/host.py b/model/host.py
index 135c8a9..abf1191 100644
--- a/model/host.py
+++ b/model/host.py
@@ -146,9 +146,9 @@ class DevicesModel(object):
class DeviceModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
- self.iommu_groups = self._get_iommu_groups()
+ self.iommu_groups = self.get_iommu_groups()
- def _get_iommu_groups(self):
+ def get_iommu_groups(self):
iommu_groups = defaultdict(list)
conn = self.conn
diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py
index dabe8e4..2a62249 100644
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -22,7 +22,7 @@ import libvirt
import os
import platform
from lxml import etree, objectify
-from lxml.builder import E
+from lxml.builder import E, ElementMaker
from operator import itemgetter
from wok.exception import InvalidOperation, InvalidParameter, NotFoundError
@@ -34,6 +34,12 @@ from wok.plugins.kimchi.model.config import CapabilitiesModel
from wok.plugins.kimchi.model.host import DeviceModel, DevicesModel
from wok.plugins.kimchi.model.utils import get_vm_config_flag
from wok.plugins.kimchi.model.vms import DOM_STATE_MAP, VMModel
+from wok.plugins.kimchi.xmlutils.qemucmdline import get_qemucmdline_xml
+from wok.plugins.kimchi.xmlutils.qemucmdline import QEMU_NAMESPACE
+
+
+CMDLINE_FIELD_NAME = 'spapr-pci-host-bridge.mem_win_size'
+WINDOW_SIZE_BAR = 0x800000000
class VMHostDevsModel(object):
@@ -149,6 +155,7 @@ class VMHostDevsModel(object):
slots = sorted(slots)
+ free = 0
for free, slot in enumerate(slots, start=1):
if free < slot:
return free
@@ -184,8 +191,9 @@ class VMHostDevsModel(object):
DOM_STATE_MAP[dom.info()[0]] == "shutoff"
pci_infos = sorted(pci_infos, key=itemgetter('name'))
- if dev_model.is_device_3D_controller(dev_info) and \
- DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+ # does not allow hot-plug of 3D graphic cards
+ is_3D_device = dev_model.is_device_3D_controller(dev_info)
+ if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
raise InvalidOperation('KCHVMHDEV0006E',
{'name': dev_info['name']})
@@ -207,6 +215,12 @@ class VMHostDevsModel(object):
device_flags = get_vm_config_flag(dom, mode='all')
+ # when attaching a 3D graphic device it might be necessary to increase
+ # the window size memory in order to be able to attach more than one
+ # device to the same guest
+ if is_3D_device:
+ self.update_mmio_guest(vmid, True)
+
slot = 0
if is_multifunction:
slot = self._available_slot(dom)
@@ -229,6 +243,115 @@ class VMHostDevsModel(object):
return dev_info['name']
+ def _count_3D_devices_attached(self, dom):
+ counter = 0
+ root = objectify.fromstring(dom.XMLDesc(0))
+
+ try:
+ hostdev = root.devices.hostdev
+
+ except AttributeError:
+ return counter
+
+ for device in hostdev:
+ if device.attrib['type'] != 'pci':
+ continue
+
+ name = DeviceModel.deduce_dev_name(device, self.conn)
+ info = DeviceModel(conn=self.conn).lookup(name)
+ if 'vga3d' in info and info['vga3d']:
+ counter += 1
+
+ return counter
+
+ def update_mmio_guest(self, vmid, is_attaching):
+ dom = VMModel.get_vm(vmid, self.conn)
+ # get the number of 3D graphic cards already attached to the guest
+ # based on this number we will decide if the memory size will be
+ # increased or not
+ counter = self._count_3D_devices_attached(dom)
+ if counter == 0 and is_attaching:
+ return
+
+ size = 0
+ if is_attaching:
+ # suppose this is the 3rd graphic card to be attached to the same
+ # guest, counter will be 2+1 (2 existing + this attachment) times
+ # 32G (0x80000000)
+ size = hex((counter + 1) * WINDOW_SIZE_BAR)
+
+ else:
+ size = hex(counter * WINDOW_SIZE_BAR)
+
+ # if the guest already has the xml file we will simply update the
+ # value, otherwise we will add the new field
+ new_xml = self._update_win_memory_size(dom, counter, size)
+ if new_xml is None and is_attaching:
+ new_xml = self._add_win_memory_size(dom, size)
+
+ # update the XML
+ self.conn.get().defineXML(new_xml)
+
+ def _update_win_memory_size(self, dom, counter, wnd_size):
+ root = objectify.fromstring(dom.XMLDesc(0))
+
+ # look for the existing argument in <qemu:commandline> and try
+ # to update the value (or remove if there is only one (or none)
+ # graphic card attached.
+ cmdline = root.findall('{%s}commandline' % QEMU_NAMESPACE)
+ for line in cmdline:
+ for arg in line.iterchildren():
+ if not arg.values()[0].startswith(CMDLINE_FIELD_NAME):
+ continue
+
+ # update mem_win_size value
+ if counter > 1:
+ arg.set('value', CMDLINE_FIELD_NAME + '=' +
wnd_size)
+
+ # remove mem_win_size
+ elif counter <= 1:
+ line.remove(arg)
+
+ return etree.tostring(root, encoding='utf-8',
+ pretty_print=True)
+
+ return None
+
+ def _add_win_memory_size(self, dom, wnd_size):
+ root = objectify.fromstring(dom.XMLDesc(0))
+ val = CMDLINE_FIELD_NAME + '=' + wnd_size
+
+ cmdline = root.find('{%s}commandline' % QEMU_NAMESPACE)
+ # <qemu:commandline> doesn't exist, create the full commandline xml
+ # with the required values and return
+ if cmdline is None:
+ args = {}
+ args['-global'] = val
+ root.append(etree.fromstring(get_qemucmdline_xml(args)))
+ return etree.tostring(root, encoding='utf-8', pretty_print=True)
+
+ # <qemu:commandline> exists and already has the tag
+ # <qemu:arg value='-global'> (user could already been using this
for
+ # something else), so we just add our <qemu:arg...> missing.
+ found = False
+ for arg in cmdline.iterchildren():
+ if arg.values()[0] == '-global':
+ EM = ElementMaker(namespace=QEMU_NAMESPACE,
+ nsmap={'qemu': QEMU_NAMESPACE})
+ cmdline.append(EM.arg(value=val))
+ found = True
+ break
+
+ # <qemu:commandline> exists but there is no <qemu:arg value global>
+ # so, we add those missing arguments inside the exising cmdline
+ if not found:
+ EM = ElementMaker(namespace=QEMU_NAMESPACE,
+ nsmap={'qemu': QEMU_NAMESPACE})
+ cmdline.append(EM.arg(value='-global'))
+ cmdline.append(EM.arg(value=val))
+
+ return etree.tostring(root, encoding='utf-8', pretty_print=True)
+
def _get_scsi_device_xml(self, dev_info):
adapter = E.adapter(name=('scsi_host%s' % dev_info['host']))
address = E.address(type='scsi', bus=str(dev_info['bus']),
@@ -285,7 +408,8 @@ class VMHostDevModel(object):
'type': e.attrib['type'],
'product': dev_info.get('product', None),
'vendor': dev_info.get('vendor', None),
- 'multifunction': dev_info.get('multifunction',
None)}
+ 'multifunction': dev_info.get('multifunction',
None),
+ 'vga3d': dev_info.get('vga3d', None)}
raise NotFoundError('KCHVMHDEV0001E',
{'vmid': vmid, 'dev_name': dev_name})
@@ -304,6 +428,13 @@ class VMHostDevModel(object):
pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e)
for e in hostdev if e.attrib['type'] == 'pci']
+ dev_model = DeviceModel(conn=self.conn)
+ dev_info = dev_model.lookup(dev_name)
+ is_3D_device = dev_model.is_device_3D_controller(dev_info)
+ if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+ raise InvalidOperation('KCHVMHDEV0006E',
+ {'name': dev_info['name']})
+
for e in hostdev:
if DeviceModel.deduce_dev_name(e, self.conn) == dev_name:
xmlstr = etree.tostring(e)
@@ -311,6 +442,8 @@ class VMHostDevModel(object):
xmlstr, get_vm_config_flag(dom, mode='all'))
if e.attrib['type'] == 'pci':
self._delete_affected_pci_devices(dom, dev_name, pci_devs)
+ if is_3D_device:
+ self.update_mmio_guest(vmid, False)
break
else:
raise NotFoundError('KCHVMHDEV0001E',
--
1.9.1