ia64/xen-unstable

view tools/python/xen/xend/server/pciif.py @ 19776:9e36ef77f658

xend: pass-through: Common parse_pci_name()

Share some parsing code between different parts of xm.

This has the side-effect that the device specification for
hot-plug may now include the VSLOT and OPTS as per device
specifictions in the domain configuration file.

SEQ:BUS:DEV.FUNC[,OPT...]

e.g. 0000:00:01.00@6

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Jun 17 07:34:59 2009 +0100 (2009-06-17)
parents 4bc1347b9b86
children 902df7680e2e
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005 XenSource Ltd
17 #============================================================================
20 import types
21 import time
23 from xen.xend import sxp
24 from xen.xend import arch
25 from xen.xend.XendError import VmError
26 from xen.xend.XendLogging import log
27 from xen.xend.XendConstants import *
29 from xen.xend.server.DevController import DevController
30 from xen.xend.server.DevConstants import xenbusState
32 import xen.lowlevel.xc
34 from xen.util.pci import *
35 import resource
36 import re
38 from xen.xend.server.pciquirk import *
39 from xen.xend.xenstore.xstransact import xstransact
40 from xen.xend.xenstore.xswatch import xswatch
42 xc = xen.lowlevel.xc.xc()
44 #Calculate PAGE_SHIFT: number of bits to shift an address to get the page number
45 PAGE_SIZE = resource.getpagesize()
46 PAGE_SHIFT = 0
47 t = PAGE_SIZE
48 while not (t&1):
49 t>>=1
50 PAGE_SHIFT+=1
52 def parse_hex(val):
53 try:
54 if isinstance(val, types.StringTypes):
55 return int(val, 16)
56 else:
57 return val
58 except ValueError:
59 return None
61 class PciController(DevController):
63 def __init__(self, vm):
64 self.aerStateWatch = None
65 DevController.__init__(self, vm)
68 def getDeviceDetails(self, config):
69 """@see DevController.getDeviceDetails"""
70 back = {}
71 pcidevid = 0
72 for pci_config in config.get('devs', []):
73 domain = parse_hex(pci_config.get('domain', 0))
74 bus = parse_hex(pci_config.get('bus', 0))
75 slot = parse_hex(pci_config.get('slot', 0))
76 func = parse_hex(pci_config.get('func', 0))
77 vslot = parse_hex(pci_config.get('vslot',
78 '0x' + AUTO_PHP_SLOT_STR))
80 if pci_config.has_key('opts'):
81 opts = serialise_pci_opts(pci_config['opts'])
82 back['opts-%i' % pcidevid] = opts
84 back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%01x" % \
85 (domain, bus, slot, func)
86 back['uuid-%i' % pcidevid] = pci_config.get('uuid', '')
87 back['vslot-%i' % pcidevid] = "%02x" % vslot
88 pcidevid += 1
90 back['num_devs']=str(pcidevid)
91 back['uuid'] = config.get('uuid','')
92 if 'pci_msitranslate' in self.vm.info['platform']:
93 back['msitranslate']=str(self.vm.info['platform']['pci_msitranslate'])
94 if 'pci_power_mgmt' in self.vm.info['platform']:
95 back['power_mgmt']=str(self.vm.info['platform']['pci_power_mgmt'])
97 return (0, back, {})
99 def reconfigureDevice_find(self, devid, nsearch_dev, match_dev):
100 for j in range(nsearch_dev):
101 if match_dev == self.readBackend(devid, 'dev-%i' % j):
102 return j
103 return None
105 def reconfigureDevice(self, _, config):
106 """@see DevController.reconfigureDevice"""
107 (devid, back, front) = self.getDeviceDetails(config)
108 num_devs = int(back['num_devs'])
109 states = config.get('states', [])
110 num_olddevs = int(self.readBackend(devid, 'num_devs'))
112 for i in range(num_devs):
113 try:
114 dev = back['dev-%i' % i]
115 state = states[i]
116 uuid = back['uuid-%i' %i]
117 opts = ''
118 if 'opts-%i' % i in back:
119 opts = back['opts-%i' % i]
120 except:
121 raise XendError('Error reading config')
123 if state == 'Initialising':
124 devno = self.reconfigureDevice_find(devid, num_olddevs, dev)
125 if devno == None:
126 devno = num_olddevs + i
127 log.debug('Attaching PCI device %s.' % dev)
128 attaching = True
129 else:
130 log.debug('Reconfiguring PCI device %s.' % dev)
131 attaching = False
133 self.setupOneDevice(parse_pci_name(dev))
135 self.writeBackend(devid, 'dev-%i' % devno, dev)
136 self.writeBackend(devid, 'state-%i' % devno,
137 str(xenbusState['Initialising']))
138 self.writeBackend(devid, 'uuid-%i' % devno, uuid)
139 if len(opts) > 0:
140 self.writeBackend(devid, 'opts-%i' % devno, opts)
141 if back.has_key('vslot-%i' % i):
142 self.writeBackend(devid, 'vslot-%i' % devno,
143 back['vslot-%i' % i])
145 # If a device is being attached then num_devs will grow
146 if attaching:
147 self.writeBackend(devid, 'num_devs', str(devno + 1))
149 elif state == 'Closing':
150 # PCI device detachment
151 devno = self.reconfigureDevice_find(devid, num_olddevs, dev)
152 if devno == None:
153 raise XendError('Device %s is not connected' % dev)
154 log.debug('Detaching device %s' % dev)
155 self.writeBackend(devid, 'state-%i' % devno,
156 str(xenbusState['Closing']))
158 else:
159 raise XendError('Error configuring device %s: invalid state %s'
160 % (dev,state))
162 self.writeBackend(devid, 'state', str(xenbusState['Reconfiguring']))
164 return self.readBackend(devid, 'uuid')
167 def getDeviceConfiguration(self, devid, transaction = None):
168 result = DevController.getDeviceConfiguration(self, devid, transaction)
169 num_devs = self.readBackend(devid, 'num_devs')
170 pci_devs = []
172 for i in range(int(num_devs)):
173 dev_config = self.readBackend(devid, 'dev-%d' % i)
175 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" +
176 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" +
177 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" +
178 r"(?P<func>[0-7]{1,2})$", dev_config)
180 if pci_match!=None:
181 pci_dev_info = pci_match.groupdict()
182 dev_dict = {'domain': '0x%(domain)s' % pci_dev_info,
183 'bus': '0x%(bus)s' % pci_dev_info,
184 'slot': '0x%(slot)s' % pci_dev_info,
185 'func': '0x%(func)s' % pci_dev_info}
187 # Per device uuid info
188 dev_dict['uuid'] = self.readBackend(devid, 'uuid-%d' % i)
189 dev_dict['vslot'] = '0x%s' % \
190 self.readBackend(devid, 'vslot-%d' % i)
192 #append opts info
193 opts = self.readBackend(devid, 'opts-%d' % i)
194 if opts is not None:
195 dev_dict['opts'] = opts
197 pci_devs.append(dev_dict)
199 result['devs'] = pci_devs
200 result['uuid'] = self.readBackend(devid, 'uuid')
201 return result
203 def configuration(self, devid, transaction = None):
204 """Returns SXPR for devices on domain.
206 @note: we treat this dict especially to convert to
207 SXP because it is not a straight dict of strings."""
209 configDict = self.getDeviceConfiguration(devid, transaction)
210 sxpr = [self.deviceClass]
212 # remove devs
213 devs = configDict.pop('devs', [])
215 for dev in devs:
216 dev_sxpr = ['dev']
217 for dev_key, dev_val in dev.items():
218 if dev_key == 'opts':
219 opts_sxpr = pci_opts_list_to_sxp(split_pci_opts(dev_val))
220 dev_sxpr = sxp.merge(dev_sxpr, opts_sxpr)
221 else:
222 dev_sxpr.append([dev_key, dev_val])
223 sxpr.append(dev_sxpr)
225 for key, val in configDict.items():
226 if type(val) == type(list()):
227 for v in val:
228 sxpr.append([key, v])
229 else:
230 sxpr.append([key, val])
232 return sxpr
234 def CheckSiblingDevices(self, domid, dev):
235 """ Check if all sibling devices of dev are owned by pciback
236 """
237 if not self.vm.info.is_hvm():
238 return
240 group_str = xc.get_device_group(domid, dev.domain, dev.bus, dev.slot, dev.func)
241 if group_str == "":
242 return
244 #group string format xx:xx.x,xx:xx.x,
245 for i in group_str.split(','):
246 if i == '':
247 continue
248 pci_dev = parse_pci_name(i)
249 pci_dev['domain'] = '%04x' % dev.domain
250 try:
251 sdev = PciDevice(pci_dev)
252 except Exception, e:
253 #no dom0 drivers bound to sdev
254 continue
256 if sdev.driver!='pciback':
257 raise VmError(("pci: PCI Backend does not own\n "+ \
258 "sibling device %s of device %s\n"+ \
259 "See the pciback.hide kernel "+ \
260 "command-line parameter or\n"+ \
261 "bind your slot/device to the PCI backend using sysfs" \
262 )%(sdev.name, dev.name))
263 return
265 def setupOneDevice(self, pci_dev):
266 """ Attach I/O resources for device to frontend domain
267 """
268 fe_domid = self.getDomid()
270 try:
271 dev = PciDevice(pci_dev)
272 except Exception, e:
273 raise VmError("pci: failed to locate device and "+
274 "parse it's resources - "+str(e))
276 if dev.driver!='pciback':
277 raise VmError(("pci: PCI Backend does not own device "+ \
278 "%s\n"+ \
279 "See the pciback.hide kernel "+ \
280 "command-line parameter or\n"+ \
281 "bind your slot/device to the PCI backend using sysfs" \
282 )%(dev.name))
284 if dev.has_non_page_aligned_bar and arch.type != "ia64":
285 raise VmError("pci: %s: non-page-aligned MMIO BAR found." % dev.name)
287 self.CheckSiblingDevices(fe_domid, dev)
289 # We don't do FLR when we create domain and hotplug device into guest,
290 # namely, we only do FLR when we destroy domain or hotplug device from
291 # guest. This is mainly to work around the race condition in hotplug code
292 # paths. See the changeset's description for details.
293 # if arch.type != "ia64":
294 # dev.do_FLR()
296 PCIQuirk(dev)
298 if not self.vm.info.is_hvm():
299 # Setup IOMMU device assignment
300 bdf = xc.assign_device(fe_domid, pci_dict_to_xc_str(pci_dev))
301 pci_str = pci_dict_to_bdf_str(pci_dev)
302 if bdf > 0:
303 raise VmError("Failed to assign device to IOMMU (%s)" % pci_str)
304 log.debug("pci: assign device %s" % pci_str)
306 for (start, size) in dev.ioports:
307 log.debug('pci: enabling ioport 0x%x/0x%x'%(start,size))
308 rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start,
309 nr_ports = size, allow_access = True)
310 if rc<0:
311 raise VmError(('pci: failed to configure I/O ports on device '+
312 '%s - errno=%d')%(dev.name,rc))
314 for (start, size) in dev.iomem:
315 # Convert start/size from bytes to page frame sizes
316 start_pfn = start>>PAGE_SHIFT
317 # Round number of pages up to nearest page boundary (if not on one)
318 nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
320 log.debug('pci: enabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
321 (start,size,start_pfn,nr_pfns))
322 rc = xc.domain_iomem_permission(domid = fe_domid,
323 first_pfn = start_pfn,
324 nr_pfns = nr_pfns,
325 allow_access = True)
326 if rc<0:
327 raise VmError(('pci: failed to configure I/O memory on device '+
328 '%s - errno=%d')%(dev.name,rc))
330 if dev.msix:
331 for (start, size) in dev.msix_iomem:
332 start_pfn = start>>PAGE_SHIFT
333 nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
334 log.debug('pci-msix: remove permission for 0x%x/0x%x 0x%x/0x%x' % \
335 (start,size, start_pfn, nr_pfns))
336 rc = xc.domain_iomem_permission(domid = fe_domid,
337 first_pfn = start_pfn,
338 nr_pfns = nr_pfns,
339 allow_access = False)
340 if rc<0:
341 raise VmError(('pci: failed to remove msi-x iomem'))
343 rc = xc.physdev_map_pirq(domid = fe_domid,
344 index = dev.irq,
345 pirq = dev.irq)
346 if rc < 0:
347 raise VmError(('pci: failed to map irq on device '+
348 '%s - errno=%d')%(dev.name,rc))
349 if dev.irq>0:
350 log.debug('pci: enabling irq %d'%dev.irq)
351 rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq,
352 allow_access = True)
353 if rc<0:
354 raise VmError(('pci: failed to configure irq on device '+
355 '%s - errno=%d')%(dev.name,rc))
357 def setupDevice(self, config):
358 """Setup devices from config
359 """
360 pci_dev_list = config.get('devs', [])
361 pci_str_list = map(pci_dict_to_bdf_str, pci_dev_list)
363 if len(pci_str_list) != len(set(pci_str_list)):
364 raise VmError('pci: duplicate devices specified in guest config?')
366 for pci_dev in pci_dev_list:
367 try:
368 dev = PciDevice(pci_dev)
369 except Exception, e:
370 raise VmError("pci: failed to locate device and "+
371 "parse it's resources - "+str(e))
372 if (dev.dev_type == DEV_TYPE_PCIe_ENDPOINT) and not dev.pcie_flr:
373 if dev.bus == 0:
374 # We cope with this case by using the Dstate transition
375 # method or some vendor specific methods for now.
376 err_msg = 'pci: %s: it is on bus 0, but has no PCIe' +\
377 ' FLR Capability. Will try the Dstate transition'+\
378 ' method or some vendor specific methods if available.'
379 log.warn(err_msg % dev.name)
380 else:
381 funcs = dev.find_all_the_multi_functions()
382 dev.devs_check_driver(funcs)
383 for f in funcs:
384 if not f in pci_str_list:
385 (f_dom, f_bus, f_slot, f_func) = parse_pci_name(f)
386 f_pci_str = '0x%x,0x%x,0x%x,0x%x' % \
387 (f_dom, f_bus, f_slot, f_func)
388 # f has been assigned to other guest?
389 if xc.test_assign_device(0, f_pci_str) != 0:
390 err_msg = 'pci: %s must be co-assigned to' + \
391 ' the same guest with %s'
392 raise VmError(err_msg % (f, dev.name))
393 elif dev.dev_type == DEV_TYPE_PCI:
394 if dev.bus == 0 or arch.type == "ia64":
395 if not dev.pci_af_flr:
396 # We cope with this case by using the Dstate transition
397 # method or some vendor specific methods for now.
398 err_msg = 'pci: %s: it is on bus 0, but has no PCI' +\
399 ' Advanced Capabilities for FLR. Will try the'+\
400 ' Dstate transition method or some vendor' +\
401 ' specific methods if available.'
402 log.warn(err_msg % dev.name)
403 else:
404 # All devices behind the uppermost PCI/PCI-X bridge must be\
405 # co-assigned to the same guest.
406 devs_str = dev.find_coassigned_pci_devices(True)
407 # Remove the element 0 which is a bridge
408 del devs_str[0]
410 dev.devs_check_driver(devs_str)
411 for s in devs_str:
412 if not s in pci_str_list:
413 s_pci_str = pci_dict_to_bdf_str(parse_pci_name(s))
414 # s has been assigned to other guest?
415 if xc.test_assign_device(0, s_pci_str) != 0:
416 err_msg = 'pci: %s must be co-assigned to the'+\
417 ' same guest with %s'
418 raise VmError(err_msg % (s, dev.name))
420 wPath = '/local/domain/0/backend/pci/%u/0/aerState' % (self.getDomid())
421 self.aerStateWatch = xswatch(wPath, self._handleAerStateWatch)
422 log.debug('pci: register aer watch %s', wPath)
423 return
425 def _handleAerStateWatch(self, _):
426 log.debug('XendDomainInfo.handleAerStateWatch')
427 if self.getDomid() == 0:
428 raise XendError('Domain 0 cannot be shutdown')
429 readPath = '/local/domain/0/backend/pci/%u/0/aerState' % (self.getDomid())
430 action = xstransact.Read(readPath)
431 if action and action=='aerfail':
432 log.debug('shutdown domain because of aer handle error')
433 self.vm.shutdown('poweroff')
434 return True
437 def cleanupOneDevice(self, pci_dev):
438 """ Detach I/O resources for device from frontend domain
439 """
440 fe_domid = self.getDomid()
442 try:
443 dev = PciDevice(pci_dev)
444 except Exception, e:
445 raise VmError("pci: failed to locate device and "+
446 "parse it's resources - "+str(e))
448 if dev.driver!='pciback':
449 raise VmError(("pci: PCI Backend does not own device "+ \
450 "%s\n"+ \
451 "See the pciback.hide kernel "+ \
452 "command-line parameter or\n"+ \
453 "bind your slot/device to the PCI backend using sysfs" \
454 )%(dev.name))
456 # Need to do FLR here before deassign device in order to terminate
457 # DMA transaction, etc
458 dev.do_FLR()
460 bdf = xc.deassign_device(fe_domid, pci_dict_to_xc_str(pci_dev))
461 pci_str = pci_dict_to_bdf_str(pci_dev)
462 if bdf > 0:
463 raise VmError("Failed to deassign device from IOMMU (%s)" % pci_str)
464 log.debug("pci: Deassign device %s" % pci_str)
466 for (start, size) in dev.ioports:
467 log.debug('pci: disabling ioport 0x%x/0x%x'%(start,size))
468 rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start,
469 nr_ports = size, allow_access = False)
470 if rc<0:
471 raise VmError(('pci: failed to configure I/O ports on device '+
472 '%s - errno=%d')%(dev.name,rc))
474 for (start, size) in dev.iomem:
475 # Convert start/size from bytes to page frame sizes
476 start_pfn = start>>PAGE_SHIFT
477 # Round number of pages up to nearest page boundary (if not on one)
478 nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
480 log.debug('pci: disabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
481 (start,size,start_pfn,nr_pfns))
482 rc = xc.domain_iomem_permission(domid = fe_domid,
483 first_pfn = start_pfn,
484 nr_pfns = nr_pfns,
485 allow_access = False)
486 if rc<0:
487 raise VmError(('pci: failed to configure I/O memory on device '+
488 '%s - errno=%d')%(dev.name,rc))
490 if dev.irq>0:
491 log.debug('pci: disabling irq %d'%dev.irq)
492 rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq,
493 allow_access = False)
494 if rc<0:
495 raise VmError(('pci: failed to configure irq on device '+
496 '%s - errno=%d')%(dev.name,rc))
498 def cleanupDevice(self, devid):
499 """ Detach I/O resources for device and cleanup xenstore nodes
500 after reconfigure.
502 @param devid: The device ID
503 @type devid: int
504 @return: Return the number of devices connected
505 @rtype: int
506 """
507 num_devs = int(self.readBackend(devid, 'num_devs'))
508 new_num_devs = 0
509 for i in range(num_devs):
510 state = int(self.readBackend(devid, 'state-%i' % i))
511 if state == xenbusState['Closing']:
512 # Detach I/O resources.
513 pci_dev = parse_pci_name(self.readBackend(devid, 'dev-%i' % i))
514 # In HVM case, I/O resources are disabled in ioemu.
515 self.cleanupOneDevice(pci_dev)
516 # Remove xenstore nodes.
517 list = ['dev', 'vdev', 'state', 'uuid', 'vslot']
518 if self.readBackend(devid, 'opts-%i' % i) is not None:
519 list.append('opts')
520 for key in list:
521 self.removeBackend(devid, '%s-%i' % (key, i))
522 else:
523 new_num_devs = new_num_devs + 1
524 if new_num_devs == i + 1:
525 continue
527 list = ['dev', 'vdev', 'state', 'uuid', 'opts', 'vslot']
528 for key in list:
529 tmp = self.readBackend(devid, '%s-%i' % (key, i))
530 if tmp is None:
531 continue
532 self.removeBackend(devid, '%s-%i' % (key, i))
533 self.writeBackend(devid,
534 '%s-%i' % (key, new_num_devs - 1), tmp)
536 self.writeBackend(devid, 'num_devs', str(new_num_devs))
538 return new_num_devs
540 def destroyDevice(self, devid, force):
541 DevController.destroyDevice(self, devid, True)
542 log.debug('pci: unregister aer watch')
543 self.unwatchAerState()
545 def unwatchAerState(self):
546 """Remove the watch on the domain's aerState node, if any."""
547 try:
548 try:
549 if self.aerStateWatch:
550 self.aerStateWatch.unwatch()
551 finally:
552 self.aerStateWatch = None
553 except:
554 log.exception("Unwatching aerState failed.")
556 def waitForBackend(self,devid):
557 return (0, "ok - no hotplug")
559 def migrate(self, config, network, dst, step, domName):
560 raise XendError('Migration not permitted with assigned PCI device.')