mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
XenAPI: Add Physical PCI Device (PPCI) Support
Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
This commit is contained in:
1 parent
fec35adc86
commit
5a940c0997
4 files changed
+334
-6
No files matched your search
@@ -8,6 +8,8 @@
|
||||
import sys
|
||||
import os, os.path
|
||||
import resource
|
||||
import re
|
||||
import types
|
||||
|
||||
PROC_MNT_PATH = '/proc/mounts'
|
||||
PROC_PCI_PATH = '/proc/bus/pci/devices'
|
||||
@@ -22,6 +24,9 @@ SYSFS_PCI_DEV_VENDOR_PATH = '/vendor'
|
||||
SYSFS_PCI_DEV_DEVICE_PATH = '/device'
|
||||
SYSFS_PCI_DEV_SUBVENDOR_PATH = '/subsystem_vendor'
|
||||
SYSFS_PCI_DEV_SUBDEVICE_PATH = '/subsystem_device'
|
||||
SYSFS_PCI_DEV_CLASS_PATH = '/class'
|
||||
|
||||
LSPCI_CMD = 'lspci'
|
||||
|
||||
PCI_BAR_IO = 0x01
|
||||
PCI_BAR_IO_MASK = ~0x03
|
||||
@@ -32,6 +37,9 @@ PCI_CAP_OFFSET = 0x34
|
||||
MSIX_BIR_MASK = 0x7
|
||||
MSIX_SIZE_MASK = 0x7ff
|
||||
|
||||
# Global variable to store information from lspci
|
||||
lspci_info = None
|
||||
|
||||
#Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
|
||||
PAGE_SIZE = resource.getpagesize()
|
||||
PAGE_SHIFT = 0
|
||||
@@ -45,6 +53,15 @@ PAGE_MASK=~(PAGE_SIZE - 1)
|
||||
def PCI_DEVFN(slot, func):
|
||||
return ((((slot) & 0x1f) << 3) | ((func) & 0x07))
|
||||
|
||||
def parse_hex(val):
|
||||
try:
|
||||
if isinstance(val, types.StringTypes):
|
||||
return int(val, 16)
|
||||
else:
|
||||
return val
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def find_sysfs_mnt():
|
||||
mounts_file = open(PROC_MNT_PATH,'r')
|
||||
|
||||
@@ -58,6 +75,61 @@ def find_sysfs_mnt():
|
||||
|
||||
return None
|
||||
|
||||
def get_all_pci_names():
|
||||
try:
|
||||
sysfs_mnt = find_sysfs_mnt()
|
||||
except IOError, (errno, strerr):
|
||||
raise PciDeviceParseError(('Failed to locate sysfs mount: %s (%d)' %
|
||||
(PROC_PCI_PATH, strerr, errno)))
|
||||
|
||||
pci_names = os.popen('ls ' + sysfs_mnt + SYSFS_PCI_DEVS_PATH).read().split()
|
||||
|
||||
return pci_names
|
||||
|
||||
def get_all_pci_devices():
|
||||
pci_devs = []
|
||||
for pci_name in get_all_pci_names():
|
||||
pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
|
||||
r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
|
||||
r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
|
||||
r"(?P<func>[0-7])$", pci_name)
|
||||
if pci_match is None:
|
||||
raise PciDeviceParseError(('Failed to parse pci device name: %s' %
|
||||
pci_name))
|
||||
pci_dev_info = pci_match.groupdict('0')
|
||||
domain = parse_hex(pci_dev_info['domain'])
|
||||
bus = parse_hex(pci_dev_info['bus'])
|
||||
slot = parse_hex(pci_dev_info['slot'])
|
||||
func = parse_hex(pci_dev_info['func'])
|
||||
try:
|
||||
pci_dev = PciDevice(domain, bus, slot, func)
|
||||
except:
|
||||
continue
|
||||
pci_devs.append(pci_dev)
|
||||
|
||||
return pci_devs
|
||||
|
||||
def create_lspci_info():
|
||||
global lspci_info
|
||||
lspci_info = {}
|
||||
|
||||
# Execute 'lspci' command and parse the result.
|
||||
# If the command does not exist, lspci_info will be kept blank ({}).
|
||||
for paragraph in os.popen(LSPCI_CMD + ' -vmmD').read().split('\n\n'):
|
||||
device_name = None
|
||||
device_info = {}
|
||||
for line in paragraph.split('\n'):
|
||||
try:
|
||||
(opt, value) = line.split(':\t')
|
||||
if opt == 'Slot':
|
||||
device_name = value
|
||||
else:
|
||||
device_info[opt] = value
|
||||
except:
|
||||
pass
|
||||
if device_name is not None:
|
||||
lspci_info[device_name] = device_info
|
||||
|
||||
class PciDeviceNotFoundError(Exception):
|
||||
def __init__(self,domain,bus,slot,func):
|
||||
self.domain = domain
|
||||
@@ -92,7 +164,15 @@ class PciDevice:
|
||||
self.subdevice = None
|
||||
self.msix = 0
|
||||
self.msix_iomem = []
|
||||
self.revision = 0
|
||||
self.classcode = None
|
||||
self.vendorname = ""
|
||||
self.devicename = ""
|
||||
self.classname = ""
|
||||
self.subvendorname = ""
|
||||
self.subdevicename = ""
|
||||
self.get_info_from_sysfs()
|
||||
self.get_info_from_lspci()
|
||||
|
||||
def find_capability(self, type):
|
||||
try:
|
||||
@@ -208,9 +288,8 @@ class PciDevice:
|
||||
self.name+SYSFS_PCI_DEV_DRIVER_DIR_PATH
|
||||
try:
|
||||
self.driver = os.path.basename(os.readlink(path))
|
||||
except IOError, (errno, strerr):
|
||||
raise PciDeviceParseError(('Failed to read %s: %s (%d)' %
|
||||
(path, strerr, errno)))
|
||||
except OSError, (errno, strerr):
|
||||
self.driver = ""
|
||||
|
||||
path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
|
||||
self.name+SYSFS_PCI_DEV_VENDOR_PATH
|
||||
@@ -244,6 +323,36 @@ class PciDevice:
|
||||
raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
|
||||
(path, strerr, errno)))
|
||||
|
||||
path = sysfs_mnt+SYSFS_PCI_DEVS_PATH+'/'+ \
|
||||
self.name+SYSFS_PCI_DEV_CLASS_PATH
|
||||
try:
|
||||
self.classcode = int(open(path,'r').readline(), 16)
|
||||
except IOError, (errno, strerr):
|
||||
raise PciDeviceParseError(('Failed to open & read %s: %s (%d)' %
|
||||
(path, strerr, errno)))
|
||||
|
||||
return True
|
||||
|
||||
def get_info_from_lspci(self):
|
||||
""" Get information such as vendor name, device name, class name, etc.
|
||||
Since we cannot obtain these data from sysfs, use 'lspci' command.
|
||||
"""
|
||||
global lspci_info
|
||||
|
||||
if lspci_info is None:
|
||||
create_lspci_info()
|
||||
|
||||
try:
|
||||
device_info = lspci_info[self.name]
|
||||
self.revision = int(device_info['Rev'], 16)
|
||||
self.vendorname = device_info['Vendor']
|
||||
self.devicename = device_info['Device']
|
||||
self.classname = device_info['Class']
|
||||
self.subvendorname = device_info['SVendor']
|
||||
self.subdevicename = device_info['SDevice']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -40,6 +40,7 @@ from XendPIFMetrics import XendPIFMetrics
|
||||
from XendVMMetrics import XendVMMetrics
|
||||
from XendPIF import XendPIF
|
||||
from XendPBD import XendPBD
|
||||
from XendPPCI import XendPPCI
|
||||
from XendXSPolicy import XendXSPolicy, XendACMPolicy
|
||||
|
||||
from XendAPIConstants import *
|
||||
@@ -476,7 +477,8 @@ classes = {
|
||||
'PIF' : valid_object("PIF"),
|
||||
'VM_metrics' : valid_object("VM_metrics"),
|
||||
'PBD' : valid_object("PBD"),
|
||||
'PIF_metrics' : valid_object("PIF_metrics")
|
||||
'PIF_metrics' : valid_object("PIF_metrics"),
|
||||
'PPCI' : valid_object("PPCI"),
|
||||
}
|
||||
|
||||
autoplug_classes = {
|
||||
@@ -485,6 +487,7 @@ autoplug_classes = {
|
||||
'VM_metrics' : XendVMMetrics,
|
||||
'PBD' : XendPBD,
|
||||
'PIF_metrics' : XendPIFMetrics,
|
||||
'PPCI' : XendPPCI,
|
||||
'XSPolicy' : XendXSPolicy,
|
||||
'ACMPolicy' : XendACMPolicy,
|
||||
}
|
||||
@@ -874,6 +877,7 @@ class XendAPI(object):
|
||||
'resident_VMs',
|
||||
'PBDs',
|
||||
'PIFs',
|
||||
'PPCIs',
|
||||
'host_CPUs',
|
||||
'cpu_configuration',
|
||||
'metrics',
|
||||
@@ -952,6 +956,8 @@ class XendAPI(object):
|
||||
return xen_api_success(XendPBD.get_all())
|
||||
def host_get_PIFs(self, session, ref):
|
||||
return xen_api_success(XendNode.instance().get_PIF_refs())
|
||||
def host_get_PPCIs(self, session, ref):
|
||||
return xen_api_success(XendNode.instance().get_PPCI_refs())
|
||||
def host_get_host_CPUs(self, session, host_ref):
|
||||
return xen_api_success(XendNode.instance().get_host_cpu_refs())
|
||||
def host_get_metrics(self, _, ref):
|
||||
@@ -1027,7 +1033,8 @@ class XendAPI(object):
|
||||
'sched_policy': node.get_vcpus_policy(),
|
||||
'logging': {},
|
||||
'PIFs': XendPIF.get_all(),
|
||||
'PBDs': XendPBD.get_all()}
|
||||
'PBDs': XendPBD.get_all(),
|
||||
'PPCIs': XendPPCI.get_all()}
|
||||
return xen_api_success(record)
|
||||
|
||||
# class methods
|
||||
@@ -1288,7 +1295,7 @@ class XendAPI(object):
|
||||
def VM_get_consoles(self, session, vm_ref):
|
||||
dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
|
||||
return xen_api_success(dom.get_consoles())
|
||||
|
||||
|
||||
def VM_get_tools_version(self, session, vm_ref):
|
||||
dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
|
||||
return dom.get_tools_version()
|
||||
|
||||
@@ -21,6 +21,7 @@ import socket
|
||||
import xen.lowlevel.xc
|
||||
|
||||
from xen.util import Brctl
|
||||
from xen.util import pci as PciUtil
|
||||
from xen.xend import XendAPIStore
|
||||
|
||||
import uuid, arch
|
||||
@@ -35,6 +36,7 @@ from XendPIFMetrics import XendPIFMetrics
|
||||
from XendNetwork import *
|
||||
from XendStateStore import XendStateStore
|
||||
from XendMonitor import XendMonitor
|
||||
from XendPPCI import XendPPCI
|
||||
|
||||
class XendNode:
|
||||
"""XendNode - Represents a Domain 0 Host."""
|
||||
@@ -49,6 +51,7 @@ class XendNode:
|
||||
* PIF_metrics
|
||||
* network
|
||||
* Storage Repository
|
||||
* PPCI
|
||||
"""
|
||||
|
||||
self.xc = xen.lowlevel.xc.xc()
|
||||
@@ -230,6 +233,41 @@ class XendNode:
|
||||
except CreateUnspecifiedAttributeError:
|
||||
log.warn("Error recreating PBD %s", pbd_uuid)
|
||||
|
||||
|
||||
# Initialise PPCIs
|
||||
saved_ppcis = self.state_store.load_state('ppci')
|
||||
saved_ppci_table = {}
|
||||
if saved_ppcis:
|
||||
for ppci_uuid, ppci_record in saved_ppcis.items():
|
||||
try:
|
||||
saved_ppci_table[ppci_record['name']] = ppci_uuid
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for pci_dev in PciUtil.get_all_pci_devices():
|
||||
ppci_record = {
|
||||
'domain': pci_dev.domain,
|
||||
'bus': pci_dev.bus,
|
||||
'slot': pci_dev.slot,
|
||||
'func': pci_dev.func,
|
||||
'vendor_id': pci_dev.vendor,
|
||||
'vendor_name': pci_dev.vendorname,
|
||||
'device_id': pci_dev.device,
|
||||
'device_name': pci_dev.devicename,
|
||||
'revision_id': pci_dev.revision,
|
||||
'class_code': pci_dev.classcode,
|
||||
'class_name': pci_dev.classname,
|
||||
'subsystem_vendor_id': pci_dev.subvendor,
|
||||
'subsystem_vendor_name': pci_dev.subvendorname,
|
||||
'subsystem_id': pci_dev.subdevice,
|
||||
'subsystem_name': pci_dev.subdevicename,
|
||||
'driver': pci_dev.driver
|
||||
}
|
||||
# If saved uuid exists, use it. Otherwise create one.
|
||||
ppci_uuid = saved_ppci_table.get(pci_dev.name, uuid.createString())
|
||||
XendPPCI(ppci_uuid, ppci_record)
|
||||
|
||||
|
||||
## def network_destroy(self, net_uuid):
|
||||
## del self.networks[net_uuid]
|
||||
## self.save_networks()
|
||||
@@ -272,6 +310,15 @@ class XendNode:
|
||||
## self.save_PIFs()
|
||||
|
||||
|
||||
def get_PPCI_refs(self):
|
||||
return XendPPCI.get_all()
|
||||
|
||||
def get_ppci_by_uuid(self, ppci_uuid):
|
||||
if ppci_uuid in self.get_PPCI_refs():
|
||||
return ppci_uuid
|
||||
return None
|
||||
|
||||
|
||||
def save(self):
|
||||
# save state
|
||||
host_record = {self.uuid: {'name_label':self.name,
|
||||
@@ -284,6 +331,7 @@ class XendNode:
|
||||
self.save_networks()
|
||||
self.save_PBDs()
|
||||
self.save_SRs()
|
||||
self.save_PPCIs()
|
||||
|
||||
def save_PIFs(self):
|
||||
pif_records = dict([(pif_uuid, XendAPIStore.get(
|
||||
@@ -308,6 +356,12 @@ class XendNode:
|
||||
for k, v in self.srs.items()])
|
||||
self.state_store.save_state('sr', sr_records)
|
||||
|
||||
def save_PPCIs(self):
|
||||
ppci_records = dict([(ppci_uuid, XendAPIStore.get(
|
||||
ppci_uuid, "PPCI").get_record())
|
||||
for ppci_uuid in XendPPCI.get_all()])
|
||||
self.state_store.save_state('ppci', ppci_records)
|
||||
|
||||
def shutdown(self):
|
||||
return 0
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (c) 2008 NEC Corporation
|
||||
# Yosuke Iwamatsu <y-iwamatsu at ab jp nec com>
|
||||
#============================================================================
|
||||
|
||||
from xen.xend.XendBase import XendBase
|
||||
from xen.xend.XendBase import XendAPIStore
|
||||
from xen.xend import uuid as genuuid
|
||||
|
||||
class XendPPCI(XendBase):
|
||||
"""Representation of a physical PCI device."""
|
||||
|
||||
def getClass(self):
|
||||
return "PPCI"
|
||||
|
||||
def getAttrRO(self):
|
||||
attrRO = ['host',
|
||||
'domain',
|
||||
'bus',
|
||||
'slot',
|
||||
'func',
|
||||
'name',
|
||||
'vendor_id',
|
||||
'vendor_name',
|
||||
'device_id',
|
||||
'device_name',
|
||||
'revision_id',
|
||||
'class_code',
|
||||
'class_name',
|
||||
'subsystem_vendor_id',
|
||||
'subsystem_vendor_name',
|
||||
'subsystem_id',
|
||||
'subsystem_name',
|
||||
'driver']
|
||||
return XendBase.getAttrRO() + attrRO
|
||||
|
||||
def getAttrRW(self):
|
||||
attrRW = []
|
||||
return XendBase.getAttrRW() + attrRW
|
||||
|
||||
def getAttrInst(self):
|
||||
attrInst = []
|
||||
return XendBase.getAttrInst() + attrInst
|
||||
|
||||
def getMethods(self):
|
||||
methods = []
|
||||
return XendBase.getMethods() + methods
|
||||
|
||||
def getFuncs(self):
|
||||
funcs = []
|
||||
return XendBase.getFuncs() + funcs
|
||||
|
||||
getClass = classmethod(getClass)
|
||||
getAttrRO = classmethod(getAttrRO)
|
||||
getAttrRW = classmethod(getAttrRW)
|
||||
getAttrInst = classmethod(getAttrInst)
|
||||
getMethods = classmethod(getMethods)
|
||||
getFuncs = classmethod(getFuncs)
|
||||
|
||||
def get_by_sbdf(self, domain, bus, slot, func):
|
||||
for ppci in XendAPIStore.get_all("PPCI"):
|
||||
if ppci.get_domain() == int(domain, 16) and \
|
||||
ppci.get_bus() == int(bus, 16) and \
|
||||
ppci.get_slot() == int(slot, 16) and \
|
||||
ppci.get_func() == int(func, 16):
|
||||
return ppci.get_uuid()
|
||||
return None
|
||||
|
||||
get_by_sbdf = classmethod(get_by_sbdf)
|
||||
|
||||
def __init__(self, uuid, record):
|
||||
self.domain = record['domain']
|
||||
self.bus = record['bus']
|
||||
self.slot = record['slot']
|
||||
self.func = record['func']
|
||||
self.vendor_id = record['vendor_id']
|
||||
self.vendor_name = record['vendor_name']
|
||||
self.device_id = record['device_id']
|
||||
self.device_name = record['device_name']
|
||||
self.revision_id = record['revision_id']
|
||||
self.class_code = record['class_code']
|
||||
self.class_name = record['class_name']
|
||||
self.subsystem_vendor_id = record['subsystem_vendor_id']
|
||||
self.subsystem_vendor_name = record['subsystem_vendor_name']
|
||||
self.subsystem_id = record['subsystem_id']
|
||||
self.subsystem_name = record['subsystem_name']
|
||||
self.driver = record['driver']
|
||||
XendBase.__init__(self, uuid, record)
|
||||
|
||||
def get_host(self):
|
||||
from xen.xend import XendNode
|
||||
return XendNode.instance().get_uuid()
|
||||
|
||||
def get_domain(self):
|
||||
return self.domain
|
||||
|
||||
def get_bus(self):
|
||||
return self.bus
|
||||
|
||||
def get_slot(self):
|
||||
return self.slot
|
||||
|
||||
def get_func(self):
|
||||
return self.func
|
||||
|
||||
def get_name(self):
|
||||
return "%04x:%02x:%02x.%01x" % (self.domain, self.bus, self.slot,
|
||||
self.func)
|
||||
|
||||
def get_vendor_id(self):
|
||||
return self.vendor_id
|
||||
|
||||
def get_vendor_name(self):
|
||||
return self.vendor_name
|
||||
|
||||
def get_device_id(self):
|
||||
return self.device_id
|
||||
|
||||
def get_device_name(self):
|
||||
return self.device_name
|
||||
|
||||
def get_class_code(self):
|
||||
return self.class_code
|
||||
|
||||
def get_class_name(self):
|
||||
return self.class_name
|
||||
|
||||
def get_revision_id(self):
|
||||
return self.revision_id
|
||||
|
||||
def get_subsystem_vendor_id(self):
|
||||
return self.subsystem_vendor_id
|
||||
|
||||
def get_subsystem_vendor_name(self):
|
||||
return self.subsystem_vendor_name
|
||||
|
||||
def get_subsystem_id(self):
|
||||
return self.subsystem_id
|
||||
|
||||
def get_subsystem_name(self):
|
||||
return self.subsystem_name
|
||||
|
||||
def get_driver(self):
|
||||
return self.driver
|
||||
|
||||