From mboxrd@z Thu Jan 1 00:00:00 1970 From: Yosuke Iwamatsu Subject: [PATCH][1/3] XenAPI: Add Physical PCI Device (PPCI) Support Date: Wed, 02 Jul 2008 20:36:51 +0900 Message-ID: <486B6853.30302@ab.jp.nec.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------010109090907020907080401" Return-path: List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: xen-devel@lists.xensource.com, xen-api@lists.xensource.com List-Id: xen-devel@lists.xenproject.org This is a multi-part message in MIME format. --------------010109090907020907080401 Content-Type: text/plain; charset=ISO-2022-JP Content-Transfer-Encoding: 7bit Add support for PPCI class, which represents physical pci devices. This makes it possible for remote servers to get information about devices the host contains. Here is the fields for PPCI class. ----------------------------- (RO_run) 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 ------------------------------ One thing to be noted; this patch uses 'lspci' command to obtain information (such as vendor_name, device_name, class_name etc) which we cannot get from sysfs. If 'lspci' command does not exist, these fields will be just left blank. Regards, ----------------------- Yosuke Iwamatsu NEC Corporation --------------010109090907020907080401 Content-Type: all/allfiles; name="xenapi_ppci.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="xenapi_ppci.patch" XenAPI: Add Physical PCI Device (PPCI) Support Signed-off-by: Yosuke Iwamatsu diff -r 19970181d6a4 -r fdf409891cd6 tools/python/xen/util/pci.py --- a/tools/python/xen/util/pci.py Tue Jul 01 14:50:35 2008 +0100 +++ b/tools/python/xen/util/pci.py Wed Jul 02 17:59:30 2008 +0900 @@ -8,6 +8,8 @@ import sys 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_DEVICE_PATH = '/device' 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 @@ MSIX_BIR_MASK = 0x7 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 @@ def PCI_DEVFN(slot, func): 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') @@ -57,6 +74,61 @@ def find_sysfs_mnt(): return sline[1] 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[0-9a-fA-F]{1,4})[:,])?" + \ + r"(?P[0-9a-fA-F]{1,2})[:,]" + \ + r"(?P[0-9a-fA-F]{1,2})[.,]" + \ + r"(?P[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): @@ -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 @@ -243,6 +322,36 @@ class PciDevice: except IOError, (errno, strerr): 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 diff -r 19970181d6a4 -r fdf409891cd6 tools/python/xen/xend/XendAPI.py --- a/tools/python/xen/xend/XendAPI.py Tue Jul 01 14:50:35 2008 +0100 +++ b/tools/python/xen/xend/XendAPI.py Wed Jul 02 17:59:30 2008 +0900 @@ -40,6 +40,7 @@ from XendVMMetrics import XendVMMetrics 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() diff -r 19970181d6a4 -r fdf409891cd6 tools/python/xen/xend/XendNode.py --- a/tools/python/xen/xend/XendNode.py Tue Jul 01 14:50:35 2008 +0100 +++ b/tools/python/xen/xend/XendNode.py Wed Jul 02 17:59:30 2008 +0900 @@ -21,6 +21,7 @@ import xen.lowlevel.xc 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 XendNetwork import * 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() @@ -270,6 +308,15 @@ class XendNode: ## del self.pifs[pif_uuid] ## 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): @@ -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( @@ -307,6 +355,12 @@ class XendNode: sr_records = dict([(k, v.get_record(transient = False)) 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 diff -r 19970181d6a4 -r fdf409891cd6 tools/python/xen/xend/XendPPCI.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/XendPPCI.py Wed Jul 02 17:59:30 2008 +0900 @@ -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 +#============================================================================ + +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 + --------------010109090907020907080401 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel --------------010109090907020907080401--