debuggers.hg

changeset 22823:1e7594758b28

VT-d/ATS: misc fixes

First of all there were three places potentially de-referencing NULL
(two after an allocation failure, and one after a failed lookup).

Second, if ATS_ENABLE was already set, the device would not have got
added to the ats_devices list, potentially resulting in
dev_invalidate_iotlb() doing an incomplete job.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Jan Beulich <jbeulich@novell.com>
date Tue Jan 18 12:28:10 2011 +0000 (2011-01-18)
parents 78e2e5a50daa
children d2a1e1e12667
files xen/drivers/passthrough/vtd/x86/ats.c
line diff
     1.1 --- a/xen/drivers/passthrough/vtd/x86/ats.c	Tue Jan 18 10:28:22 2011 +0000
     1.2 +++ b/xen/drivers/passthrough/vtd/x86/ats.c	Tue Jan 18 12:28:10 2011 +0000
     1.3 @@ -93,6 +93,9 @@ int ats_device(int seg, int bus, int dev
     1.4          return 0;
     1.5  
     1.6      pdev = pci_get_pdev(bus, devfn);
     1.7 +    if ( !pdev )
     1.8 +        return 0;
     1.9 +
    1.10      drhd = acpi_find_matched_drhd_unit(pdev);
    1.11      if ( !drhd )
    1.12          return 0;
    1.13 @@ -110,6 +113,8 @@ int ats_device(int seg, int bus, int dev
    1.14      if ( pos && (ats_drhd == NULL) )
    1.15      {
    1.16          new_drhd = xmalloc(struct acpi_drhd_unit);
    1.17 +        if ( !new_drhd )
    1.18 +            return 0;
    1.19          memcpy(new_drhd, drhd, sizeof(struct acpi_drhd_unit));
    1.20          list_add_tail(&new_drhd->list, &ats_dev_drhd_units);
    1.21      }
    1.22 @@ -118,9 +123,8 @@ int ats_device(int seg, int bus, int dev
    1.23  
    1.24  int enable_ats_device(int seg, int bus, int devfn)
    1.25  {
    1.26 -    struct pci_ats_dev *pdev;
    1.27 +    struct pci_ats_dev *pdev = NULL;
    1.28      u32 value;
    1.29 -    u16 queue_depth;
    1.30      int pos;
    1.31  
    1.32      if ( !acpi_find_matched_atsr_unit(bus, devfn) )
    1.33 @@ -144,26 +148,43 @@ int enable_ats_device(int seg, int bus, 
    1.34  
    1.35      /* BUGBUG: add back seg when multi-seg platform support is enabled */
    1.36      value = pci_conf_read16(bus, PCI_SLOT(devfn),
    1.37 -                            PCI_FUNC(devfn), pos + ATS_REG_CAP);
    1.38 -    queue_depth = value & ATS_QUEUE_DEPTH_MASK;
    1.39 -
    1.40 -    value = pci_conf_read16(bus, PCI_SLOT(devfn),
    1.41                              PCI_FUNC(devfn), pos + ATS_REG_CTL);
    1.42      if ( value & ATS_ENABLE )
    1.43 -        return 0;
    1.44 -
    1.45 -    value |= ATS_ENABLE;
    1.46 -    pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
    1.47 -                     pos + ATS_REG_CTL, value);
    1.48 +    {
    1.49 +        list_for_each_entry ( pdev, &ats_devices, list )
    1.50 +        {
    1.51 +            if ( pdev->bus == bus && pdev->devfn == devfn )
    1.52 +            {
    1.53 +                pos = 0;
    1.54 +                break;
    1.55 +            }
    1.56 +        }
    1.57 +    }
    1.58 +    if ( pos )
    1.59 +        pdev = xmalloc(struct pci_ats_dev);
    1.60 +    if ( !pdev )
    1.61 +        return -ENOMEM;
    1.62  
    1.63 -    pdev = xmalloc(struct pci_ats_dev);
    1.64 -    pdev->bus = bus;
    1.65 -    pdev->devfn = devfn;
    1.66 -    pdev->ats_queue_depth = queue_depth;
    1.67 -    list_add(&(pdev->list), &ats_devices);
    1.68 +    if ( !(value & ATS_ENABLE) )
    1.69 +    {
    1.70 +        value |= ATS_ENABLE;
    1.71 +        pci_conf_write16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
    1.72 +                         pos + ATS_REG_CTL, value);
    1.73 +    }
    1.74 +
    1.75 +    if ( pos )
    1.76 +    {
    1.77 +        pdev->bus = bus;
    1.78 +        pdev->devfn = devfn;
    1.79 +        value = pci_conf_read16(bus, PCI_SLOT(devfn),
    1.80 +                                PCI_FUNC(devfn), pos + ATS_REG_CAP);
    1.81 +        pdev->ats_queue_depth = value & ATS_QUEUE_DEPTH_MASK;
    1.82 +        list_add(&pdev->list, &ats_devices);
    1.83 +    }
    1.84 +
    1.85      if ( iommu_verbose )
    1.86 -        dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS is enabled\n",
    1.87 -                bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
    1.88 +        dprintk(XENLOG_INFO VTDPREFIX, "%x:%x.%x: ATS %s enabled\n",
    1.89 +                bus, PCI_SLOT(devfn), PCI_FUNC(devfn), pos ? "is" : "was");
    1.90  
    1.91      return pos;
    1.92  }