ia64/xen-unstable

changeset 19679:ec2bc4b9fa32

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.

Signed-off-by: Simon Horman <horms@verge.net.au>
author Keir Fraser <keir.fraser@citrix.com>
date Fri May 29 09:32:02 2009 +0100 (2009-05-29)
parents ef6911934b6f
children 401a793c4b42
files tools/python/xen/xend/XendConfig.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/image.py tools/python/xen/xend/server/pciif.py
line diff
     1.1 --- a/tools/python/xen/xend/XendConfig.py	Fri May 29 09:29:58 2009 +0100
     1.2 +++ b/tools/python/xen/xend/XendConfig.py	Fri May 29 09:32:02 2009 +0100
     1.3 @@ -1669,6 +1669,13 @@ class XendConfig(dict):
     1.4  
     1.5          return ''
     1.6  
     1.7 +    def pci_convert_dict_to_sxp(self, dev, state, sub_state = None):
     1.8 +        sxp =  ['pci', ['dev'] + map(lambda (x, y): [x, y], dev.items()),
     1.9 +                ['state', state]]
    1.10 +        if sub_state != None:
    1.11 +            sxp.append(['sub_state', sub_state])
    1.12 +        return sxp
    1.13 +
    1.14      def pci_convert_sxp_to_dict(self, dev_sxp):
    1.15          """Convert pci device sxp to dict
    1.16          @param dev_sxp: device configuration
    1.17 @@ -1723,9 +1730,10 @@ class XendConfig(dict):
    1.18                      pci_dev_info[opt] = val
    1.19                  except (TypeError, ValueError):
    1.20                      pass
    1.21 -            # append uuid for each pci device.
    1.22 -            dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
    1.23 -            pci_dev_info['uuid'] = dpci_uuid
    1.24 +            # append uuid to each pci device that does't already have one.
    1.25 +            if not pci_dev_info.has_key('uuid'):
    1.26 +                dpci_uuid = pci_dev_info.get('uuid', uuid.createString())
    1.27 +                pci_dev_info['uuid'] = dpci_uuid
    1.28              pci_devs.append(pci_dev_info)
    1.29          dev_config['devs'] = pci_devs 
    1.30  
     2.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Fri May 29 09:29:58 2009 +0100
     2.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Fri May 29 09:32:02 2009 +0100
     2.3 @@ -601,7 +601,7 @@ class XendDomainInfo:
     2.4          asserts.isCharConvertible(key)
     2.5          self.storeDom("control/sysrq", '%c' % key)
     2.6  
     2.7 -    def sync_pcidev_info(self):
     2.8 +    def pci_device_configure_boot(self):
     2.9  
    2.10          if not self.info.is_hvm():
    2.11              return
    2.12 @@ -615,33 +615,12 @@ class XendDomainInfo:
    2.13          dev_uuid = sxp.child_value(dev_info, 'uuid')
    2.14          pci_conf = self.info['devices'][dev_uuid][1]
    2.15          pci_devs = pci_conf['devs']
    2.16 -
    2.17 -        count = 0
    2.18 -        vslots = None
    2.19 -        while vslots is None and count < 20:
    2.20 -            vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots"
    2.21 -                              % (self.getDomid(), devid))
    2.22 -            time.sleep(0.1)
    2.23 -            count += 1
    2.24 -        if vslots is None:
    2.25 -            log.error("Device model didn't tell the vslots for PCI device")
    2.26 -            return
    2.27 -
    2.28 -        #delete last delim
    2.29 -        if vslots[-1] == ";":
    2.30 -            vslots = vslots[:-1]
    2.31 -
    2.32 -        slot_list = vslots.split(';')
    2.33 -        if len(slot_list) != len(pci_devs):
    2.34 -            log.error("Device model's pci dev num dismatch")
    2.35 -            return
    2.36 -
    2.37 -        #update the vslot info
    2.38 -        count = 0;
    2.39 -        for x in pci_devs:
    2.40 -            x['vslot'] = slot_list[count]
    2.41 -            count += 1
    2.42 -
    2.43 +        request = map(lambda x:
    2.44 +                      self.info.pci_convert_dict_to_sxp(x, 'Initialising',
    2.45 +                                                        'Booting'), pci_devs)
    2.46 +
    2.47 +        for i in request:
    2.48 +                self.pci_device_configure(i)
    2.49  
    2.50      def hvm_pci_device_create(self, dev_config):
    2.51          log.debug("XendDomainInfo.hvm_pci_device_create: %s"
    2.52 @@ -741,6 +720,23 @@ class XendDomainInfo:
    2.53                      " assigned to other domain." \
    2.54                      )% (pci_device.name, self.info['name_label'], pci_str))
    2.55  
    2.56 +        return self.hvm_pci_device_insert_dev(new_dev)
    2.57 +
    2.58 +    def hvm_pci_device_insert(self, dev_config):
    2.59 +        log.debug("XendDomainInfo.hvm_pci_device_insert: %s"
    2.60 +                  % scrub_password(dev_config))
    2.61 +
    2.62 +        if not self.info.is_hvm():
    2.63 +            raise VmError("hvm_pci_device_create called on non-HVM guest")
    2.64 +
    2.65 +        new_dev = dev_config['devs'][0]
    2.66 +
    2.67 +        return self.hvm_pci_device_insert_dev(new_dev)
    2.68 +
    2.69 +    def hvm_pci_device_insert_dev(self, new_dev):
    2.70 +        log.debug("XendDomainInfo.hvm_pci_device_insert_dev: %s"
    2.71 +                  % scrub_password(new_dev))
    2.72 +
    2.73          if self.domid is not None:
    2.74              opts = ''
    2.75              if 'opts' in new_dev and len(new_dev['opts']) > 0:
    2.76 @@ -752,7 +748,10 @@ class XendDomainInfo:
    2.77                  new_dev['bus'],
    2.78                  new_dev['slot'],
    2.79                  new_dev['func'],
    2.80 -                new_dev['requested_vslot'],
    2.81 +                # vslot will be used when activating a
    2.82 +                # previously activated domain.
    2.83 +                # Otherwise requested_vslot will be used.
    2.84 +                assigned_or_requested_vslot(new_dev),
    2.85                  opts)
    2.86              self.image.signalDeviceModel('pci-ins', 'pci-inserted', bdf_str)
    2.87  
    2.88 @@ -827,6 +826,7 @@ class XendDomainInfo:
    2.89              return False
    2.90  
    2.91          pci_state = sxp.child_value(dev_sxp, 'state')
    2.92 +        pci_sub_state = sxp.child_value(dev_sxp, 'sub_state')
    2.93          existing_dev_info = self._getDeviceInfo_pci(devid)
    2.94  
    2.95          if existing_dev_info is None and pci_state != 'Initialising':
    2.96 @@ -840,7 +840,10 @@ class XendDomainInfo:
    2.97          if self.info.is_hvm():
    2.98              if pci_state == 'Initialising':
    2.99                  # HVM PCI device attachment
   2.100 -                vslot = self.hvm_pci_device_create(dev_config)
   2.101 +                if pci_sub_state == 'Booting':
   2.102 +                    vslot = self.hvm_pci_device_insert(dev_config)
   2.103 +                else:
   2.104 +                    vslot = self.hvm_pci_device_create(dev_config)
   2.105                  # Update vslot
   2.106                  dev['vslot'] = vslot
   2.107                  for n in sxp.children(pci_dev):
   2.108 @@ -907,7 +910,7 @@ class XendDomainInfo:
   2.109                          continue
   2.110                  new_dev_sxp.append(cur_dev)
   2.111  
   2.112 -            if pci_state == 'Initialising':
   2.113 +            if pci_state == 'Initialising' and pci_sub_state != 'Booting':
   2.114                  for new_dev in sxp.children(dev_sxp, 'dev'):
   2.115                      new_dev_sxp.append(new_dev)
   2.116  
   2.117 @@ -2246,7 +2249,7 @@ class XendDomainInfo:
   2.118              self.image.createDeviceModel()
   2.119  
   2.120          #if have pass-through devs, need the virtual pci slots info from qemu
   2.121 -        self.sync_pcidev_info()
   2.122 +        self.pci_device_configure_boot()
   2.123  
   2.124      def _releaseDevices(self, suspend = False):
   2.125          """Release all domain's devices.  Nothrow guarantee."""
     3.1 --- a/tools/python/xen/xend/image.py	Fri May 29 09:29:58 2009 +0100
     3.2 +++ b/tools/python/xen/xend/image.py	Fri May 29 09:32:02 2009 +0100
     3.3 @@ -454,8 +454,22 @@ class ImageHandler:
     3.4          if cmd is '' or ret is '':
     3.5              raise VmError('need valid command and result when signal device model')
     3.6  
     3.7 -        orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
     3.8 +        count = 0
     3.9 +        while True:
    3.10 +            orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
    3.11                                  % self.vm.getDomid())
    3.12 +            # This can occur right after start-up
    3.13 +            if orig_state != None:
    3.14 +                break
    3.15 +
    3.16 +            log.debug('signalDeviceModel: orig_state is None, retrying')
    3.17 +
    3.18 +            time.sleep(0.1)
    3.19 +            count += 1
    3.20 +            if count < 100:
    3.21 +                continue
    3.22 +
    3.23 +            VmError('Device model isn\'t ready for commands')
    3.24  
    3.25          if par is not None:
    3.26              xstransact.Store("/local/domain/0/device-model/%i"
     4.1 --- a/tools/python/xen/xend/server/pciif.py	Fri May 29 09:29:58 2009 +0100
     4.2 +++ b/tools/python/xen/xend/server/pciif.py	Fri May 29 09:32:02 2009 +0100
     4.3 @@ -69,12 +69,7 @@ class PciController(DevController):
     4.4          """@see DevController.getDeviceDetails"""
     4.5          back = {}
     4.6          pcidevid = 0
     4.7 -        vslots = ""
     4.8          for pci_config in config.get('devs', []):
     4.9 -            attached_vslot = pci_config.get('vslot')
    4.10 -            if attached_vslot is not None:
    4.11 -                vslots = vslots + attached_vslot + ";"
    4.12 -
    4.13              domain = parse_hex(pci_config.get('domain', 0))
    4.14              bus = parse_hex(pci_config.get('bus', 0))
    4.15              slot = parse_hex(pci_config.get('slot', 0))
    4.16 @@ -93,9 +88,6 @@ class PciController(DevController):
    4.17              back['vslot-%i' % pcidevid] = "%02x" % vslot
    4.18              pcidevid += 1
    4.19  
    4.20 -        if vslots != "":
    4.21 -            back['vslots'] = vslots
    4.22 -
    4.23          back['num_devs']=str(pcidevid)
    4.24          back['uuid'] = config.get('uuid','')
    4.25          if 'pci_msitranslate' in self.vm.info['platform']:
    4.26 @@ -105,16 +97,17 @@ class PciController(DevController):
    4.27  
    4.28          return (0, back, {})
    4.29  
    4.30 +    def reconfigureDevice_find(self, devid, nsearch_dev, match_dev):
    4.31 +        for j in range(nsearch_dev):
    4.32 +            if match_dev == self.readBackend(devid, 'dev-%i' % j):
    4.33 +                return j
    4.34 +        return None
    4.35  
    4.36      def reconfigureDevice(self, _, config):
    4.37          """@see DevController.reconfigureDevice"""
    4.38          (devid, back, front) = self.getDeviceDetails(config)
    4.39          num_devs = int(back['num_devs'])
    4.40          states = config.get('states', [])
    4.41 -
    4.42 -        old_vslots = self.readBackend(devid, 'vslots')
    4.43 -        if old_vslots is None:
    4.44 -            old_vslots = ''
    4.45          num_olddevs = int(self.readBackend(devid, 'num_devs'))
    4.46  
    4.47          for i in range(num_devs):
    4.48 @@ -129,11 +122,15 @@ class PciController(DevController):
    4.49                  raise XendError('Error reading config')
    4.50  
    4.51              if state == 'Initialising':
    4.52 -                # PCI device attachment
    4.53 -                for j in range(num_olddevs):
    4.54 -                    if dev == self.readBackend(devid, 'dev-%i' % j):
    4.55 -                        raise XendError('Device %s is already connected.' % dev)
    4.56 -                log.debug('Attaching PCI device %s.' % dev)
    4.57 +                devno = self.reconfigureDevice_find(devid, num_olddevs, dev)
    4.58 +                if devno == None:
    4.59 +                    devno = num_olddevs + i
    4.60 +                    log.debug('Attaching PCI device %s.' % dev)
    4.61 +                    attaching = True
    4.62 +                else:
    4.63 +                    log.debug('Reconfiguring PCI device %s.' % dev)
    4.64 +                    attaching = False
    4.65 +
    4.66                  (domain, bus, slotfunc) = dev.split(':')
    4.67                  (slot, func) = slotfunc.split('.')
    4.68                  domain = parse_hex(domain)
    4.69 @@ -142,41 +139,28 @@ class PciController(DevController):
    4.70                  func = parse_hex(func)
    4.71                  self.setupOneDevice(domain, bus, slot, func)
    4.72  
    4.73 -                self.writeBackend(devid, 'dev-%i' % (num_olddevs + i), dev)
    4.74 -                self.writeBackend(devid, 'state-%i' % (num_olddevs + i),
    4.75 +                self.writeBackend(devid, 'dev-%i' % devno, dev)
    4.76 +                self.writeBackend(devid, 'state-%i' % devno,
    4.77                                    str(xenbusState['Initialising']))
    4.78 -                self.writeBackend(devid, 'uuid-%i' % (num_olddevs + i), uuid)
    4.79 +                self.writeBackend(devid, 'uuid-%i' % devno, uuid)
    4.80                  if len(opts) > 0:
    4.81 -                    self.writeBackend(devid, 'opts-%i' % (num_olddevs + i), opts)
    4.82 -                self.writeBackend(devid, 'num_devs', str(num_olddevs + i + 1))
    4.83 +                    self.writeBackend(devid, 'opts-%i' % devno, opts)
    4.84 +                if back.has_key('vslot-%i' % i):
    4.85 +                    self.writeBackend(devid, 'vslot-%i' % devno,
    4.86 +                                      back['vslot-%i' % i])
    4.87  
    4.88 -                # Update vslots
    4.89 -                if back['vslots'] is not None:
    4.90 -                    vslots = old_vslots + back['vslots']
    4.91 -                    self.writeBackend(devid, 'vslots', vslots)
    4.92 +                # If a device is being attached then num_devs will grow
    4.93 +                if attaching:
    4.94 +                    self.writeBackend(devid, 'num_devs', str(devno + 1))
    4.95  
    4.96              elif state == 'Closing':
    4.97                  # PCI device detachment
    4.98 -                found = False
    4.99 -                for j in range(num_olddevs):
   4.100 -                    if dev == self.readBackend(devid, 'dev-%i' % j):
   4.101 -                        found = True
   4.102 -                        log.debug('Detaching device %s' % dev)
   4.103 -                        self.writeBackend(devid, 'state-%i' % j,
   4.104 -                                          str(xenbusState['Closing']))
   4.105 -                if not found:
   4.106 +                devno = self.reconfigureDevice_find(devid, num_olddevs, dev)
   4.107 +                if devno == None:
   4.108                      raise XendError('Device %s is not connected' % dev)
   4.109 -
   4.110 -                # Update vslots
   4.111 -                if back.get('vslots') is not None:
   4.112 -                    vslots = old_vslots
   4.113 -                    for vslot in back['vslots'].split(';'):
   4.114 -                        if vslot != '':
   4.115 -                            vslots = vslots.replace(vslot + ';', '', 1)
   4.116 -                    if vslots == '':
   4.117 -                        self.removeBackend(devid, 'vslots')
   4.118 -                    else:
   4.119 -                        self.writeBackend(devid, 'vslots', vslots)
   4.120 +                log.debug('Detaching device %s' % dev)
   4.121 +                self.writeBackend(devid, 'state-%i' % devno,
   4.122 +                                  str(xenbusState['Closing']))
   4.123  
   4.124              else:
   4.125                  raise XendError('Error configuring device %s: invalid state %s'
   4.126 @@ -191,12 +175,6 @@ class PciController(DevController):
   4.127          result = DevController.getDeviceConfiguration(self, devid, transaction)
   4.128          num_devs = self.readBackend(devid, 'num_devs')
   4.129          pci_devs = []
   4.130 -        
   4.131 -        vslots = self.readBackend(devid, 'vslots')
   4.132 -        if vslots is not None:
   4.133 -            if vslots[-1] == ";":
   4.134 -                vslots = vslots[:-1]
   4.135 -            slot_list = vslots.split(';')
   4.136  
   4.137          for i in range(int(num_devs)):
   4.138              dev_config = self.readBackend(devid, 'dev-%d' % i)
   4.139 @@ -215,13 +193,11 @@ class PciController(DevController):
   4.140  
   4.141                  # Per device uuid info
   4.142                  dev_dict['uuid'] = self.readBackend(devid, 'uuid-%d' % i)
   4.143 -
   4.144 -                #append vslot info
   4.145 -                if vslots is not None:
   4.146 -                    try:
   4.147 -                        dev_dict['vslot'] = slot_list[i]
   4.148 -                    except IndexError:
   4.149 -                        dev_dict['vslot'] = AUTO_PHP_SLOT_STR
   4.150 +                vslot = self.readBackend(devid, 'vslot-%d' % i)
   4.151 +                if vslot != None:
   4.152 +                    dev_dict['vslot'] = self.readBackend(devid, 'vslot-%d' % i)
   4.153 +                else:
   4.154 +                    dev_dict['vslot'] = AUTO_PHP_SLOT_STR
   4.155  
   4.156                  #append opts info
   4.157                  opts = self.readBackend(devid, 'opts-%d' % i)