ia64/xen-unstable

changeset 17240:3f407392da49

tools: Add PV passthrough PCI device hotplug support.
Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Mar 18 11:39:21 2008 +0000 (2008-03-18)
parents e678b42c36c4
children ef85eeaf439a
files tools/python/xen/xend/XendConfig.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/server/DevController.py tools/python/xen/xend/server/pciif.py tools/python/xen/xm/main.py
line diff
     1.1 --- a/tools/python/xen/xend/XendConfig.py	Tue Mar 18 11:34:44 2008 +0000
     1.2 +++ b/tools/python/xen/xend/XendConfig.py	Tue Mar 18 11:39:21 2008 +0000
     1.3 @@ -1461,6 +1461,23 @@ class XendConfig(dict):
     1.4                  config = cfg_sxp
     1.5  
     1.6              dev_type, dev_info = self['devices'][dev_uuid]
     1.7 +
     1.8 +            if dev_type == 'pci': # Special case for pci
     1.9 +                pci_devs = []
    1.10 +                for pci_dev in sxp.children(config, 'dev'):
    1.11 +                    pci_dev_info = {}
    1.12 +                    for opt_val in pci_dev[1:]:
    1.13 +                        try:
    1.14 +                            opt, val = opt_val
    1.15 +                            pci_dev_info[opt] = val
    1.16 +                        except TypeError:
    1.17 +                            pass
    1.18 +                    pci_devs.append(pci_dev_info)
    1.19 +                self['devices'][dev_uuid] = (dev_type,
    1.20 +                                             {'devs': pci_devs,
    1.21 +                                              'uuid': dev_uuid})
    1.22 +                return True
    1.23 +                
    1.24              for opt_val in config[1:]:
    1.25                  try:
    1.26                      opt, val = opt_val
     2.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Tue Mar 18 11:34:44 2008 +0000
     2.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Tue Mar 18 11:39:21 2008 +0000
     2.3 @@ -558,18 +558,17 @@ class XendDomainInfo:
     2.4              count += 1
     2.5  
     2.6  
     2.7 -    def pci_device_create(self, dev_config):
     2.8 -        log.debug("XendDomainInfo.pci_device_create: %s" % scrub_password(dev_config))
     2.9 +    def hvm_pci_device_create(self, dev_config):
    2.10 +        log.debug("XendDomainInfo.hvm_pci_device_create: %s"
    2.11 +                  % scrub_password(dev_config))
    2.12  
    2.13          if not self.info.is_hvm():
    2.14 -            raise VmError("only HVM guest support pci attach")
    2.15 +            raise VmError("hvm_pci_device_create called on non-HVM guest")
    2.16  
    2.17          #all the PCI devs share one conf node
    2.18          devid = '0'
    2.19  
    2.20 -        dev_type = sxp.name(dev_config)
    2.21 -        new_devs = sxp.child_value(dev_config, 'devs')
    2.22 -        new_dev = new_devs[0]
    2.23 +        new_dev = dev_config['devs'][0]
    2.24          dev_info = self._getDeviceInfo_pci(devid)#from self.info['devices']
    2.25  
    2.26          #check conflict before trigger hotplug event
    2.27 @@ -611,35 +610,6 @@ class XendDomainInfo:
    2.28                  new_dev['vslt'])
    2.29          self.image.signalDeviceModel('pci-ins', 'pci-inserted', bdf_str)
    2.30  
    2.31 -        # update the virtual pci slot
    2.32 -        vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter"
    2.33 -                          % self.getDomid())
    2.34 -        new_dev['vslt'] = vslt
    2.35 -
    2.36 -        if dev_info is None:
    2.37 -            # create a new one from scrach
    2.38 -            dev_cfg_sxp = [dev_type,
    2.39 -                ['dev',
    2.40 -                  ['domain', new_dev['domain']],
    2.41 -                  ['bus',    new_dev['bus']],
    2.42 -                  ['slot',   new_dev['slot']],
    2.43 -                  ['func',   new_dev['func']],
    2.44 -                  ['vslt',   new_dev['vslt']]
    2.45 -                ]]
    2.46 -            dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_cfg_sxp)
    2.47 -            dev_config_dict = self.info['devices'][dev_uuid][1]
    2.48 -            try:
    2.49 -                dev_config_dict['devid'] = devid = \
    2.50 -                    self._createDevice(dev_type, dev_config_dict)
    2.51 -                self._waitForDevice(dev_type, devid)
    2.52 -            except VmError, ex:
    2.53 -                raise ex
    2.54 -        else:
    2.55 -            # update the pci config to add the new dev
    2.56 -            pci_devs.extend(new_devs)
    2.57 -            self._reconfigureDevice('pci', devid, pci_conf)
    2.58 -
    2.59 -        return self.getDeviceController('pci').sxpr(devid)
    2.60  
    2.61      def device_create(self, dev_config):
    2.62          """Create a new device.
    2.63 @@ -649,11 +619,6 @@ class XendDomainInfo:
    2.64          """
    2.65          log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
    2.66          dev_type = sxp.name(dev_config)
    2.67 -
    2.68 -        if dev_type == 'pci':
    2.69 -            rc = self.pci_device_create(dev_config)
    2.70 -            return rc
    2.71 -
    2.72          dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
    2.73          dev_config_dict = self.info['devices'][dev_uuid][1]
    2.74          log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
    2.75 @@ -676,6 +641,151 @@ class XendDomainInfo:
    2.76          xen.xend.XendDomain.instance().managed_config_save(self)
    2.77          return self.getDeviceController(dev_type).sxpr(devid)
    2.78  
    2.79 +    def pci_convert_sxp_to_dict(self, dev_sxp):
    2.80 +        """Convert pci device sxp to dict
    2.81 +        @param dev_sxp: device configuration
    2.82 +        @type  dev_sxp: SXP object (parsed config)
    2.83 +        @return: dev_config
    2.84 +        @rtype: dictionary
    2.85 +        """
    2.86 +        # In reconfigure phase, config of PCI device looks like below:
    2.87 +        #
    2.88 +        # sxp:
    2.89 +        # [device, [pci, [dev, [domain, '0x0'], [bus, '0x0'], [slot, '0x0'],
    2.90 +        #                      [func, '0x0'], [vslt, '0x0']],
    2.91 +        #                [state, 'Initialising']]]
    2.92 +        #
    2.93 +        # dict:
    2.94 +        # {devs: [{domain: '0x0', bus: '0x0', slot: '0x0', func: '0x0',
    2.95 +        #          vslt: '0x0'}],
    2.96 +        #  states: ['Initialising']}
    2.97 +        #
    2.98 +        # state 'Initialising' means the device is being attached.
    2.99 +        # state 'Closing' means the device is being detached.
   2.100 +
   2.101 +        dev_config = {}
   2.102 +        pci_devs = []
   2.103 +        for pci_dev in sxp.children(dev_sxp, 'dev'):
   2.104 +            pci_dev_info = {}
   2.105 +            for opt_val in pci_dev[1:]:
   2.106 +                try:
   2.107 +                    opt, val = opt_val
   2.108 +                    pci_dev_info[opt] = val
   2.109 +                except TypeError:
   2.110 +                    pass
   2.111 +            pci_devs.append(pci_dev_info)
   2.112 +        dev_config['devs'] = pci_devs 
   2.113 +        pci_states = []
   2.114 +        for pci_state in sxp.children(dev_sxp, 'state'):
   2.115 +            try:
   2.116 +                pci_states.append(pci_state[1])
   2.117 +            except IndexError:
   2.118 +                raise XendError("Error reading state while parsing pci sxp")
   2.119 +        dev_config['states'] = pci_states
   2.120 +        
   2.121 +        return dev_config
   2.122 + 
   2.123 +    def pci_device_configure(self, dev_sxp, devid = 0):
   2.124 +        """Configure an existing pci device.
   2.125 +        
   2.126 +        @param dev_sxp: device configuration
   2.127 +        @type  dev_sxp: SXP object (parsed config)
   2.128 +        @param devid:      device id
   2.129 +        @type  devid:      int
   2.130 +        @return: Returns True if successfully updated device
   2.131 +        @rtype: boolean
   2.132 +        """
   2.133 +        log.debug("XendDomainInfo.pci_device_configure: %s"
   2.134 +                  % scrub_password(dev_sxp))
   2.135 +
   2.136 +        dev_class = sxp.name(dev_sxp)
   2.137 +
   2.138 +        if dev_class != 'pci':
   2.139 +            return False
   2.140 +
   2.141 +        pci_state = sxp.child_value(dev_sxp, 'state')
   2.142 +        existing_dev_info = self._getDeviceInfo_pci(devid)
   2.143 +
   2.144 +        if existing_dev_info is None and pci_state != 'Initialising':
   2.145 +            raise XendError("Cannot detach when pci platform does not exist")
   2.146 +
   2.147 +        pci_dev = sxp.children(dev_sxp, 'dev')[0]
   2.148 +        dev_config = self.pci_convert_sxp_to_dict(dev_sxp)
   2.149 +        dev = dev_config['devs'][0]
   2.150 +                
   2.151 +        # Do HVM specific processing
   2.152 +        if self.info.is_hvm():
   2.153 +            if pci_state == 'Initialising':
   2.154 +                # HVM PCI device attachment
   2.155 +                self.hvm_pci_device_create(dev_config)
   2.156 +                # Update vslt
   2.157 +                vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter"
   2.158 +                                       % self.getDomid())
   2.159 +                dev['vslt'] = vslt
   2.160 +                for n in sxp.children(pci_dev):
   2.161 +                    if(n[0] == 'vslt'):
   2.162 +                        n[1] = vslt
   2.163 +            else:
   2.164 +                # HVM PCI device detachment
   2.165 +                existing_dev_uuid = sxp.child_value(existing_dev_info, 'uuid')
   2.166 +                existing_pci_conf = self.info['devices'][existing_dev_uuid][1]
   2.167 +                existing_pci_devs = existing_pci_conf['devs']
   2.168 +                vslt = '0x0'
   2.169 +                for x in existing_pci_devs:
   2.170 +                    if ( int(x['domain'], 16) == int(dev['domain'], 16) and
   2.171 +                         int(x['bus'], 16) == int(dev['bus'], 16) and
   2.172 +                         int(x['slot'], 16) == int(dev['slot'], 16) and
   2.173 +                         int(x['func'], 16) == int(dev['func'], 16) ):
   2.174 +                        vslt = x['vslt']
   2.175 +                        break
   2.176 +                if vslt == '0x0':
   2.177 +                    raise VmError("Device %04x:%02x:%02x.%02x is not connected"
   2.178 +                                  % (int(dev['domain'],16), int(dev['bus'],16),
   2.179 +                                     int(dev['slot'],16), int(dev['func'],16)))
   2.180 +                self.hvm_destroyPCIDevice(int(vslt, 16))
   2.181 +                # Update vslt
   2.182 +                dev['vslt'] = vslt
   2.183 +                for n in sxp.children(pci_dev):
   2.184 +                    if(n[0] == 'vslt'):
   2.185 +                        n[1] = vslt
   2.186 +
   2.187 +        # If pci platform does not exist, create and exit.
   2.188 +        if existing_dev_info is None:
   2.189 +            self.device_create(dev_sxp)
   2.190 +            return True
   2.191 +
   2.192 +        # use DevController.reconfigureDevice to change device config
   2.193 +        dev_control = self.getDeviceController(dev_class)
   2.194 +        dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
   2.195 +        if not self.info.is_hvm():
   2.196 +            # in PV case, wait until backend state becomes connected.
   2.197 +            dev_control.waitForDevice_reconfigure(devid)
   2.198 +        num_devs = dev_control.cleanupDevice(devid)
   2.199 +
   2.200 +        # update XendConfig with new device info
   2.201 +        if dev_uuid:
   2.202 +            new_dev_sxp = dev_control.configuration(devid)
   2.203 +            self.info.device_update(dev_uuid, new_dev_sxp)
   2.204 +
   2.205 +        # If there is no device left, destroy pci and remove config.
   2.206 +        if num_devs == 0:
   2.207 +            if self.info.is_hvm():
   2.208 +                self.destroyDevice('pci', devid, True)
   2.209 +                del self.info['devices'][dev_uuid]
   2.210 +                platform = self.info['platform']
   2.211 +                orig_dev_num = len(platform['pci'])
   2.212 +                # TODO: can use this to keep some info to ask high level
   2.213 +                # management tools to hot insert a new passthrough dev
   2.214 +                # after migration
   2.215 +                if orig_dev_num != 0:
   2.216 +                    #platform['pci'] = ["%dDEVs" % orig_dev_num]
   2.217 +                    platform['pci'] = []
   2.218 +            else:
   2.219 +                self.destroyDevice('pci', devid)
   2.220 +                del self.info['devices'][dev_uuid]
   2.221 +
   2.222 +        return True
   2.223 +
   2.224      def device_configure(self, dev_sxp, devid = None):
   2.225          """Configure an existing device.
   2.226          
   2.227 @@ -690,6 +800,10 @@ class XendDomainInfo:
   2.228          # convert device sxp to a dict
   2.229          dev_class = sxp.name(dev_sxp)
   2.230          dev_config = {}
   2.231 +
   2.232 +        if dev_class == 'pci':
   2.233 +            return self.pci_device_configure(dev_sxp)
   2.234 +
   2.235          for opt_val in dev_sxp[1:]:
   2.236              try:
   2.237                  dev_config[opt_val[0]] = opt_val[1]
   2.238 @@ -714,11 +828,11 @@ class XendDomainInfo:
   2.239          for devclass in XendDevices.valid_devices():
   2.240              self.getDeviceController(devclass).waitForDevices()
   2.241  
   2.242 -    def destroyPCIDevice(self, vslot):
   2.243 -        log.debug("destroyPCIDevice called %s", vslot)
   2.244 +    def hvm_destroyPCIDevice(self, vslot):
   2.245 +        log.debug("hvm_destroyPCIDevice called %s", vslot)
   2.246  
   2.247          if not self.info.is_hvm():
   2.248 -            raise VmError("only HVM guest support pci detach")
   2.249 +            raise VmError("hvm_destroyPCIDevice called on non-HVM guest")
   2.250  
   2.251          #all the PCI devs share one conf node
   2.252          devid = '0'
   2.253 @@ -744,35 +858,16 @@ class XendDomainInfo:
   2.254              raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot))
   2.255  
   2.256          bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func'])
   2.257 -        log.info("destroyPCIDevice:%s:%s!", x, bdf_str)
   2.258 +        log.info("hvm_destroyPCIDevice:%s:%s!", x, bdf_str)
   2.259  
   2.260          self.image.signalDeviceModel('pci-rem', 'pci-removed', bdf_str)
   2.261  
   2.262 -        if pci_len > 1:
   2.263 -            del pci_conf['devs'][devnum]
   2.264 -            self._reconfigureDevice('pci', devid, pci_conf)
   2.265 -        else:
   2.266 -            self.getDeviceController('pci').destroyDevice(devid, True)
   2.267 -            del self.info['devices'][dev_uuid]
   2.268 -            platform = self.info['platform']
   2.269 -            orig_dev_num = len(platform['pci'])
   2.270 -
   2.271 -            #need remove the pci config
   2.272 -            #TODO:can use this to keep some info to ask high level management tools to hot insert a new passthrough dev after migration
   2.273 -            if orig_dev_num != 0:
   2.274 -#                platform['pci'] = ["%dDEVs" % orig_dev_num]
   2.275 -                platform['pci'] = []
   2.276 -
   2.277          return 0
   2.278  
   2.279      def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
   2.280          log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
   2.281                    deviceClass, devid)
   2.282  
   2.283 -        if deviceClass == 'dpci':
   2.284 -            rc = self.destroyPCIDevice(devid)
   2.285 -            return rc
   2.286 -
   2.287          if rm_cfg:
   2.288              # Convert devid to device number.  A device number is
   2.289              # needed to remove its configuration.
     3.1 --- a/tools/python/xen/xend/server/DevController.py	Tue Mar 18 11:34:44 2008 +0000
     3.2 +++ b/tools/python/xen/xend/server/DevController.py	Tue Mar 18 11:39:21 2008 +0000
     3.3 @@ -51,6 +51,8 @@ xenbusState = {
     3.4      'Connected'    : 4,
     3.5      'Closing'      : 5,
     3.6      'Closed'       : 6,
     3.7 +    'Reconfiguring': 7,
     3.8 +    'Reconfigured' : 8,
     3.9      }
    3.10  
    3.11  xoptions = XendOptions.instance()
    3.12 @@ -89,6 +91,8 @@ class DevController:
    3.13          if devid is None:
    3.14              return 0
    3.15  
    3.16 +        self.setupDevice(config)
    3.17 +
    3.18          (backpath, frontpath) = self.addStoreEntries(config, devid, back,
    3.19                                                       front)
    3.20  
    3.21 @@ -200,6 +204,15 @@ class DevController:
    3.22              raise VmError("Device %s (%s) could not be disconnected. " %
    3.23                            (devid, self.deviceClass))
    3.24  
    3.25 +    def waitForDevice_reconfigure(self, devid):
    3.26 +        log.debug("Waiting for %s - reconfigureDevice.", devid)
    3.27 +
    3.28 +        (status, err) = self.waitForBackend_reconfigure(devid)
    3.29 +
    3.30 +        if status == Timeout:
    3.31 +            raise VmError("Device %s (%s) could not be reconfigured. " %
    3.32 +                          (devid, self.deviceClass))
    3.33 +
    3.34  
    3.35      def reconfigureDevice(self, devid, config):
    3.36          """Reconfigure the specified device.
    3.37 @@ -326,6 +339,11 @@ class DevController:
    3.38  
    3.39          raise NotImplementedError()
    3.40  
    3.41 +    def setupDevice(self, config):
    3.42 +        """ Setup device from config.
    3.43 +        """
    3.44 +        return
    3.45 +
    3.46      def migrate(self, deviceConfig, network, dst, step, domName):
    3.47          """ Migration of a device. The 'network' parameter indicates
    3.48              whether the device is network-migrated (True). 'dst' then gives
    3.49 @@ -569,6 +587,22 @@ class DevController:
    3.50  
    3.51          return result['status']
    3.52  
    3.53 +    def waitForBackend_reconfigure(self, devid):
    3.54 +        frontpath = self.frontendPath(devid)
    3.55 +        backpath = xstransact.Read(frontpath, "backend")
    3.56 +        if backpath:
    3.57 +            statusPath = backpath + '/' + "state"
    3.58 +            ev = Event()
    3.59 +            result = { 'status': Timeout }
    3.60 +
    3.61 +            xswatch(statusPath, xenbusStatusCallback, ev, result)
    3.62 +
    3.63 +            ev.wait(DEVICE_CREATE_TIMEOUT)
    3.64 +
    3.65 +            return (result['status'], None)
    3.66 +        else:
    3.67 +            return (Missing, None)
    3.68 +
    3.69  
    3.70      def backendPath(self, backdom, devid):
    3.71          """Construct backend path given the backend domain and device id.
    3.72 @@ -634,3 +668,19 @@ def deviceDestroyCallback(statusPath, ev
    3.73  
    3.74      ev.set()
    3.75      return 0
    3.76 +
    3.77 +
    3.78 +def xenbusStatusCallback(statusPath, ev, result):
    3.79 +    log.debug("xenbusStatusCallback %s.", statusPath)
    3.80 +
    3.81 +    status = xstransact.Read(statusPath)
    3.82 +
    3.83 +    if status == str(xenbusState['Connected']):
    3.84 +        result['status'] = Connected
    3.85 +    else:
    3.86 +        return 1
    3.87 +
    3.88 +    log.debug("xenbusStatusCallback %d.", result['status'])
    3.89 +
    3.90 +    ev.set()
    3.91 +    return 0
     4.1 --- a/tools/python/xen/xend/server/pciif.py	Tue Mar 18 11:34:44 2008 +0000
     4.2 +++ b/tools/python/xen/xend/server/pciif.py	Tue Mar 18 11:39:21 2008 +0000
     4.3 @@ -24,7 +24,7 @@ from xen.xend import sxp
     4.4  from xen.xend.XendError import VmError
     4.5  from xen.xend.XendLogging import log
     4.6  
     4.7 -from xen.xend.server.DevController import DevController
     4.8 +from xen.xend.server.DevController import DevController, xenbusState
     4.9  
    4.10  import xen.lowlevel.xc
    4.11  
    4.12 @@ -44,6 +44,15 @@ while not (t&1):
    4.13      t>>=1
    4.14      PAGE_SHIFT+=1
    4.15  
    4.16 +def parse_hex(val):
    4.17 +    try:
    4.18 +        if isinstance(val, types.StringTypes):
    4.19 +            return int(val, 16)
    4.20 +        else:
    4.21 +            return val
    4.22 +    except ValueError:
    4.23 +        return None
    4.24 +
    4.25  class PciController(DevController):
    4.26  
    4.27      def __init__(self, vm):
    4.28 @@ -52,15 +61,6 @@ class PciController(DevController):
    4.29  
    4.30      def getDeviceDetails(self, config):
    4.31          """@see DevController.getDeviceDetails"""
    4.32 -        def parse_hex(val):
    4.33 -            try:
    4.34 -                if isinstance(val, types.StringTypes):
    4.35 -                    return int(val, 16)
    4.36 -                else:
    4.37 -                    return val
    4.38 -            except ValueError:
    4.39 -                return None
    4.40 -            
    4.41          back = {}
    4.42          pcidevid = 0
    4.43          vslots = ""
    4.44 @@ -74,7 +74,6 @@ class PciController(DevController):
    4.45              if vslt is not None:
    4.46                  vslots = vslots + vslt + ";"
    4.47  
    4.48 -            self.setupDevice(domain, bus, slot, func)
    4.49              back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%02x" % \
    4.50                                          (domain, bus, slot, func)
    4.51              pcidevid += 1
    4.52 @@ -86,27 +85,80 @@ class PciController(DevController):
    4.53          back['uuid'] = config.get('uuid','')
    4.54          return (0, back, {})
    4.55  
    4.56 +
    4.57      def reconfigureDevice(self, _, config):
    4.58          """@see DevController.reconfigureDevice"""
    4.59 -        #currently only support config changes by hot insert/remove pass-through dev
    4.60 -        #delete all the devices in xenstore
    4.61 -        (devid, new_back, new_front) = self.getDeviceDetails(config)
    4.62 -        num_devs = self.readBackend(devid, 'num_devs')
    4.63 -        for i in range(int(num_devs)):
    4.64 -            self.removeBackend(devid, 'dev-%d' % i)
    4.65 -        self.removeBackend(devid, 'num_devs')
    4.66 +        (devid, back, front) = self.getDeviceDetails(config)
    4.67 +        num_devs = int(back['num_devs'])
    4.68 +        states = config.get('states', [])
    4.69  
    4.70 -        #create new devices config
    4.71 -        num_devs = new_back['num_devs']
    4.72 -        for i in range(int(num_devs)):
    4.73 -            dev_no = 'dev-%d' % i
    4.74 -            self.writeBackend(devid, dev_no, new_back[dev_no])
    4.75 -        self.writeBackend(devid, 'num_devs', num_devs)
    4.76 +        old_vslots = self.readBackend(devid, 'vslots')
    4.77 +        if old_vslots is None:
    4.78 +            old_vslots = ''
    4.79 +        num_olddevs = int(self.readBackend(devid, 'num_devs'))
    4.80  
    4.81 -        if new_back['vslots'] is not None:
    4.82 -            self.writeBackend(devid, 'vslots', new_back['vslots'])
    4.83 +        for i in range(num_devs):
    4.84 +            try:
    4.85 +                dev = back['dev-%i' % i]
    4.86 +                state = states[i]
    4.87 +            except:
    4.88 +                raise XendError('Error reading config')
    4.89  
    4.90 -        return new_back.get('uuid')
    4.91 +            if state == 'Initialising':
    4.92 +                # PCI device attachment
    4.93 +                for j in range(num_olddevs):
    4.94 +                    if dev == self.readBackend(devid, 'dev-%i' % j):
    4.95 +                        raise XendError('Device %s is already connected.' % dev)
    4.96 +                log.debug('Attaching PCI device %s.' % dev)
    4.97 +                (domain, bus, slotfunc) = dev.split(':')
    4.98 +                (slot, func) = slotfunc.split('.')
    4.99 +                domain = parse_hex(domain)
   4.100 +                bus = parse_hex(bus)
   4.101 +                slot = parse_hex(slot)
   4.102 +                func = parse_hex(func)
   4.103 +                self.setupOneDevice(domain, bus, slot, func)
   4.104 +
   4.105 +                self.writeBackend(devid, 'dev-%i' % (num_olddevs + i), dev)
   4.106 +                self.writeBackend(devid, 'state-%i' % (num_olddevs + i),
   4.107 +                                  str(xenbusState['Initialising']))
   4.108 +                self.writeBackend(devid, 'num_devs', str(num_olddevs + i + 1))
   4.109 +
   4.110 +                # Update vslots
   4.111 +                if back['vslots'] is not None:
   4.112 +                    vslots = old_vslots + back['vslots']
   4.113 +                    self.writeBackend(devid, 'vslots', vslots)
   4.114 +
   4.115 +            elif state == 'Closing':
   4.116 +                # PCI device detachment
   4.117 +                found = False
   4.118 +                for j in range(num_olddevs):
   4.119 +                    if dev == self.readBackend(devid, 'dev-%i' % j):
   4.120 +                        found = True
   4.121 +                        log.debug('Detaching device %s' % dev)
   4.122 +                        self.writeBackend(devid, 'state-%i' % j,
   4.123 +                                          str(xenbusState['Closing']))
   4.124 +                if not found:
   4.125 +                    raise XendError('Device %s is not connected' % dev)
   4.126 +
   4.127 +                # Update vslots
   4.128 +                if back['vslots'] is not None:
   4.129 +                    vslots = old_vslots
   4.130 +                    for vslt in back['vslots'].split(';'):
   4.131 +                        if vslt != '':
   4.132 +                            vslots = vslots.replace(vslt + ';', '', 1)
   4.133 +                    if vslots == '':
   4.134 +                        self.removeBackend(devid, 'vslots')
   4.135 +                    else:
   4.136 +                        self.writeBackend(devid, 'vslots', vslots)
   4.137 +
   4.138 +            else:
   4.139 +                raise XendError('Error configuring device %s: invalid state %s'
   4.140 +                                % (dev,state))
   4.141 +
   4.142 +        self.writeBackend(devid, 'state', str(xenbusState['Reconfiguring']))
   4.143 +
   4.144 +        return self.readBackend(devid, 'uuid')
   4.145 +
   4.146  
   4.147      def getDeviceConfiguration(self, devid, transaction = None):
   4.148          result = DevController.getDeviceConfiguration(self, devid, transaction)
   4.149 @@ -136,7 +188,10 @@ class PciController(DevController):
   4.150  
   4.151                  #append vslot info
   4.152                  if vslots is not None:
   4.153 -                    dev_dict['vslt'] = slot_list[i]
   4.154 +                    try:
   4.155 +                        dev_dict['vslt'] = slot_list[i]
   4.156 +                    except IndexError:
   4.157 +                        dev_dict['vslt'] = '0x0'
   4.158  
   4.159                  pci_devs.append(dev_dict)
   4.160  
   4.161 @@ -171,7 +226,7 @@ class PciController(DevController):
   4.162  
   4.163          return sxpr    
   4.164  
   4.165 -    def setupDevice(self, domain, bus, slot, func):
   4.166 +    def setupOneDevice(self, domain, bus, slot, func):
   4.167          """ Attach I/O resources for device to frontend domain
   4.168          """
   4.169          fe_domid = self.getDomid()
   4.170 @@ -225,6 +280,116 @@ class PciController(DevController):
   4.171                  raise VmError(('pci: failed to configure irq on device '+
   4.172                              '%s - errno=%d')%(dev.name,rc))
   4.173  
   4.174 +    def setupDevice(self, config):
   4.175 +        """Setup devices from config
   4.176 +        """
   4.177 +        for pci_config in config.get('devs', []):
   4.178 +            domain = parse_hex(pci_config.get('domain', 0))
   4.179 +            bus = parse_hex(pci_config.get('bus', 0))
   4.180 +            slot = parse_hex(pci_config.get('slot', 0))
   4.181 +            func = parse_hex(pci_config.get('func', 0))            
   4.182 +            self.setupOneDevice(domain, bus, slot, func)
   4.183 +
   4.184 +        return
   4.185 +
   4.186 +    def cleanupOneDevice(self, domain, bus, slot, func):
   4.187 +        """ Detach I/O resources for device from frontend domain
   4.188 +        """
   4.189 +        fe_domid = self.getDomid()
   4.190 +
   4.191 +        try:
   4.192 +            dev = PciDevice(domain, bus, slot, func)
   4.193 +        except Exception, e:
   4.194 +            raise VmError("pci: failed to locate device and "+
   4.195 +                    "parse it's resources - "+str(e))
   4.196 +
   4.197 +        if dev.driver!='pciback':
   4.198 +            raise VmError(("pci: PCI Backend does not own device "+ \
   4.199 +                    "%s\n"+ \
   4.200 +                    "See the pciback.hide kernel "+ \
   4.201 +                    "command-line parameter or\n"+ \
   4.202 +                    "bind your slot/device to the PCI backend using sysfs" \
   4.203 +                    )%(dev.name))
   4.204 +
   4.205 +        for (start, size) in dev.ioports:
   4.206 +            log.debug('pci: disabling ioport 0x%x/0x%x'%(start,size))
   4.207 +            rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start,
   4.208 +                    nr_ports = size, allow_access = False)
   4.209 +            if rc<0:
   4.210 +                raise VmError(('pci: failed to configure I/O ports on device '+
   4.211 +                            '%s - errno=%d')%(dev.name,rc))
   4.212 +
   4.213 +        for (start, size) in dev.iomem:
   4.214 +            # Convert start/size from bytes to page frame sizes
   4.215 +            start_pfn = start>>PAGE_SHIFT
   4.216 +            # Round number of pages up to nearest page boundary (if not on one)
   4.217 +            nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
   4.218 +
   4.219 +            log.debug('pci: disabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
   4.220 +                    (start,size,start_pfn,nr_pfns))
   4.221 +            rc = xc.domain_iomem_permission(domid =  fe_domid,
   4.222 +                    first_pfn = start_pfn,
   4.223 +                    nr_pfns = nr_pfns,
   4.224 +                    allow_access = False)
   4.225 +            if rc<0:
   4.226 +                raise VmError(('pci: failed to configure I/O memory on device '+
   4.227 +                            '%s - errno=%d')%(dev.name,rc))
   4.228 +
   4.229 +        if dev.irq>0:
   4.230 +            log.debug('pci: disabling irq %d'%dev.irq)
   4.231 +            rc = xc.domain_irq_permission(domid =  fe_domid, pirq = dev.irq,
   4.232 +                    allow_access = False)
   4.233 +            if rc<0:
   4.234 +                raise VmError(('pci: failed to configure irq on device '+
   4.235 +                            '%s - errno=%d')%(dev.name,rc))
   4.236 +
   4.237 +    def cleanupDevice(self, devid):
   4.238 +        """ Detach I/O resources for device and cleanup xenstore nodes
   4.239 +        after reconfigure.
   4.240 +
   4.241 +        @param devid: The device ID
   4.242 +        @type devid:  int
   4.243 +        @return:      Return the number of devices connected
   4.244 +        @rtype:       int
   4.245 +        """
   4.246 +        num_devs = int(self.readBackend(devid, 'num_devs'))
   4.247 +        new_num_devs = 0
   4.248 +        for i in range(num_devs):
   4.249 +            state = int(self.readBackend(devid, 'state-%i' % i))
   4.250 +            if state == xenbusState['Closing']:
   4.251 +                # Detach I/O resources.
   4.252 +                dev = self.readBackend(devid, 'dev-%i' % i)
   4.253 +                (domain, bus, slotfunc) = dev.split(':')
   4.254 +                (slot, func) = slotfunc.split('.')
   4.255 +                domain = parse_hex(domain)
   4.256 +                bus = parse_hex(bus)
   4.257 +                slot = parse_hex(slot)
   4.258 +                func = parse_hex(func)            
   4.259 +                # In HVM case, I/O resources are disabled in ioemu.
   4.260 +                self.cleanupOneDevice(domain, bus, slot, func)
   4.261 +                # Remove xenstore nodes.
   4.262 +                self.removeBackend(devid, 'dev-%i' % i)
   4.263 +                self.removeBackend(devid, 'vdev-%i' % i)
   4.264 +                self.removeBackend(devid, 'state-%i' % i)
   4.265 +            else:
   4.266 +                if new_num_devs != i:
   4.267 +                    tmpdev = self.readBackend(devid, 'dev-%i' % i)
   4.268 +                    self.writeBackend(devid, 'dev-%i' % new_num_devs, tmpdev)
   4.269 +                    self.removeBackend(devid, 'dev-%i' % i)
   4.270 +                    tmpvdev = self.readBackend(devid, 'vdev-%i' % i)
   4.271 +                    if tmpvdev is not None:
   4.272 +                        self.writeBackend(devid, 'vdev-%i' % new_num_devs,
   4.273 +                                          tmpvdev)
   4.274 +                    self.removeBackend(devid, 'vdev-%i' % i)
   4.275 +                    tmpstate = self.readBackend(devid, 'state-%i' % i)
   4.276 +                    self.writeBackend(devid, 'state-%i' % new_num_devs, tmpstate)
   4.277 +                    self.removeBackend(devid, 'state-%i' % i)
   4.278 +                new_num_devs = new_num_devs + 1
   4.279 +
   4.280 +        self.writeBackend(devid, 'num_devs', str(new_num_devs))
   4.281 +
   4.282 +        return new_num_devs
   4.283 +
   4.284      def waitForBackend(self,devid):
   4.285          return (0, "ok - no hotplug")
   4.286  
     5.1 --- a/tools/python/xen/xm/main.py	Tue Mar 18 11:34:44 2008 +0000
     5.2 +++ b/tools/python/xen/xm/main.py	Tue Mar 18 11:39:21 2008 +0000
     5.3 @@ -175,11 +175,11 @@ SUBCOMMAND_HELP = {
     5.4      'vnet-delete'   :  ('<VnetId>', 'Delete a Vnet.'),
     5.5      'vnet-list'     :  ('[-l|--long]', 'List Vnets.'),
     5.6      'vtpm-list'     :  ('<Domain> [--long]', 'List virtual TPM devices.'),
     5.7 -    'pci-attach '   :  ('<Domain> <dom> <bus> <slot> <func> [virtual slot]',
     5.8 +    'pci-attach'    :  ('<Domain> <domain:bus:slot.func> [virtual slot]',
     5.9                          'Insert a new pass-through pci device.'),
    5.10 -    'pci-detach '   :  ('<Domain> <virtual slot>',
    5.11 +    'pci-detach'    :  ('<Domain> <domain:bus:slot.func>',
    5.12                          'Remove a domain\'s pass-through pci device.'),
    5.13 -    'pci-list'     :  ('<Domain>',
    5.14 +    'pci-list'      :  ('<Domain>',
    5.15                          'List pass-through pci devices for a domain.'),
    5.16  
    5.17      # security
    5.18 @@ -2232,29 +2232,37 @@ def xm_network_attach(args):
    5.19              vif.append(vif_param)
    5.20          server.xend.domain.device_create(dom, vif)
    5.21  
    5.22 -def parse_pci_configuration(args):
    5.23 +def parse_pci_configuration(args, state):
    5.24      dom = args[0]
    5.25 -
    5.26 -    if len(args) == 6:
    5.27 -        vslt = args[5]
    5.28 +    pci_dev_str = args[1]
    5.29 +    if len(args) == 3:
    5.30 +        vslt = args[2]
    5.31      else:
    5.32          vslt = '0x0' #chose a free virtual PCI slot
    5.33 -
    5.34 -    pci = ['pci',
    5.35 -          ['devs',
    5.36 -            [{'domain': "0x%x" % int(args[1], 16),
    5.37 -              'bus':    "0x%x" % int(args[2], 16),
    5.38 -              'slot':   "0x%x" % int(args[3], 16),
    5.39 -              'func':   "0x%x" % int(args[4], 16),
    5.40 -              'vslt':   "0x%x" % int(vslt,    16)}]
    5.41 -          ]]
    5.42 +    pci=['pci']
    5.43 +    pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
    5.44 +            r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
    5.45 +            r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
    5.46 +            r"(?P<func>[0-7])$", pci_dev_str)
    5.47 +    if pci_match == None:
    5.48 +        raise OptionError("Invalid argument: %s %s" % (pci_dev_str,vslt))
    5.49 +    pci_dev_info = pci_match.groupdict('0')
    5.50 +    try:
    5.51 +        pci.append(['dev', ['domain', '0x'+ pci_dev_info['domain']], \
    5.52 +                ['bus', '0x'+ pci_dev_info['bus']],
    5.53 +                ['slot', '0x'+ pci_dev_info['slot']],
    5.54 +                ['func', '0x'+ pci_dev_info['func']],
    5.55 +                ['vslt', '0x%x' % int(vslt, 16)]])
    5.56 +    except:
    5.57 +        raise OptionError("Invalid argument: %s %s" % (pci_dev_str,vslt))
    5.58 +    pci.append(['state', state])
    5.59  
    5.60      return (dom, pci)
    5.61  
    5.62  def xm_pci_attach(args):
    5.63 -    arg_check(args, 'pci-attach', 5, 6)
    5.64 -    (dom, pci) = parse_pci_configuration(args)
    5.65 -    server.xend.domain.device_create(dom, pci)
    5.66 +    arg_check(args, 'pci-attach', 2, 3)
    5.67 +    (dom, pci) = parse_pci_configuration(args, 'Initialising')
    5.68 +    server.xend.domain.device_configure(dom, pci)
    5.69  
    5.70  def detach(args, deviceClass):
    5.71      rm_cfg = True
    5.72 @@ -2319,12 +2327,11 @@ def xm_network_detach(args):
    5.73          arg_check(args, 'network-detach', 2, 3)
    5.74          detach(args, 'vif')
    5.75  
    5.76 -
    5.77  def xm_pci_detach(args):
    5.78      arg_check(args, 'pci-detach', 2)
    5.79 -    dom = args[0]
    5.80 -    dev = args[1]
    5.81 -    server.xend.domain.destroyDevice(dom, 'dpci', dev)
    5.82 +    (dom, pci) = parse_pci_configuration(args, 'Closing')
    5.83 +    server.xend.domain.device_configure(dom, pci)
    5.84 +
    5.85  
    5.86  def xm_vnet_list(args):
    5.87      xenapi_unsupported()