XenAPI: Add Direct PCI Device (DPCI) Assignment Support

Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>

diff -r bca35da3e14b tools/python/xen/xend/XendAPI.py
--- a/tools/python/xen/xend/XendAPI.py	Wed Jul 02 18:23:27 2008 +0900
+++ b/tools/python/xen/xend/XendAPI.py	Wed Jul 02 18:26:24 2008 +0900
@@ -41,6 +41,7 @@ from XendPIF import XendPIF
 from XendPIF import XendPIF
 from XendPBD import XendPBD
 from XendPPCI import XendPPCI
+from XendDPCI import XendDPCI
 from XendXSPolicy import XendXSPolicy, XendACMPolicy
 
 from XendAPIConstants import *
@@ -479,6 +480,7 @@ classes = {
     'PBD'          : valid_object("PBD"),
     'PIF_metrics'  : valid_object("PIF_metrics"),
     'PPCI'         : valid_object("PPCI"),
+    'DPCI'         : valid_object("DPCI")
 }
 
 autoplug_classes = {
@@ -488,6 +490,7 @@ autoplug_classes = {
     'PBD'         : XendPBD,
     'PIF_metrics' : XendPIFMetrics,
     'PPCI'        : XendPPCI,
+    'DPCI'        : XendDPCI,
     'XSPolicy'    : XendXSPolicy,
     'ACMPolicy'   : XendACMPolicy,
 }
@@ -1154,6 +1157,7 @@ class XendAPI(object):
                   'VIFs',
                   'VBDs',
                   'VTPMs',
+                  'DPCIs',
                   'tools_version',
                   'domid',
                   'is_control_domain',
@@ -1296,6 +1300,10 @@ class XendAPI(object):
         dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
         return xen_api_success(dom.get_consoles())
 
+    def VM_get_DPCIs(self, session, vm_ref):
+        dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+        return xen_api_success(dom.get_dpcis())
+    
     def VM_get_tools_version(self, session, vm_ref):
         dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
         return dom.get_tools_version()
@@ -1675,6 +1683,7 @@ class XendAPI(object):
             'VIFs': xeninfo.get_vifs(),
             'VBDs': xeninfo.get_vbds(),
             'VTPMs': xeninfo.get_vtpms(),
+            'DPCIs': xeninfo.get_dpcis(),
             'PV_bootloader': xeninfo.info.get('PV_bootloader'),
             'PV_kernel': xeninfo.info.get('PV_kernel'),
             'PV_ramdisk': xeninfo.info.get('PV_ramdisk'),
diff -r bca35da3e14b tools/python/xen/xend/XendConfig.py
--- a/tools/python/xen/xend/XendConfig.py	Wed Jul 02 18:23:27 2008 +0900
+++ b/tools/python/xen/xend/XendConfig.py	Wed Jul 02 18:26:24 2008 +0900
@@ -24,6 +24,8 @@ from xen.xend import uuid
 from xen.xend import uuid
 from xen.xend import XendOptions
 from xen.xend import XendAPIStore
+from xen.xend.XendPPCI import XendPPCI
+from xen.xend.XendDPCI import XendDPCI
 from xen.xend.XendError import VmError
 from xen.xend.XendDevices import XendDevices
 from xen.xend.PrettyPrint import prettyprintstring
@@ -773,6 +775,11 @@ class XendConfig(dict):
         """
         log.debug('_sxp_to_xapi(%s)' % scrub_password(sxp_cfg))
 
+        # _parse_sxp() below will call device_add() and construct devices.
+        # Some devices (currently only pci) may require VM's uuid, so
+        # setup self['uuid'] beforehand.
+        self['uuid'] = sxp.child_value(sxp_cfg, 'uuid', uuid.createString())
+
         cfg = self._parse_sxp(sxp_cfg)
 
         for key, typ in XENAPI_CFG_TYPES.items():
@@ -1209,42 +1216,35 @@ class XendConfig(dict):
             dev_type = sxp.name(config)
             dev_info = {}
 
-            # Parsing the device SXP's. In most cases, the SXP looks
-            # like this:
-            #
-            # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
-            #
-            # However, for PCI devices it looks like this:
-            #
-            # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1]]]]
-            #
-            # It seems the reasoning for this difference is because
-            # pciif.py needs all the PCI device configurations at
-            # the same time when creating the devices.
-            #
-            # To further complicate matters, Xen 2.0 configuration format
-            # uses the following for pci device configuration:
-            #
-            # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
-
             if dev_type == 'pci':
                 pci_devs_uuid = sxp.child_value(config, 'uuid',
                                                 uuid.createString())
-                pci_devs = []
-                for pci_dev in sxp.children(config, 'dev'):
-                    pci_dev_info = {}
-                    for opt_val in pci_dev[1:]:
-                        try:
-                            opt, val = opt_val
-                            pci_dev_info[opt] = val
-                        except TypeError:
-                            pass
-                    pci_devs.append(pci_dev_info)
+
+                pci_dict = self.pci_convert_sxp_to_dict(config)
+                pci_devs = pci_dict['devs']
+
+                # create XenAPI DPCI objects.
+                for pci_dev in pci_devs:
+                    dpci_uuid = pci_dev.get('uuid')
+                    ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
+                                                    pci_dev['bus'],
+                                                    pci_dev['slot'],
+                                                    pci_dev['func'])
+                    if ppci_uuid is None:
+                        continue
+                    dpci_record = {
+                        'VM': self['uuid'],
+                        'PPCI': ppci_uuid,
+                        'hotplug_slot': pci_dev.get('vslot', 0)
+                    }
+                    XendDPCI(dpci_uuid, dpci_record)
+                    
                 target['devices'][pci_devs_uuid] = (dev_type,
                                                     {'devs': pci_devs,
                                                      'uuid': pci_devs_uuid})
 
                 log.debug("XendConfig: reading device: %s" % pci_devs)
+
                 return pci_devs_uuid
 
             for opt_val in config[1:]:
@@ -1482,6 +1482,76 @@ class XendConfig(dict):
 
         return ''
 
+    def pci_convert_sxp_to_dict(self, dev_sxp):
+        """Convert pci device sxp to dict
+        @param dev_sxp: device configuration
+        @type  dev_sxp: SXP object (parsed config)
+        @return: dev_config
+        @rtype: dictionary
+        """
+        # Parsing the device SXP's. In most cases, the SXP looks
+        # like this:
+        #
+        # [device, [vif, [mac, xx:xx:xx:xx:xx:xx], [ip 1.3.4.5]]]
+        #
+        # However, for PCI devices it looks like this:
+        #
+        # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2]]]
+        #
+        # It seems the reasoning for this difference is because
+        # pciif.py needs all the PCI device configurations at
+        # the same time when creating the devices.
+        #
+        # To further complicate matters, Xen 2.0 configuration format
+        # uses the following for pci device configuration:
+        #
+        # [device, [pci, [domain, 0], [bus, 0], [dev, 1], [func, 2]]]
+
+        # For PCI device hotplug support, the SXP of PCI devices is
+        # extendend like this:
+        #
+        # [device, [pci, [dev, [domain, 0], [bus, 0], [slot, 1], [func, 2],
+        #                      [vslt, 0]],
+        #                [state, 'Initialising']]]
+        #
+        # 'vslt' shows the virtual hotplug slot number which the PCI device
+        # is inserted in. This is only effective for HVM domains.
+        #
+        # state 'Initialising' indicates that the device is being attached,
+        # while state 'Closing' indicates that the device is being detached.
+        #
+        # The Dict looks like this:
+        #
+        # { devs: [{domain: 0, bus: 0, slot: 1, func: 2, vslt: 0}],
+        #   states: ['Initialising'] }
+
+        dev_config = {}
+
+        pci_devs = []
+        for pci_dev in sxp.children(dev_sxp, 'dev'):
+            pci_dev_info = {}
+            for opt_val in pci_dev[1:]:
+                try:
+                    opt, val = opt_val
+                    pci_dev_info[opt] = val
+                except TypeError:
+                    pass
+                # append uuid for each pci device.
+                dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
+                pci_dev_info['uuid'] = dpci_uuid
+            pci_devs.append(pci_dev_info)
+        dev_config['devs'] = pci_devs 
+
+        pci_states = []
+        for pci_state in sxp.children(dev_sxp, 'state'):
+            try:
+                pci_states.append(pci_state[1])
+            except IndexError:
+                raise XendError("Error reading state while parsing pci sxp")
+        dev_config['states'] = pci_states
+
+        return dev_config
+
     def console_add(self, protocol, location, other_config = {}):
         dev_uuid = uuid.createString()
         if protocol == 'vt100':
@@ -1556,16 +1626,29 @@ class XendConfig(dict):
             dev_type, dev_info = self['devices'][dev_uuid]
 
             if dev_type == 'pci': # Special case for pci
-                pci_devs = []
-                for pci_dev in sxp.children(config, 'dev'):
-                    pci_dev_info = {}
-                    for opt_val in pci_dev[1:]:
-                        try:
-                            opt, val = opt_val
-                            pci_dev_info[opt] = val
-                        except TypeError:
-                            pass
-                    pci_devs.append(pci_dev_info)
+                pci_dict = self.pci_convert_sxp_to_dict(config)
+                pci_devs = pci_dict['devs']
+
+                # destroy existing XenAPI DPCI objects
+                for dpci_uuid in XendDPCI.get_by_VM(self['uuid']):
+                    XendAPIStore.deregister(dpci_uuid, "DPCI")
+
+                # create XenAPI DPCI objects.
+                for pci_dev in pci_devs:
+                    dpci_uuid = pci_dev.get('uuid')
+                    ppci_uuid = XendPPCI.get_by_sbdf(pci_dev['domain'],
+                                                     pci_dev['bus'],
+                                                     pci_dev['slot'],
+                                                     pci_dev['func'])
+                    if ppci_uuid is None:
+                        continue
+                    dpci_record = {
+                        'VM': self['uuid'],
+                        'PPCI': ppci_uuid,
+                        'hotplug_slot': pci_dev.get('vslot', 0)
+                    }
+                    XendDPCI(dpci_uuid, dpci_record)
+
                 self['devices'][dev_uuid] = (dev_type,
                                              {'devs': pci_devs,
                                               'uuid': dev_uuid})
diff -r bca35da3e14b tools/python/xen/xend/XendDPCI.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/python/xen/xend/XendDPCI.py	Wed Jul 02 18:26:24 2008 +0900
@@ -0,0 +1,154 @@
+#============================================================================
+# 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.XendPPCI import XendPPCI
+from xen.xend import XendAPIStore
+from xen.xend import uuid as genuuid
+
+import XendDomain, XendNode
+
+from XendError import *
+from XendTask import XendTask
+from XendLogging import log
+
+class XendDPCI(XendBase):
+    """Representation of a passthrough PCI device."""
+
+    def getClass(self):
+        return "DPCI"
+
+    def getAttrRO(self):
+        attrRO = ['virtual_domain',
+                  'virtual_bus',
+                  'virtual_slot',
+                  'virtual_func',
+                  'virtual_name',
+                  'VM',
+                  'PPCI',
+                  'hotplug_slot']
+        return XendBase.getAttrRO() + attrRO
+
+    def getAttrRW(self):
+        attrRW = []
+        return XendBase.getAttrRW() + attrRW
+
+    def getAttrInst(self):
+        attrInst = ['VM',
+                    'PPCI',
+                    'hotplug_slot']
+        return XendBase.getAttrInst() + attrInst
+
+    def getMethods(self):
+        methods = ['destroy']
+        return XendBase.getMethods() + methods
+
+    def getFuncs(self):
+        funcs = ['create']
+        return XendBase.getFuncs() + funcs
+
+    getClass    = classmethod(getClass)
+    getAttrRO   = classmethod(getAttrRO)
+    getAttrRW   = classmethod(getAttrRW)
+    getAttrInst = classmethod(getAttrInst)
+    getMethods  = classmethod(getMethods)
+    getFuncs    = classmethod(getFuncs)
+ 
+    def create(self, dpci_struct):
+
+        # Check if VM is valid
+        xendom = XendDomain.instance()
+        if not xendom.is_valid_vm(dpci_struct['VM']):
+            raise InvalidHandleError('VM', dpci_struct['VM'])
+        dom = xendom.get_vm_by_uuid(dpci_struct['VM'])
+
+        # Check if PPCI is valid
+        xennode = XendNode.instance()
+        ppci_uuid = xennode.get_ppci_by_uuid(dpci_struct['PPCI'])
+        if not ppci_uuid:
+            raise InvalidHandleError('PPCI', dpci_struct['PPCI'])
+        for existing_dpci in XendAPIStore.get_all('DPCI'):
+            if ppci_uuid == existing_dpci.get_PPCI():
+                raise DirectPCIError("Device is in use")
+
+        # Assign PPCI to VM
+        try:
+            dpci_ref = XendTask.log_progress(0, 100, dom.create_dpci,
+                                             dpci_struct)
+        except XendError, e:
+            raise DirectPCIError("Failed to assign device")
+
+        # TODO: Retrive virtual pci device infomation.
+
+        return dpci_ref
+
+    create = classmethod(create)
+
+    def get_by_VM(cls, VM_ref):
+        result = []
+        for dpci in XendAPIStore.get_all("DPCI"):
+            if dpci.get_VM() == VM_ref:
+                result.append(dpci.get_uuid())
+        return result
+
+    get_by_VM = classmethod(get_by_VM)
+
+    def __init__(self, uuid, record):
+        XendBase.__init__(self, uuid, record)
+
+        self.virtual_domain = -1
+        self.virtual_bus = -1
+        self.virtual_slot = -1
+        self.virtual_func = -1
+
+        self.VM = record['VM']
+        self.PPCI = record['PPCI']
+        self.hotplug_slot = record['hotplug_slot']
+
+    def destroy(self):
+        xendom = XendDomain.instance()
+        dom = xendom.get_vm_by_uuid(self.get_VM())
+        if not dom:
+            raise InvalidHandleError("VM", self.get_VM())
+        XendTask.log_progress(0, 100, dom.destroy_dpci, self.get_uuid())
+
+    def get_virtual_domain(self):
+        return self.virtual_domain
+
+    def get_virtual_bus(self):
+        return self.virtual_bus
+
+    def get_virtual_slot(self):
+        return self.virtual_slot
+
+    def get_virtual_func(self):
+        return self.virtual_func
+
+    def get_virtual_name(self):
+        return "%04x:%02x:%02x.%01x" % (self.virtual_domain, self.virtual_bus,
+                                        self.virtual_slot, self.virtual_func)
+
+    def get_VM(self):
+        return self.VM
+
+    def get_PPCI(self):
+        return self.PPCI
+
+    def get_hotplug_slot(self):
+        return self.hotplug_slot
+
diff -r bca35da3e14b tools/python/xen/xend/XendDomain.py
--- a/tools/python/xen/xend/XendDomain.py	Wed Jul 02 18:23:27 2008 +0900
+++ b/tools/python/xen/xend/XendDomain.py	Wed Jul 02 18:26:24 2008 +0900
@@ -478,6 +478,8 @@ class XendDomain:
             
             if domid in self.domains:
                 del self.domains[domid]
+
+            info.destroy_xapi_device_instances()
         else:
             log.warning("Attempted to remove non-existent domain.")
 
@@ -1091,6 +1093,7 @@ class XendDomain:
         self._managed_domain_unregister(dominfo)
         self._remove_domain(dominfo)
         XendDevices.destroy_device_state(dominfo)
+        dominfo.destroy_xapi_device_instances()
 
 
     def domain_configure(self, config):
diff -r bca35da3e14b tools/python/xen/xend/XendDomainInfo.py
--- a/tools/python/xen/xend/XendDomainInfo.py	Wed Jul 02 18:23:27 2008 +0900
+++ b/tools/python/xen/xend/XendDomainInfo.py	Wed Jul 02 18:26:24 2008 +0900
@@ -54,6 +54,10 @@ from xen.xend.XendAPIConstants import *
 from xen.xend.XendAPIConstants import *
 
 from xen.xend.XendVMMetrics import XendVMMetrics
+
+from xen.xend.XendPPCI import XendPPCI
+from xen.xend.XendDPCI import XendDPCI
+from xen.xend import XendAPIStore
 
 MIGRATE_TIMEOUT = 30.0
 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
@@ -642,50 +646,7 @@ class XendDomainInfo:
         xen.xend.XendDomain.instance().managed_config_save(self)
         return self.getDeviceController(dev_type).sxpr(devid)
 
-    def pci_convert_sxp_to_dict(self, dev_sxp):
-        """Convert pci device sxp to dict
-        @param dev_sxp: device configuration
-        @type  dev_sxp: SXP object (parsed config)
-        @return: dev_config
-        @rtype: dictionary
-        """
-        # In reconfigure phase, config of PCI device looks like below:
-        #
-        # sxp:
-        # [device, [pci, [dev, [domain, '0x0'], [bus, '0x0'], [slot, '0x0'],
-        #                      [func, '0x0'], [vslt, '0x0']],
-        #                [state, 'Initialising']]]
-        #
-        # dict:
-        # {devs: [{domain: '0x0', bus: '0x0', slot: '0x0', func: '0x0',
-        #          vslt: '0x0'}],
-        #  states: ['Initialising']}
-        #
-        # state 'Initialising' means the device is being attached.
-        # state 'Closing' means the device is being detached.
-
-        dev_config = {}
-        pci_devs = []
-        for pci_dev in sxp.children(dev_sxp, 'dev'):
-            pci_dev_info = {}
-            for opt_val in pci_dev[1:]:
-                try:
-                    opt, val = opt_val
-                    pci_dev_info[opt] = val
-                except TypeError:
-                    pass
-            pci_devs.append(pci_dev_info)
-        dev_config['devs'] = pci_devs 
-        pci_states = []
-        for pci_state in sxp.children(dev_sxp, 'state'):
-            try:
-                pci_states.append(pci_state[1])
-            except IndexError:
-                raise XendError("Error reading state while parsing pci sxp")
-        dev_config['states'] = pci_states
-        
-        return dev_config
- 
+
     def pci_device_configure(self, dev_sxp, devid = 0):
         """Configure an existing pci device.
         
@@ -711,7 +672,7 @@ class XendDomainInfo:
             raise XendError("Cannot detach when pci platform does not exist")
 
         pci_dev = sxp.children(dev_sxp, 'dev')[0]
-        dev_config = self.pci_convert_sxp_to_dict(dev_sxp)
+        dev_config = self.info.pci_convert_sxp_to_dict(dev_sxp)
         dev = dev_config['devs'][0]
                 
         # Do HVM specific processing
@@ -785,6 +746,8 @@ class XendDomainInfo:
                 self.destroyDevice('pci', devid)
                 del self.info['devices'][dev_uuid]
 
+        xen.xend.XendDomain.instance().managed_config_save(self)
+
         return True
 
     def device_configure(self, dev_sxp, devid = None):
@@ -3169,6 +3132,9 @@ class XendDomainInfo:
     def get_vtpms(self):
         return self.info.get('vtpm_refs', [])
 
+    def get_dpcis(self):
+        return XendDPCI.get_by_VM(self.info.get('uuid'))
+
     def create_vbd(self, xenapi_vbd, vdi_image_path):
         """Create a VBD using a VDI from XendStorageRepository.
 
@@ -3291,6 +3257,64 @@ class XendDomainInfo:
 
     def set_console_other_config(self, console_uuid, other_config):
         self.info.console_update(console_uuid, 'other_config', other_config)
+
+    def create_dpci(self, xenapi_pci):
+        """Create pci device from the passed struct in Xen API format.
+
+        @param xenapi_pci: DPCI struct from Xen API
+        @rtype: bool
+        #@rtype: string
+        @return: True if successfully created device
+        #@return: UUID
+        """
+
+        dpci_uuid = uuid.createString()
+
+        # Convert xenapi to sxp
+        ppci = XendAPIStore.get(xenapi_pci.get('PPCI'), 'PPCI')
+
+        target_pci_sxp = \
+            ['pci', 
+                ['dev',
+                    ['domain', '0x%02x' % ppci.get_domain()],
+                    ['bus', '0x%02x' % ppci.get_bus()],
+                    ['slot', '0x%02x' % ppci.get_slot()],
+                    ['func', '0x%1x' % ppci.get_func()],
+                    ['vslt', '0x%02x' % xenapi_pci.get('hotplug_slot')],
+                    ['uuid', dpci_uuid]
+                ],
+                ['state', 'Initialising']
+            ]
+
+        if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING:
+
+            old_pci_sxp = self._getDeviceInfo_pci(0)
+
+            if old_pci_sxp is None:
+                dev_uuid = self.info.device_add('pci', cfg_sxp = target_pci_sxp)
+                if not dev_uuid:
+                    raise XendError('Failed to create device')
+
+            else:
+                new_pci_sxp = ['pci']
+                for existing_dev in sxp.children(old_pci_sxp, 'dev'):
+                    new_pci_sxp.append(existing_dev)
+                new_pci_sxp.append(sxp.child0(target_pci_sxp, 'dev'))
+
+                dev_uuid = sxp.child_value(old_pci_sxp, 'uuid')
+                self.info.device_update(dev_uuid, new_pci_sxp)
+
+            xen.xend.XendDomain.instance().managed_config_save(self)
+
+        else:
+            try:
+                self.device_configure(target_pci_sxp)
+
+            except Exception, exn:
+                raise XendError('Failed to create device')
+
+        return dpci_uuid
+
 
     def destroy_device_by_uuid(self, dev_type, dev_uuid):
         if dev_uuid not in self.info['devices']:
@@ -3318,6 +3342,63 @@ class XendDomainInfo:
 
     def destroy_vtpm(self, dev_uuid):
         self.destroy_device_by_uuid('vtpm', dev_uuid)
+
+    def destroy_dpci(self, dev_uuid):
+
+        dpci = XendAPIStore.get(dev_uuid, 'DPCI')
+        ppci = XendAPIStore.get(dpci.get_PPCI(), 'PPCI')
+
+        old_pci_sxp = self._getDeviceInfo_pci(0)
+        dev_uuid = sxp.child_value(old_pci_sxp, 'uuid')
+        target_dev = None
+        new_pci_sxp = ['pci']
+        for dev in sxp.children(old_pci_sxp, 'dev'):
+            domain = int(sxp.child_value(dev, 'domain'), 16)
+            bus = int(sxp.child_value(dev, 'bus'), 16)
+            slot = int(sxp.child_value(dev, 'slot'), 16)
+            func = int(sxp.child_value(dev, 'func'), 16)
+            name = "%04x:%02x:%02x.%01x" % (domain, bus, slot, func)
+            if ppci.get_name() == name:
+                target_dev = dev
+            else:
+                new_pci_sxp.append(dev)
+
+        if target_dev is None:
+            raise XendError('Failed to destroy device')
+
+        target_pci_sxp = ['pci', target_dev, ['state', 'Closing']]
+
+        if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING:
+
+            self.info.device_update(dev_uuid, new_pci_sxp)
+            if len(sxp.children(new_pci_sxp, 'dev')) == 0:
+                del self.info['devices'][dev_uuid]
+            xen.xend.XendDomain.instance().managed_config_save(self)
+
+        else:
+            try:
+                self.device_configure(target_pci_sxp)
+
+            except Exception, exn:
+                raise XendError('Failed to destroy device')
+
+    def destroy_xapi_device_instances(self):
+        """Destroy Xen-API device instances stored in XendAPIStore.
+        """
+        # Xen-API classes based on XendBase have their instances stored
+        # in XendAPIStore. Cleanup these virtual device instances here
+        # if they are supposed to be destroyed when the parent domain is dead.
+        #
+        # Most of the virtual devices (vif, vbd, vfb, etc) are not based on
+        # XendBase and there's no need to remove them from XendAPIStore.
+
+        from xen.xend import XendDomain
+        if XendDomain.instance().is_valid_vm(self.info.get('uuid')):
+            # domain still exists.
+            return
+
+        for dpci_uuid in XendDPCI.get_by_VM(self.info.get('uuid')):
+            XendAPIStore.deregister(dpci_uuid, "DPCI")
             
     def has_device(self, dev_class, dev_uuid):
         return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
diff -r bca35da3e14b tools/python/xen/xend/XendError.py
--- a/tools/python/xen/xend/XendError.py	Wed Jul 02 18:23:27 2008 +0900
+++ b/tools/python/xen/xend/XendError.py	Wed Jul 02 18:26:24 2008 +0900
@@ -174,6 +174,17 @@ class NetworkError(XendAPIError):
 
     def __str__(self):
         return 'NETWORK_ERROR: %s %s' % (self.error, self.network)
+
+class DirectPCIError(XendAPIError):
+    def __init__(self, error):
+        XendAPIError.__init__(self)
+        self.error = error
+
+    def get_api_error(self):
+        return ['DIRECT_PCI_ERROR', self.error]
+
+    def __str__(self):
+        return 'DIRECT_PCI_ERROR: %s' % self.error
 
 from xen.util.xsconstants import xserr2string
 
diff -r bca35da3e14b tools/python/xen/xend/server/pciif.py
--- a/tools/python/xen/xend/server/pciif.py	Wed Jul 02 18:23:27 2008 +0900
+++ b/tools/python/xen/xend/server/pciif.py	Wed Jul 02 18:26:24 2008 +0900
@@ -76,6 +76,7 @@ class PciController(DevController):
 
             back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%02x" % \
                                         (domain, bus, slot, func)
+            back['uuid-%i' % pcidevid] = pci_config.get('uuid', '')
             pcidevid += 1
 
         if vslots != "":
@@ -101,6 +102,7 @@ class PciController(DevController):
             try:
                 dev = back['dev-%i' % i]
                 state = states[i]
+                uuid = back['uuid-%i' %i]
             except:
                 raise XendError('Error reading config')
 
@@ -121,6 +123,7 @@ class PciController(DevController):
                 self.writeBackend(devid, 'dev-%i' % (num_olddevs + i), dev)
                 self.writeBackend(devid, 'state-%i' % (num_olddevs + i),
                                   str(xenbusState['Initialising']))
+                self.writeBackend(devid, 'uuid-%i' % (num_olddevs + i), uuid)
                 self.writeBackend(devid, 'num_devs', str(num_olddevs + i + 1))
 
                 # Update vslots
@@ -141,7 +144,7 @@ class PciController(DevController):
                     raise XendError('Device %s is not connected' % dev)
 
                 # Update vslots
-                if back['vslots'] is not None:
+                if back.get('vslots') is not None:
                     vslots = old_vslots
                     for vslt in back['vslots'].split(';'):
                         if vslt != '':
@@ -185,6 +188,9 @@ class PciController(DevController):
                                  'bus': '0x%(bus)s' % pci_dev_info,
                                  'slot': '0x%(slot)s' % pci_dev_info,
                                  'func': '0x%(func)s' % pci_dev_info}
+
+                # Per device uuid info
+                dev_dict['uuid'] = self.readBackend(devid, 'uuid-%d' % i)
 
                 #append vslot info
                 if vslots is not None:
@@ -442,6 +448,7 @@ class PciController(DevController):
                 self.removeBackend(devid, 'dev-%i' % i)
                 self.removeBackend(devid, 'vdev-%i' % i)
                 self.removeBackend(devid, 'state-%i' % i)
+                self.removeBackend(devid, 'uuid-%i' % i)
             else:
                 if new_num_devs != i:
                     tmpdev = self.readBackend(devid, 'dev-%i' % i)
@@ -455,6 +462,9 @@ class PciController(DevController):
                     tmpstate = self.readBackend(devid, 'state-%i' % i)
                     self.writeBackend(devid, 'state-%i' % new_num_devs, tmpstate)
                     self.removeBackend(devid, 'state-%i' % i)
+                    tmpuuid = self.readBackend(devid, 'uuid-%i' % i)
+                    self.writeBackend(devid, 'uuid-%i' % new_num_devs, tmpuuid)
+                    self.removeBackend(devid, 'uuid-%i' % i)
                 new_num_devs = new_num_devs + 1
 
         self.writeBackend(devid, 'num_devs', str(new_num_devs))
