debuggers.hg
changeset 17261:3f407392da49
tools: Add PV passthrough PCI device hotplug support.
Signed-off-by: Yosuke Iwamatsu <y-iwamatsu@ab.jp.nec.com>
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 + old_vslots = self.readBackend(devid, 'vslots') 4.71 + if old_vslots is None: 4.72 + old_vslots = '' 4.73 + num_olddevs = int(self.readBackend(devid, 'num_devs')) 4.74 + 4.75 + for i in range(num_devs): 4.76 + try: 4.77 + dev = back['dev-%i' % i] 4.78 + state = states[i] 4.79 + except: 4.80 + raise XendError('Error reading config') 4.81 + 4.82 + if state == 'Initialising': 4.83 + # PCI device attachment 4.84 + for j in range(num_olddevs): 4.85 + if dev == self.readBackend(devid, 'dev-%i' % j): 4.86 + raise XendError('Device %s is already connected.' % dev) 4.87 + log.debug('Attaching PCI device %s.' % dev) 4.88 + (domain, bus, slotfunc) = dev.split(':') 4.89 + (slot, func) = slotfunc.split('.') 4.90 + domain = parse_hex(domain) 4.91 + bus = parse_hex(bus) 4.92 + slot = parse_hex(slot) 4.93 + func = parse_hex(func) 4.94 + self.setupOneDevice(domain, bus, slot, func) 4.95 + 4.96 + self.writeBackend(devid, 'dev-%i' % (num_olddevs + i), dev) 4.97 + self.writeBackend(devid, 'state-%i' % (num_olddevs + i), 4.98 + str(xenbusState['Initialising'])) 4.99 + self.writeBackend(devid, 'num_devs', str(num_olddevs + i + 1)) 4.100 4.101 - #create new devices config 4.102 - num_devs = new_back['num_devs'] 4.103 - for i in range(int(num_devs)): 4.104 - dev_no = 'dev-%d' % i 4.105 - self.writeBackend(devid, dev_no, new_back[dev_no]) 4.106 - self.writeBackend(devid, 'num_devs', num_devs) 4.107 + # Update vslots 4.108 + if back['vslots'] is not None: 4.109 + vslots = old_vslots + back['vslots'] 4.110 + self.writeBackend(devid, 'vslots', vslots) 4.111 + 4.112 + elif state == 'Closing': 4.113 + # PCI device detachment 4.114 + found = False 4.115 + for j in range(num_olddevs): 4.116 + if dev == self.readBackend(devid, 'dev-%i' % j): 4.117 + found = True 4.118 + log.debug('Detaching device %s' % dev) 4.119 + self.writeBackend(devid, 'state-%i' % j, 4.120 + str(xenbusState['Closing'])) 4.121 + if not found: 4.122 + raise XendError('Device %s is not connected' % dev) 4.123 4.124 - if new_back['vslots'] is not None: 4.125 - self.writeBackend(devid, 'vslots', new_back['vslots']) 4.126 + # Update vslots 4.127 + if back['vslots'] is not None: 4.128 + vslots = old_vslots 4.129 + for vslt in back['vslots'].split(';'): 4.130 + if vslt != '': 4.131 + vslots = vslots.replace(vslt + ';', '', 1) 4.132 + if vslots == '': 4.133 + self.removeBackend(devid, 'vslots') 4.134 + else: 4.135 + self.writeBackend(devid, 'vslots', vslots) 4.136 4.137 - return new_back.get('uuid') 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()