ia64/xen-unstable

changeset 18982:f76f1294d82c

vtd hotplug: check if a device can be hot-plugged.

When we statically assign a pci device (the pci=3D['xx:xx.x'] string
in guest config file) to guest, we make many checkings (for instance,
if the device is specified in 'pciback.hide', if it has
non-page-aligned MMIO BARs, if it has a proper FLR capability, if the
related devices should be co-assigned). However, with respect to the
guest hotplug, we only check if the device exists and not assigned yet
-- this is not enough, for instance, now xend allows us to assign an
in-use device (being used by Dom0) to an HVM guest (because
xc.test_assigned() returns OK) -- this will cause disaster... The
patch adds some necessary checkings.

Signed-off-by: Dexuan Cui <dexuan.cui@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Jan 05 11:14:18 2009 +0000 (2009-01-05)
parents b3a9bc726241
children 42108955f52e
files tools/python/xen/util/pci.py tools/python/xen/xend/XendDomainInfo.py tools/python/xen/xend/server/pciif.py
line diff
     1.1 --- a/tools/python/xen/util/pci.py	Mon Jan 05 11:13:22 2009 +0000
     1.2 +++ b/tools/python/xen/util/pci.py	Mon Jan 05 11:14:18 2009 +0000
     1.3 @@ -276,7 +276,7 @@ def check_FLR_capability(dev_list):
     1.4                      coassigned_pci_list = dev.find_all_the_multi_functions()
     1.5                      need_transform = True
     1.6                  elif dev.dev_type == DEV_TYPE_PCI and not dev.pci_af_flr:
     1.7 -                    coassigned_pci_list = dev.find_coassigned_devices(True)
     1.8 +                    coassigned_pci_list = dev.find_coassigned_pci_devices(True)
     1.9                      del coassigned_pci_list[0]
    1.10                      need_transform = True
    1.11  
    1.12 @@ -434,7 +434,7 @@ class PciDevice:
    1.13                  list = list + [dev.name]
    1.14          return list
    1.15          
    1.16 -    def find_coassigned_devices(self, ignore_bridge = True):
    1.17 +    def find_coassigned_pci_devices(self, ignore_bridge = True):
    1.18          ''' Here'self' is a PCI device, we need find the uppermost PCI/PCI-X
    1.19              bridge, and all devices behind it must be co-assigned to the same
    1.20              guest.
    1.21 @@ -532,6 +532,16 @@ class PciDevice:
    1.22          funcs = re.findall(p, pci_names)
    1.23          return funcs
    1.24  
    1.25 +    def find_coassigned_devices(self):
    1.26 +        if self.dev_type == DEV_TYPE_PCIe_ENDPOINT and not self.pcie_flr:
    1.27 +            return self.find_all_the_multi_functions()
    1.28 +        elif self.dev_type == DEV_TYPE_PCI and not self.pci_af_flr:
    1.29 +            coassigned_pci_list = self.find_coassigned_pci_devices(True)
    1.30 +            del coassigned_pci_list[0]
    1.31 +            return coassigned_pci_list
    1.32 +        else:
    1.33 +            return [self.name]
    1.34 +
    1.35      def find_cap_offset(self, cap):
    1.36          path = find_sysfs_mnt()+SYSFS_PCI_DEVS_PATH+'/'+ \
    1.37                 self.name+SYSFS_PCI_DEV_CONFIG_PATH
    1.38 @@ -718,7 +728,7 @@ class PciDevice:
    1.39                  if self.bus == 0:
    1.40                      self.do_FLR_for_integrated_device()
    1.41                  else:
    1.42 -                    devs = self.find_coassigned_devices(False)
    1.43 +                    devs = self.find_coassigned_pci_devices(False)
    1.44                      # Remove the element 0 which is a bridge
    1.45                      target_bus = devs[0]
    1.46                      del devs[0]
     2.1 --- a/tools/python/xen/xend/XendDomainInfo.py	Mon Jan 05 11:13:22 2009 +0000
     2.2 +++ b/tools/python/xen/xend/XendDomainInfo.py	Mon Jan 05 11:14:18 2009 +0000
     2.3 @@ -290,19 +290,21 @@ def dom_get(dom):
     2.4          log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
     2.5      return None
     2.6  
     2.7 -def do_FLR(domid):
     2.8 -    from xen.xend.server.pciif import parse_pci_name, PciDevice
     2.9 +def get_assigned_pci_devices(domid):
    2.10 +    dev_str_list = []
    2.11      path = '/local/domain/0/backend/pci/%u/0/' % domid
    2.12      num_devs = xstransact.Read(path + 'num_devs');
    2.13      if num_devs is None or num_devs == "":
    2.14 -        return;
    2.15 -
    2.16 -    num_devs = int(xstransact.Read(path + 'num_devs'));
    2.17 -
    2.18 -    dev_str_list = []
    2.19 +        return dev_str_list
    2.20 +    num_devs = int(num_devs);
    2.21      for i in range(num_devs):
    2.22          dev_str = xstransact.Read(path + 'dev-%i' % i)
    2.23          dev_str_list = dev_str_list + [dev_str]
    2.24 +    return dev_str_list 
    2.25 +
    2.26 +def do_FLR(domid):
    2.27 +    from xen.xend.server.pciif import parse_pci_name, PciDevice
    2.28 +    dev_str_list = get_assigned_pci_devices(domid)
    2.29  
    2.30      for dev_str in dev_str_list:
    2.31          (dom, b, d, f) = parse_pci_name(dev_str)
    2.32 @@ -645,6 +647,55 @@ class XendDomainInfo:
    2.33                            " already been assigned to other domain, or maybe"
    2.34                            " it doesn't exist." % (bus, dev, func))
    2.35  
    2.36 +        # Here, we duplicate some checkings (in some cases, we mustn't allow
    2.37 +        # a device to be hot-plugged into an HVM guest) that are also done in
    2.38 +        # pci_device_configure()'s self.device_create(dev_sxp) or
    2.39 +        # dev_control.reconfigureDevice(devid, dev_config).
    2.40 +        # We must make the checkings before sending the command 'pci-ins' to
    2.41 +        # ioemu.
    2.42 +
    2.43 +        # Test whether the device is owned by pciback. For instance, we can't
    2.44 +        # hotplug a device being used by Dom0 itself to an HVM guest.
    2.45 +        from xen.xend.server.pciif import PciDevice, parse_pci_name
    2.46 +        domain = int(new_dev['domain'],16)
    2.47 +        bus    = int(new_dev['bus'],16)
    2.48 +        dev    = int(new_dev['slot'],16)
    2.49 +        func   = int(new_dev['func'],16)
    2.50 +        try:
    2.51 +            pci_device = PciDevice(domain, bus, dev, func)
    2.52 +        except Exception, e:
    2.53 +            raise VmError("pci: failed to locate device and "+
    2.54 +                    "parse it's resources - "+str(e))
    2.55 +        if pci_device.driver!='pciback':
    2.56 +            raise VmError(("pci: PCI Backend does not own device "+ \
    2.57 +                    "%s\n"+ \
    2.58 +                    "See the pciback.hide kernel "+ \
    2.59 +                    "command-line parameter or\n"+ \
    2.60 +                    "bind your slot/device to the PCI backend using sysfs" \
    2.61 +                    )%(pci_device.name))
    2.62 +
    2.63 +        # Check non-page-aligned MMIO BAR.
    2.64 +        if pci_device.has_non_page_aligned_bar and arch.type != "ia64":
    2.65 +            raise VmError("pci: %s: non-page-aligned MMIO BAR found." % \
    2.66 +                pci_device.name)
    2.67 +
    2.68 +        # Check the co-assignment.
    2.69 +        # To pci-attach a device D to domN, we should ensure each of D's
    2.70 +        # co-assignment devices hasn't been assigned, or has been assigned to
    2.71 +        # domN.
    2.72 +        coassignment_list = pci_device.find_coassigned_devices()
    2.73 +        assigned_pci_device_str_list = get_assigned_pci_devices(self.domid)
    2.74 +        for pci_str in coassignment_list:
    2.75 +            (domain, bus, dev, func) = parse_pci_name(pci_str) 
    2.76 +            dev_str =  '0x%x,0x%x,0x%x,0x%x' % (domain, bus, dev, func)
    2.77 +            if xc.test_assign_device(self.domid, dev_str) == 0:
    2.78 +                continue
    2.79 +            if not pci_str in assigned_pci_device_str_list:
    2.80 +                raise VmError(('pci: failed to pci-attach %s to dom%d" + \
    2.81 +                    " because one of its co-assignment device %s has been" + \
    2.82 +                    " assigned to other domain.' \
    2.83 +                    )% (pci_device.name, self.domid, pci_str))
    2.84 +
    2.85          bdf_str = "%s:%s:%s.%s@%s" % (new_dev['domain'],
    2.86                  new_dev['bus'],
    2.87                  new_dev['slot'],
    2.88 @@ -935,6 +986,31 @@ class XendDomainInfo:
    2.89          if vslot == 0:
    2.90              raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot))
    2.91  
    2.92 +        # Check the co-assignment.
    2.93 +        # To pci-detach a device D from domN, we should ensure: for each DD in the
    2.94 +        # list of D's co-assignment devices, DD is not assigned (to domN).
    2.95 +        # 
    2.96 +        from xen.xend.server.pciif import PciDevice
    2.97 +        domain = int(x['domain'],16)
    2.98 +        bus    = int(x['bus'],16)
    2.99 +        dev    = int(x['slot'],16)
   2.100 +        func   = int(x['func'],16)
   2.101 +        try:
   2.102 +            pci_device = PciDevice(domain, bus, dev, func)
   2.103 +        except Exception, e:
   2.104 +            raise VmError("pci: failed to locate device and "+
   2.105 +                    "parse it's resources - "+str(e))
   2.106 +        coassignment_list = pci_device.find_coassigned_devices()
   2.107 +        coassignment_list.remove(pci_device.name)
   2.108 +        assigned_pci_device_str_list = get_assigned_pci_devices(self.domid)
   2.109 +        for pci_str in coassignment_list:
   2.110 +            if pci_str in assigned_pci_device_str_list:
   2.111 +                raise VmError(('pci: failed to pci-detach %s from dom%d" + \
   2.112 +                    " because one of its co-assignment device %s is still " + \
   2.113 +                    " assigned to the domain.' \
   2.114 +                    )% (pci_device.name, self.domid, pci_str))
   2.115 +
   2.116 +
   2.117          bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func'])
   2.118          log.info("hvm_destroyPCIDevice:%s:%s!", x, bdf_str)
   2.119  
     3.1 --- a/tools/python/xen/xend/server/pciif.py	Mon Jan 05 11:13:22 2009 +0000
     3.2 +++ b/tools/python/xen/xend/server/pciif.py	Mon Jan 05 11:14:18 2009 +0000
     3.3 @@ -417,7 +417,7 @@ class PciController(DevController):
     3.4                  else:
     3.5                      # All devices behind the uppermost PCI/PCI-X bridge must be\
     3.6                      # co-assigned to the same guest.
     3.7 -                    devs_str = dev.find_coassigned_devices(True)
     3.8 +                    devs_str = dev.find_coassigned_pci_devices(True)
     3.9                      # Remove the element 0 which is a bridge
    3.10                      del devs_str[0]
    3.11