This patch adds a '_passthrough=1' filter to /host/devices, so it can
filter and shows all devices eligible to passthrough to guest.
Theoretically, all PCI, USB and SCSI devices can be assigned to guest
directly. However usually all host devices form a tree, if we assign a
PCI port/SCSI controller/USB controller to guest, all devices/disks
under the controller are assigned as well. In this patch we only present
the "leaf" host devices to the user as potential passthrough devices.
In other word, the possible devices are wireless network interface, SD
card reader, camera, SCSI unit(disk or CD), and so on.
Linux kernel is able to recognize the host IOMMU group layout. If two
PCI devices are in the same IOMMU group, it means there are possible
interconnections between the devices, and the devices can talk to each
other bypassing IOMMU. This implies isolation is not pefect between those
devices, so all devices in a IOMMU group must be assigned to guest
together. On host that recognizes IOMMU groups, by accessing the URI
/host/devices/deviceX/passthrough_affected_devices, it returns a list
containing the devices in the same IOMMU group as deviceX.
How to test:
List all types of devices to passthrough
curl -k -u root -H "Content-Type: application/json" \
-H "Accept: application/json" \
'https://127.0.0.1:8001/host/devices?_passthrough=1'
List all eligible PCI devices to passthrough
'https://127.0.0.1:8001/host/devices?_passthrough=1&_cap=pci'
List all USB devices to passthrough
'https://127.0.0.1:8001/host/devices?_passthrough=1&_cap=usb_device'
List all SCSI devices to passthrough
'https://127.0.0.1:8001/host/devices?_passthrough=1&_cap=scsi'
List devices in the same IOMMU group as pci_0000_00_19_0
'https://127.0.0.1:8001/host/devices/pci_0000_00_19_0/passthrough_affected_devices'
v1:
v1 series does not contain this patch.
v2:
Deal with calculation "leaf" device and "affected" device.
v3:
No change.
Signed-off-by: Zhou Zheng Sheng <zhshzhou(a)linux.vnet.ibm.com>
---
src/kimchi/control/host.py | 9 ++++++++
src/kimchi/hostdev.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++
src/kimchi/model/host.py | 17 ++++++++++++++-
3 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py
index ebf1bed..15f2343 100644
--- a/src/kimchi/control/host.py
+++ b/src/kimchi/control/host.py
@@ -103,9 +103,18 @@ class Devices(Collection):
self.resource = Device
+class PassthroughAffectedDevices(Collection):
+ def __init__(self, model, device_id):
+ super(PassthroughAffectedDevices, self).__init__(model)
+ self.resource = Device
+ self.model_args = (device_id, )
+
+
class Device(Resource):
def __init__(self, model, id):
super(Device, self).__init__(model, id)
+ self.passthrough_affected_devices = \
+ PassthroughAffectedDevices(self.model, id)
@property
def data(self):
diff --git a/src/kimchi/hostdev.py b/src/kimchi/hostdev.py
index d4c142d..a9c40e8 100644
--- a/src/kimchi/hostdev.py
+++ b/src/kimchi/hostdev.py
@@ -46,6 +46,57 @@ def _get_dev_info_tree(dev_infos):
return root
+def get_passthrough_dev_infos():
+ ''' Get devices eligible to be passed through to VM. '''
+
+ def strip_parents(devs, dev):
+ parent = dev['parent']
+ while parent is not None:
+ try:
+ parent = devs.pop(parent)['parent']
+ except KeyError:
+ break
+
+ dev_infos = _get_all_host_dev_infos()
+ devs = dict([(dev_info['name'], dev_info) for dev_info in dev_infos])
+
+ for dev in dev_infos:
+ if dev['device_type'] in ('pci', 'usb_device',
'scsi'):
+ strip_parents(devs, dev)
+
+ return [dev for dev in devs.itervalues()
+ if dev['device_type'] in ('pci', 'usb_device',
'scsi') and
+ dev.get('driver', {}).get('name', '') !=
'pcieport']
+
+
+def get_affected_passthrough_devices(passthrough_dev):
+ devs = dict([(dev['name'], dev) for dev in _get_all_host_dev_infos()])
+
+ def get_iommu_group(dev_info):
+ try:
+ return int(dev_info['iommuGroup'])
+ except KeyError:
+ pass
+
+ parent = dev_info['parent']
+ while parent is not None:
+ try:
+ iommuGroup = int(devs[parent]['iommuGroup'])
+ except KeyError:
+ pass
+ else:
+ return iommuGroup
+ parent = devs[parent]['parent']
+
+ return -1
+
+ iommu_group = get_iommu_group(passthrough_dev)
+
+ return [dev for dev in get_passthrough_dev_infos()
+ if dev['name'] != passthrough_dev['name'] and
+ get_iommu_group(dev) == iommu_group]
+
+
def get_dev_info(node_dev):
''' Parse the node device XML string into dict according to
http://libvirt.org/formatnode.html. '''
@@ -206,4 +257,7 @@ def _format_dev_node(node):
if __name__ == '__main__':
+ from pprint import pprint
_print_host_dev_tree()
+ print 'Eligible passthrough devices:'
+ pprint(get_passthrough_dev_infos())
diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py
index f4bd613..7022566 100644
--- a/src/kimchi/model/host.py
+++ b/src/kimchi/model/host.py
@@ -253,7 +253,7 @@ class DevicesModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
- def get_list(self, _cap=None):
+ def get_list(self, _cap=None, _passthrough=None):
conn = self.conn.get()
if _cap is None:
dev_names = [name.name() for name in conn.listAllDevices(0)]
@@ -262,6 +262,11 @@ class DevicesModel(object):
else:
# Get devices with required capability
dev_names = conn.listDevices(_cap, 0)
+
+ if _passthrough is not None and bool(int(_passthrough)):
+ passthrough_names = [
+ dev['name'] for dev in hostdev.get_passthrough_dev_infos()]
+ dev_names = list(set(dev_names) & set(passthrough_names))
return dev_names
def _get_devices_fc_host(self):
@@ -279,6 +284,16 @@ class DevicesModel(object):
return conn.listDevices('fc_host', 0)
+class PassthroughAffectedDevicesModel(object):
+ def __init__(self, **kargs):
+ self.conn = kargs['conn']
+
+ def get_list(self, device_id):
+ dev_info = DeviceModel(conn=self.conn).lookup(device_id)
+ affected = hostdev.get_affected_passthrough_devices(dev_info)
+ return [dev['name'] for dev in affected]
+
+
class DeviceModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
--
1.9.0