ia64/xen-unstable

view tools/python/xen/xend/server/pciif.py @ 19529:90d5fb694620

xend: fix a typo in pciif so PciController.unwatchAerState() works

Signed-off-by: Kouya Shimura <kouya@jp.fujitsu.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Apr 09 08:45:32 2009 +0100 (2009-04-09)
parents cb453e5a2f81
children 4eef30c14a14
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 vslots = ""
73 for pci_config in config.get('devs', []):
74 vslot = pci_config.get('vslot')
75 if vslot is not None:
76 vslots = vslots + vslot + ";"
78 domain = parse_hex(pci_config.get('domain', 0))
79 bus = parse_hex(pci_config.get('bus', 0))
80 slot = parse_hex(pci_config.get('slot', 0))
81 func = parse_hex(pci_config.get('func', 0))
82 vslot = parse_hex(pci_config.get('vslot', 0))
84 opts = pci_config.get('opts', '')
85 if len(opts) > 0:
86 opts = map(lambda (x, y): x+'='+y, opts)
87 opts = reduce(lambda x, y: x+','+y, opts)
88 back['opts-%i' % pcidevid] = opts
90 back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%01x" % \
91 (domain, bus, slot, func)
92 back['uuid-%i' % pcidevid] = pci_config.get('uuid', '')
93 back['vslot-%i' % pcidevid] = "%02x" % vslot
94 pcidevid += 1
96 if vslots != "":
97 back['vslots'] = vslots
99 back['num_devs']=str(pcidevid)
100 back['uuid'] = config.get('uuid','')
101 if 'pci_msitranslate' in self.vm.info['platform']:
102 back['msitranslate']=str(self.vm.info['platform']['pci_msitranslate'])
103 if 'pci_power_mgmt' in self.vm.info['platform']:
104 back['power_mgmt']=str(self.vm.info['platform']['pci_power_mgmt'])
106 return (0, back, {})
109 def reconfigureDevice(self, _, config):
110 """@see DevController.reconfigureDevice"""
111 (devid, back, front) = self.getDeviceDetails(config)
112 num_devs = int(back['num_devs'])
113 states = config.get('states', [])
115 old_vslots = self.readBackend(devid, 'vslots')
116 if old_vslots is None:
117 old_vslots = ''
118 num_olddevs = int(self.readBackend(devid, 'num_devs'))
120 for i in range(num_devs):
121 try:
122 dev = back['dev-%i' % i]
123 state = states[i]
124 uuid = back['uuid-%i' %i]
125 opts = ''
126 if 'opts-%i' % i in back:
127 opts = back['opts-%i' % i]
128 except:
129 raise XendError('Error reading config')
131 if state == 'Initialising':
132 # PCI device attachment
133 for j in range(num_olddevs):
134 if dev == self.readBackend(devid, 'dev-%i' % j):
135 raise XendError('Device %s is already connected.' % dev)
136 log.debug('Attaching PCI device %s.' % dev)
137 (domain, bus, slotfunc) = dev.split(':')
138 (slot, func) = slotfunc.split('.')
139 domain = parse_hex(domain)
140 bus = parse_hex(bus)
141 slot = parse_hex(slot)
142 func = parse_hex(func)
143 self.setupOneDevice(domain, bus, slot, func)
145 self.writeBackend(devid, 'dev-%i' % (num_olddevs + i), dev)
146 self.writeBackend(devid, 'state-%i' % (num_olddevs + i),
147 str(xenbusState['Initialising']))
148 self.writeBackend(devid, 'uuid-%i' % (num_olddevs + i), uuid)
149 if len(opts) > 0:
150 self.writeBackend(devid, 'opts-%i' % (num_olddevs + i), opts)
151 self.writeBackend(devid, 'num_devs', str(num_olddevs + i + 1))
153 # Update vslots
154 if back['vslots'] is not None:
155 vslots = old_vslots + back['vslots']
156 self.writeBackend(devid, 'vslots', vslots)
158 elif state == 'Closing':
159 # PCI device detachment
160 found = False
161 for j in range(num_olddevs):
162 if dev == self.readBackend(devid, 'dev-%i' % j):
163 found = True
164 log.debug('Detaching device %s' % dev)
165 self.writeBackend(devid, 'state-%i' % j,
166 str(xenbusState['Closing']))
167 if not found:
168 raise XendError('Device %s is not connected' % dev)
170 # Update vslots
171 if back.get('vslots') is not None:
172 vslots = old_vslots
173 for vslot in back['vslots'].split(';'):
174 if vslot != '':
175 vslots = vslots.replace(vslot + ';', '', 1)
176 if vslots == '':
177 self.removeBackend(devid, 'vslots')
178 else:
179 self.writeBackend(devid, 'vslots', vslots)
181 else:
182 raise XendError('Error configuring device %s: invalid state %s'
183 % (dev,state))
185 self.writeBackend(devid, 'state', str(xenbusState['Reconfiguring']))
187 return self.readBackend(devid, 'uuid')
190 def getDeviceConfiguration(self, devid, transaction = None):
191 result = DevController.getDeviceConfiguration(self, devid, transaction)
192 num_devs = self.readBackend(devid, 'num_devs')
193 pci_devs = []
195 vslots = self.readBackend(devid, 'vslots')
196 if vslots is not None:
197 if vslots[-1] == ";":
198 vslots = vslots[:-1]
199 slot_list = vslots.split(';')
201 for i in range(int(num_devs)):
202 dev_config = self.readBackend(devid, 'dev-%d' % i)
204 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" +
205 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" +
206 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" +
207 r"(?P<func>[0-7]{1,2})$", dev_config)
209 if pci_match!=None:
210 pci_dev_info = pci_match.groupdict()
211 dev_dict = {'domain': '0x%(domain)s' % pci_dev_info,
212 'bus': '0x%(bus)s' % pci_dev_info,
213 'slot': '0x%(slot)s' % pci_dev_info,
214 'func': '0x%(func)s' % pci_dev_info}
216 # Per device uuid info
217 dev_dict['uuid'] = self.readBackend(devid, 'uuid-%d' % i)
219 #append vslot info
220 if vslots is not None:
221 try:
222 dev_dict['vslot'] = slot_list[i]
223 except IndexError:
224 dev_dict['vslot'] = AUTO_PHP_SLOT_STR
226 pci_devs.append(dev_dict)
228 result['devs'] = pci_devs
229 result['uuid'] = self.readBackend(devid, 'uuid')
230 return result
232 def configuration(self, devid, transaction = None):
233 """Returns SXPR for devices on domain.
235 @note: we treat this dict especially to convert to
236 SXP because it is not a straight dict of strings."""
238 configDict = self.getDeviceConfiguration(devid, transaction)
239 sxpr = [self.deviceClass]
241 # remove devs
242 devs = configDict.pop('devs', [])
244 for dev in devs:
245 dev_sxpr = ['dev']
246 for dev_item in dev.items():
247 dev_sxpr.append(list(dev_item))
248 sxpr.append(dev_sxpr)
250 for key, val in configDict.items():
251 if type(val) == type(list()):
252 for v in val:
253 sxpr.append([key, v])
254 else:
255 sxpr.append([key, val])
257 return sxpr
259 def CheckSiblingDevices(self, domid, dev):
260 """ Check if all sibling devices of dev are owned by pciback
261 """
262 if not self.vm.info.is_hvm():
263 return
265 group_str = xc.get_device_group(domid, dev.domain, dev.bus, dev.slot, dev.func)
266 if group_str == "":
267 return
269 #group string format xx:xx.x,xx:xx.x,
270 devstr_len = group_str.find(',')
271 for i in range(0, len(group_str), devstr_len + 1):
272 (bus, slotfunc) = group_str[i:i + devstr_len].split(':')
273 (slot, func) = slotfunc.split('.')
274 b = parse_hex(bus)
275 d = parse_hex(slot)
276 f = parse_hex(func)
277 try:
278 sdev = PciDevice(dev.domain, b, d, f)
279 except Exception, e:
280 #no dom0 drivers bound to sdev
281 continue
283 if sdev.driver!='pciback':
284 raise VmError(("pci: PCI Backend does not own\n "+ \
285 "sibling device %s of device %s\n"+ \
286 "See the pciback.hide kernel "+ \
287 "command-line parameter or\n"+ \
288 "bind your slot/device to the PCI backend using sysfs" \
289 )%(sdev.name, dev.name))
290 return
292 def setupOneDevice(self, domain, bus, slot, func):
293 """ Attach I/O resources for device to frontend domain
294 """
295 fe_domid = self.getDomid()
297 try:
298 dev = PciDevice(domain, bus, slot, func)
299 except Exception, e:
300 raise VmError("pci: failed to locate device and "+
301 "parse it's resources - "+str(e))
303 if dev.driver!='pciback':
304 raise VmError(("pci: PCI Backend does not own device "+ \
305 "%s\n"+ \
306 "See the pciback.hide kernel "+ \
307 "command-line parameter or\n"+ \
308 "bind your slot/device to the PCI backend using sysfs" \
309 )%(dev.name))
311 if dev.has_non_page_aligned_bar and arch.type != "ia64":
312 raise VmError("pci: %s: non-page-aligned MMIO BAR found." % dev.name)
314 self.CheckSiblingDevices(fe_domid, dev)
316 # We don't do FLR when we create domain and hotplug device into guest,
317 # namely, we only do FLR when we destroy domain or hotplug device from
318 # guest. This is mainly to work around the race condition in hotplug code
319 # paths. See the changeset's description for details.
320 # if arch.type != "ia64":
321 # dev.do_FLR()
323 PCIQuirk(dev.vendor, dev.device, dev.subvendor, dev.subdevice, domain,
324 bus, slot, func)
326 if not self.vm.info.is_hvm():
327 # Setup IOMMU device assignment
328 pci_str = "0x%x, 0x%x, 0x%x, 0x%x" % (domain, bus, slot, func)
329 bdf = xc.assign_device(fe_domid, pci_str)
330 if bdf > 0:
331 raise VmError("Failed to assign device to IOMMU (%x:%x.%x)"
332 % (bus, slot, func))
333 log.debug("pci: assign device %x:%x.%x" % (bus, slot, func))
335 for (start, size) in dev.ioports:
336 log.debug('pci: enabling ioport 0x%x/0x%x'%(start,size))
337 rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start,
338 nr_ports = size, allow_access = True)
339 if rc<0:
340 raise VmError(('pci: failed to configure I/O ports on device '+
341 '%s - errno=%d')%(dev.name,rc))
343 for (start, size) in dev.iomem:
344 # Convert start/size from bytes to page frame sizes
345 start_pfn = start>>PAGE_SHIFT
346 # Round number of pages up to nearest page boundary (if not on one)
347 nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
349 log.debug('pci: enabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
350 (start,size,start_pfn,nr_pfns))
351 rc = xc.domain_iomem_permission(domid = fe_domid,
352 first_pfn = start_pfn,
353 nr_pfns = nr_pfns,
354 allow_access = True)
355 if rc<0:
356 raise VmError(('pci: failed to configure I/O memory on device '+
357 '%s - errno=%d')%(dev.name,rc))
359 if dev.msix:
360 for (start, size) in dev.msix_iomem:
361 start_pfn = start>>PAGE_SHIFT
362 nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
363 log.debug('pci-msix: remove permission for 0x%x/0x%x 0x%x/0x%x' % \
364 (start,size, start_pfn, nr_pfns))
365 rc = xc.domain_iomem_permission(domid = fe_domid,
366 first_pfn = start_pfn,
367 nr_pfns = nr_pfns,
368 allow_access = False)
369 if rc<0:
370 raise VmError(('pci: failed to remove msi-x iomem'))
372 rc = xc.physdev_map_pirq(domid = fe_domid,
373 index = dev.irq,
374 pirq = dev.irq)
375 if rc < 0:
376 raise VmError(('pci: failed to map irq on device '+
377 '%s - errno=%d')%(dev.name,rc))
378 if dev.irq>0:
379 log.debug('pci: enabling irq %d'%dev.irq)
380 rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq,
381 allow_access = True)
382 if rc<0:
383 raise VmError(('pci: failed to configure irq on device '+
384 '%s - errno=%d')%(dev.name,rc))
386 def setupDevice(self, config):
387 """Setup devices from config
388 """
389 pci_str_list = []
390 pci_dev_list = []
391 for pci_config in config.get('devs', []):
392 domain = parse_hex(pci_config.get('domain', 0))
393 bus = parse_hex(pci_config.get('bus', 0))
394 slot = parse_hex(pci_config.get('slot', 0))
395 func = parse_hex(pci_config.get('func', 0))
396 pci_str = '%04x:%02x:%02x.%01x' % (domain, bus, slot, func)
397 pci_str_list = pci_str_list + [pci_str]
398 pci_dev_list = pci_dev_list + [(domain, bus, slot, func)]
400 for (domain, bus, slot, func) in pci_dev_list:
401 try:
402 dev = PciDevice(domain, bus, slot, func)
403 except Exception, e:
404 raise VmError("pci: failed to locate device and "+
405 "parse it's resources - "+str(e))
406 if (dev.dev_type == DEV_TYPE_PCIe_ENDPOINT) and not dev.pcie_flr:
407 if dev.bus == 0:
408 # We cope with this case by using the Dstate transition
409 # method or some vendor specific methods for now.
410 err_msg = 'pci: %s: it is on bus 0, but has no PCIe' +\
411 ' FLR Capability. Will try the Dstate transition'+\
412 ' method or some vendor specific methods if available.'
413 log.warn(err_msg % dev.name)
414 else:
415 funcs = dev.find_all_the_multi_functions()
416 for f in funcs:
417 if not f in pci_str_list:
418 (f_dom, f_bus, f_slot, f_func) = parse_pci_name(f)
419 f_pci_str = '0x%x,0x%x,0x%x,0x%x' % \
420 (f_dom, f_bus, f_slot, f_func)
421 # f has been assigned to other guest?
422 if xc.test_assign_device(0, f_pci_str) != 0:
423 err_msg = 'pci: %s must be co-assigned to' + \
424 ' the same guest with %s'
425 raise VmError(err_msg % (f, dev.name))
426 elif dev.dev_type == DEV_TYPE_PCI:
427 if dev.bus == 0 or arch.type == "ia64":
428 if not dev.pci_af_flr:
429 # We cope with this case by using the Dstate transition
430 # method or some vendor specific methods for now.
431 err_msg = 'pci: %s: it is on bus 0, but has no PCI' +\
432 ' Advanced Capabilities for FLR. Will try the'+\
433 ' Dstate transition method or some vendor' +\
434 ' specific methods if available.'
435 log.warn(err_msg % dev.name)
436 else:
437 # All devices behind the uppermost PCI/PCI-X bridge must be\
438 # co-assigned to the same guest.
439 devs_str = dev.find_coassigned_pci_devices(True)
440 # Remove the element 0 which is a bridge
441 del devs_str[0]
443 for s in devs_str:
444 if not s in pci_str_list:
445 (s_dom, s_bus, s_slot, s_func) = parse_pci_name(s)
446 s_pci_str = '0x%x,0x%x,0x%x,0x%x' % \
447 (s_dom, s_bus, s_slot, s_func)
448 # s has been assigned to other guest?
449 if xc.test_assign_device(0, s_pci_str) != 0:
450 err_msg = 'pci: %s must be co-assigned to the'+\
451 ' same guest with %s'
452 raise VmError(err_msg % (s, dev.name))
454 for (domain, bus, slot, func) in pci_dev_list:
455 self.setupOneDevice(domain, bus, slot, func)
456 wPath = '/local/domain/0/backend/pci/%u/0/aerState' % (self.getDomid())
457 self.aerStateWatch = xswatch(wPath, self._handleAerStateWatch)
458 log.debug('pci: register aer watch %s', wPath)
459 return
461 def _handleAerStateWatch(self, _):
462 log.debug('XendDomainInfo.handleAerStateWatch')
463 if self.getDomid() == 0:
464 raise XendError('Domain 0 cannot be shutdown')
465 readPath = '/local/domain/0/backend/pci/%u/0/aerState' % (self.getDomid())
466 action = xstransact.Read(readPath)
467 if action and action=='aerfail':
468 log.debug('shutdown domain because of aer handle error')
469 self.vm.shutdown('poweroff')
470 return True
473 def cleanupOneDevice(self, domain, bus, slot, func):
474 """ Detach I/O resources for device from frontend domain
475 """
476 fe_domid = self.getDomid()
478 try:
479 dev = PciDevice(domain, bus, slot, func)
480 except Exception, e:
481 raise VmError("pci: failed to locate device and "+
482 "parse it's resources - "+str(e))
484 if dev.driver!='pciback':
485 raise VmError(("pci: PCI Backend does not own device "+ \
486 "%s\n"+ \
487 "See the pciback.hide kernel "+ \
488 "command-line parameter or\n"+ \
489 "bind your slot/device to the PCI backend using sysfs" \
490 )%(dev.name))
492 if not self.vm.info.is_hvm():
493 pci_str = "0x%x, 0x%x, 0x%x, 0x%x" % (domain, bus, slot, func)
494 bdf = xc.deassign_device(fe_domid, pci_str)
495 if bdf > 0:
496 raise VmError("Failed to deassign device from IOMMU (%x:%x.%x)"
497 % (bus, slot, func))
498 log.debug("pci: deassign device %x:%x.%x" % (bus, slot, func))
500 for (start, size) in dev.ioports:
501 log.debug('pci: disabling ioport 0x%x/0x%x'%(start,size))
502 rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start,
503 nr_ports = size, allow_access = False)
504 if rc<0:
505 raise VmError(('pci: failed to configure I/O ports on device '+
506 '%s - errno=%d')%(dev.name,rc))
508 for (start, size) in dev.iomem:
509 # Convert start/size from bytes to page frame sizes
510 start_pfn = start>>PAGE_SHIFT
511 # Round number of pages up to nearest page boundary (if not on one)
512 nr_pfns = (size+(PAGE_SIZE-1))>>PAGE_SHIFT
514 log.debug('pci: disabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
515 (start,size,start_pfn,nr_pfns))
516 rc = xc.domain_iomem_permission(domid = fe_domid,
517 first_pfn = start_pfn,
518 nr_pfns = nr_pfns,
519 allow_access = False)
520 if rc<0:
521 raise VmError(('pci: failed to configure I/O memory on device '+
522 '%s - errno=%d')%(dev.name,rc))
524 if dev.irq>0:
525 log.debug('pci: disabling irq %d'%dev.irq)
526 rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq,
527 allow_access = False)
528 if rc<0:
529 raise VmError(('pci: failed to configure irq on device '+
530 '%s - errno=%d')%(dev.name,rc))
531 dev.do_FLR()
533 def cleanupDevice(self, devid):
534 """ Detach I/O resources for device and cleanup xenstore nodes
535 after reconfigure.
537 @param devid: The device ID
538 @type devid: int
539 @return: Return the number of devices connected
540 @rtype: int
541 """
542 num_devs = int(self.readBackend(devid, 'num_devs'))
543 new_num_devs = 0
544 for i in range(num_devs):
545 state = int(self.readBackend(devid, 'state-%i' % i))
546 if state == xenbusState['Closing']:
547 # Detach I/O resources.
548 dev = self.readBackend(devid, 'dev-%i' % i)
549 (domain, bus, slotfunc) = dev.split(':')
550 (slot, func) = slotfunc.split('.')
551 domain = parse_hex(domain)
552 bus = parse_hex(bus)
553 slot = parse_hex(slot)
554 func = parse_hex(func)
555 # In HVM case, I/O resources are disabled in ioemu.
556 self.cleanupOneDevice(domain, bus, slot, func)
557 # Remove xenstore nodes.
558 self.removeBackend(devid, 'dev-%i' % i)
559 self.removeBackend(devid, 'vdev-%i' % i)
560 self.removeBackend(devid, 'state-%i' % i)
561 self.removeBackend(devid, 'uuid-%i' % i)
562 tmpopts = self.readBackend(devid, 'opts-%i' % i)
563 if tmpopts is not None:
564 self.removeBackend(devid, 'opts-%i' % i)
565 else:
566 if new_num_devs != i:
567 tmpdev = self.readBackend(devid, 'dev-%i' % i)
568 self.writeBackend(devid, 'dev-%i' % new_num_devs, tmpdev)
569 self.removeBackend(devid, 'dev-%i' % i)
570 tmpvdev = self.readBackend(devid, 'vdev-%i' % i)
571 if tmpvdev is not None:
572 self.writeBackend(devid, 'vdev-%i' % new_num_devs,
573 tmpvdev)
574 self.removeBackend(devid, 'vdev-%i' % i)
575 tmpstate = self.readBackend(devid, 'state-%i' % i)
576 self.writeBackend(devid, 'state-%i' % new_num_devs, tmpstate)
577 self.removeBackend(devid, 'state-%i' % i)
578 tmpuuid = self.readBackend(devid, 'uuid-%i' % i)
579 self.writeBackend(devid, 'uuid-%i' % new_num_devs, tmpuuid)
580 self.removeBackend(devid, 'uuid-%i' % i)
581 tmpopts = self.readBackend(devid, 'opts-%i' % i)
582 if tmpopts is not None:
583 self.removeBackend(devid, 'opts-%i' % i)
584 new_num_devs = new_num_devs + 1
586 self.writeBackend(devid, 'num_devs', str(new_num_devs))
588 return new_num_devs
590 def destroyDevice(self, devid, force):
591 DevController.destroyDevice(self, devid, True)
592 log.debug('pci: unregister aer watch')
593 self.unwatchAerState()
595 def unwatchAerState(self):
596 """Remove the watch on the domain's aerState node, if any."""
597 try:
598 try:
599 if self.aerStateWatch:
600 self.aerStateWatch.unwatch()
601 finally:
602 self.aerStateWatch = None
603 except:
604 log.exception("Unwatching aerState failed.")
606 def waitForBackend(self,devid):
607 return (0, "ok - no hotplug")
609 def migrate(self, config, network, dst, step, domName):
610 raise XendError('Migration not permitted with assigned PCI device.')