Simon Horman
2009-May-29 06:56 UTC
[Xen-devel] [patch] xend: hot-plug PCI devices at boot-time
Currently there are two interfaces to pass-through PCI devices: 1. A method driven through per-device xenstore entries that is used at boot-time 2. An event-based method used for hot-plug. This seems somewhat redundant and makes extending the code cumbersome and prone to error - often the change needs to be made twice, in two different ways. This patch unifies PCI pass-through by using the existing event-based method at boot-time. There is a companion patch for qemu-xen that removes its support for the now unused boot-time protocol. Cc: Edwin Zhai <edwin.zhai@intel.com> Cc: Masaki Kanno <kanno.masaki@jp.fujitsu.com> Signed-off-by: Simon Horman <horms@verge.net.au> --- tools/python/xen/xend/XendConfig.py | 14 +++- tools/python/xen/xend/XendDomainInfo.py | 65 +++++++++++---------- tools/python/xen/xend/image.py | 16 ++++- tools/python/xen/xend/server/pciif.py | 94 +++++++++++-------------------- 4 files changed, 95 insertions(+), 94 deletions(-) Index: xen-unstable.hg/tools/python/xen/xend/XendDomainInfo.py ==================================================================--- xen-unstable.hg.orig/tools/python/xen/xend/XendDomainInfo.py 2009-05-29 16:39:00.000000000 +1000 +++ xen-unstable.hg/tools/python/xen/xend/XendDomainInfo.py 2009-05-29 16:43:22.000000000 +1000 @@ -601,7 +601,7 @@ class XendDomainInfo: asserts.isCharConvertible(key) self.storeDom("control/sysrq", ''%c'' % key) - def sync_pcidev_info(self): + def pci_device_configure_boot(self): if not self.info.is_hvm(): return @@ -615,33 +615,12 @@ class XendDomainInfo: dev_uuid = sxp.child_value(dev_info, ''uuid'') pci_conf = self.info[''devices''][dev_uuid][1] pci_devs = pci_conf[''devs''] + request = map(lambda x: + self.info.pci_convert_dict_to_sxp(x, ''Initialising'', + ''Booting''), pci_devs) - count = 0 - vslots = None - while vslots is None and count < 20: - vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots" - % (self.getDomid(), devid)) - time.sleep(0.1) - count += 1 - if vslots is None: - log.error("Device model didn''t tell the vslots for PCI device") - return - - #delete last delim - if vslots[-1] == ";": - vslots = vslots[:-1] - - slot_list = vslots.split('';'') - if len(slot_list) != len(pci_devs): - log.error("Device model''s pci dev num dismatch") - return - - #update the vslot info - count = 0; - for x in pci_devs: - x[''vslot''] = slot_list[count] - count += 1 - + for i in request: + self.pci_device_configure(i) def hvm_pci_device_create(self, dev_config): log.debug("XendDomainInfo.hvm_pci_device_create: %s" @@ -741,6 +720,23 @@ class XendDomainInfo: " assigned to other domain." \ )% (pci_device.name, self.info[''name_label''], pci_str)) + return self.hvm_pci_device_insert_dev(new_dev) + + def hvm_pci_device_insert(self, dev_config): + log.debug("XendDomainInfo.hvm_pci_device_insert: %s" + % scrub_password(dev_config)) + + if not self.info.is_hvm(): + raise VmError("hvm_pci_device_create called on non-HVM guest") + + new_dev = dev_config[''devs''][0] + + return self.hvm_pci_device_insert_dev(new_dev) + + def hvm_pci_device_insert_dev(self, new_dev): + log.debug("XendDomainInfo.hvm_pci_device_insert_dev: %s" + % scrub_password(new_dev)) + if self.domid is not None: opts = '''' if ''opts'' in new_dev and len(new_dev[''opts'']) > 0: @@ -752,7 +748,10 @@ class XendDomainInfo: new_dev[''bus''], new_dev[''slot''], new_dev[''func''], - new_dev[''requested_vslot''], + # vslot will be used when activating a + # previously activated domain. + # Otherwise requested_vslot will be used. + assigned_or_requested_vslot(new_dev), opts) self.image.signalDeviceModel(''pci-ins'', ''pci-inserted'', bdf_str) @@ -827,6 +826,7 @@ class XendDomainInfo: return False pci_state = sxp.child_value(dev_sxp, ''state'') + pci_sub_state = sxp.child_value(dev_sxp, ''sub_state'') existing_dev_info = self._getDeviceInfo_pci(devid) if existing_dev_info is None and pci_state != ''Initialising'': @@ -840,7 +840,10 @@ class XendDomainInfo: if self.info.is_hvm(): if pci_state == ''Initialising'': # HVM PCI device attachment - vslot = self.hvm_pci_device_create(dev_config) + if pci_sub_state == ''Booting'': + vslot = self.hvm_pci_device_insert(dev_config) + else: + vslot = self.hvm_pci_device_create(dev_config) # Update vslot dev[''vslot''] = vslot for n in sxp.children(pci_dev): @@ -907,7 +910,7 @@ class XendDomainInfo: continue new_dev_sxp.append(cur_dev) - if pci_state == ''Initialising'': + if pci_state == ''Initialising'' and pci_sub_state != ''Booting'': for new_dev in sxp.children(dev_sxp, ''dev''): new_dev_sxp.append(new_dev) @@ -2246,7 +2249,7 @@ class XendDomainInfo: self.image.createDeviceModel() #if have pass-through devs, need the virtual pci slots info from qemu - self.sync_pcidev_info() + self.pci_device_configure_boot() def _releaseDevices(self, suspend = False): """Release all domain''s devices. Nothrow guarantee.""" Index: xen-unstable.hg/tools/python/xen/xend/XendConfig.py ==================================================================--- xen-unstable.hg.orig/tools/python/xen/xend/XendConfig.py 2009-05-29 16:39:00.000000000 +1000 +++ xen-unstable.hg/tools/python/xen/xend/XendConfig.py 2009-05-29 16:43:22.000000000 +1000 @@ -1669,6 +1669,13 @@ class XendConfig(dict): return '''' + def pci_convert_dict_to_sxp(self, dev, state, sub_state = None): + sxp = [''pci'', [''dev''] + map(lambda (x, y): [x, y], dev.items()), + [''state'', state]] + if sub_state != None: + sxp.append([''sub_state'', sub_state]) + return sxp + def pci_convert_sxp_to_dict(self, dev_sxp): """Convert pci device sxp to dict @param dev_sxp: device configuration @@ -1723,9 +1730,10 @@ class XendConfig(dict): pci_dev_info[opt] = val except (TypeError, ValueError): pass - # append uuid for each pci device. - dpci_uuid = pci_dev_info.get(''uuid'', uuid.createString()) - pci_dev_info[''uuid''] = dpci_uuid + # append uuid to each pci device that does''t already have one. + if not pci_dev_info.has_key(''uuid''): + dpci_uuid = pci_dev_info.get(''uuid'', uuid.createString()) + pci_dev_info[''uuid''] = dpci_uuid pci_devs.append(pci_dev_info) dev_config[''devs''] = pci_devs Index: xen-unstable.hg/tools/python/xen/xend/server/pciif.py ==================================================================--- xen-unstable.hg.orig/tools/python/xen/xend/server/pciif.py 2009-05-29 16:39:00.000000000 +1000 +++ xen-unstable.hg/tools/python/xen/xend/server/pciif.py 2009-05-29 16:43:22.000000000 +1000 @@ -69,12 +69,7 @@ class PciController(DevController): """@see DevController.getDeviceDetails""" back = {} pcidevid = 0 - vslots = "" for pci_config in config.get(''devs'', []): - attached_vslot = pci_config.get(''vslot'') - if attached_vslot is not None: - vslots = vslots + attached_vslot + ";" - domain = parse_hex(pci_config.get(''domain'', 0)) bus = parse_hex(pci_config.get(''bus'', 0)) slot = parse_hex(pci_config.get(''slot'', 0)) @@ -93,9 +88,6 @@ class PciController(DevController): back[''vslot-%i'' % pcidevid] = "%02x" % vslot pcidevid += 1 - if vslots != "": - back[''vslots''] = vslots - back[''num_devs'']=str(pcidevid) back[''uuid''] = config.get(''uuid'','''') if ''pci_msitranslate'' in self.vm.info[''platform'']: @@ -105,16 +97,17 @@ class PciController(DevController): return (0, back, {}) + def reconfigureDevice_find(self, devid, nsearch_dev, match_dev): + for j in range(nsearch_dev): + if match_dev == self.readBackend(devid, ''dev-%i'' % j): + return j + return None def reconfigureDevice(self, _, config): """@see DevController.reconfigureDevice""" (devid, back, front) = self.getDeviceDetails(config) num_devs = int(back[''num_devs'']) states = config.get(''states'', []) - - old_vslots = self.readBackend(devid, ''vslots'') - if old_vslots is None: - old_vslots = '''' num_olddevs = int(self.readBackend(devid, ''num_devs'')) for i in range(num_devs): @@ -129,11 +122,15 @@ class PciController(DevController): raise XendError(''Error reading config'') if state == ''Initialising'': - # PCI device attachment - for j in range(num_olddevs): - if dev == self.readBackend(devid, ''dev-%i'' % j): - raise XendError(''Device %s is already connected.'' % dev) - log.debug(''Attaching PCI device %s.'' % dev) + devno = self.reconfigureDevice_find(devid, num_olddevs, dev) + if devno == None: + devno = num_olddevs + i + log.debug(''Attaching PCI device %s.'' % dev) + attaching = True + else: + log.debug(''Reconfiguring PCI device %s.'' % dev) + attaching = False + (domain, bus, slotfunc) = dev.split('':'') (slot, func) = slotfunc.split(''.'') domain = parse_hex(domain) @@ -142,41 +139,28 @@ class PciController(DevController): func = parse_hex(func) self.setupOneDevice(domain, bus, slot, func) - self.writeBackend(devid, ''dev-%i'' % (num_olddevs + i), dev) - self.writeBackend(devid, ''state-%i'' % (num_olddevs + i), + self.writeBackend(devid, ''dev-%i'' % devno, dev) + self.writeBackend(devid, ''state-%i'' % devno, str(xenbusState[''Initialising''])) - self.writeBackend(devid, ''uuid-%i'' % (num_olddevs + i), uuid) + self.writeBackend(devid, ''uuid-%i'' % devno, uuid) if len(opts) > 0: - self.writeBackend(devid, ''opts-%i'' % (num_olddevs + i), opts) - self.writeBackend(devid, ''num_devs'', str(num_olddevs + i + 1)) - - # Update vslots - if back[''vslots''] is not None: - vslots = old_vslots + back[''vslots''] - self.writeBackend(devid, ''vslots'', vslots) + self.writeBackend(devid, ''opts-%i'' % devno, opts) + if back.has_key(''vslot-%i'' % i): + self.writeBackend(devid, ''vslot-%i'' % devno, + back[''vslot-%i'' % i]) + + # If a device is being attached then num_devs will grow + if attaching: + self.writeBackend(devid, ''num_devs'', str(devno + 1)) elif state == ''Closing'': # PCI device detachment - found = False - for j in range(num_olddevs): - if dev == self.readBackend(devid, ''dev-%i'' % j): - found = True - log.debug(''Detaching device %s'' % dev) - self.writeBackend(devid, ''state-%i'' % j, - str(xenbusState[''Closing''])) - if not found: + devno = self.reconfigureDevice_find(devid, num_olddevs, dev) + if devno == None: raise XendError(''Device %s is not connected'' % dev) - - # Update vslots - if back.get(''vslots'') is not None: - vslots = old_vslots - for vslot in back[''vslots''].split('';''): - if vslot != '''': - vslots = vslots.replace(vslot + '';'', '''', 1) - if vslots == '''': - self.removeBackend(devid, ''vslots'') - else: - self.writeBackend(devid, ''vslots'', vslots) + log.debug(''Detaching device %s'' % dev) + self.writeBackend(devid, ''state-%i'' % devno, + str(xenbusState[''Closing''])) else: raise XendError(''Error configuring device %s: invalid state %s'' @@ -191,12 +175,6 @@ class PciController(DevController): result = DevController.getDeviceConfiguration(self, devid, transaction) num_devs = self.readBackend(devid, ''num_devs'') pci_devs = [] - - vslots = self.readBackend(devid, ''vslots'') - if vslots is not None: - if vslots[-1] == ";": - vslots = vslots[:-1] - slot_list = vslots.split('';'') for i in range(int(num_devs)): dev_config = self.readBackend(devid, ''dev-%d'' % i) @@ -215,13 +193,11 @@ class PciController(DevController): # Per device uuid info dev_dict[''uuid''] = self.readBackend(devid, ''uuid-%d'' % i) - - #append vslot info - if vslots is not None: - try: - dev_dict[''vslot''] = slot_list[i] - except IndexError: - dev_dict[''vslot''] = AUTO_PHP_SLOT_STR + vslot = self.readBackend(devid, ''vslot-%d'' % i) + if vslot != None: + dev_dict[''vslot''] = self.readBackend(devid, ''vslot-%d'' % i) + else: + dev_dict[''vslot''] = AUTO_PHP_SLOT_STR #append opts info opts = self.readBackend(devid, ''opts-%d'' % i) Index: xen-unstable.hg/tools/python/xen/xend/image.py ==================================================================--- xen-unstable.hg.orig/tools/python/xen/xend/image.py 2009-05-29 16:39:00.000000000 +1000 +++ xen-unstable.hg/tools/python/xen/xend/image.py 2009-05-29 16:43:22.000000000 +1000 @@ -454,8 +454,22 @@ class ImageHandler: if cmd is '''' or ret is '''': raise VmError(''need valid command and result when signal device model'') - orig_state = xstransact.Read("/local/domain/0/device-model/%i/state" + count = 0 + while True: + orig_state = xstransact.Read("/local/domain/0/device-model/%i/state" % self.vm.getDomid()) + # This can occur right after start-up + if orig_state != None: + break + + log.debug(''signalDeviceModel: orig_state is None, retrying'') + + time.sleep(0.1) + count += 1 + if count < 100: + continue + + VmError(''Device model isn\''t ready for commands'') if par is not None: xstransact.Store("/local/domain/0/device-model/%i" _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel