lkml.org 
[lkml]   [2014]   [Jan]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [Patch Part2 V1 03/14] iommu/vt-d: simplify function get_domain_for_dev()


On 2014/1/8 11:06, Kai Huang wrote:
>
>
>
> On Tue, Jan 7, 2014 at 5:00 PM, Jiang Liu <jiang.liu@linux.intel.com
> <mailto:jiang.liu@linux.intel.com>> wrote:
>
> Function get_domain_for_dev() is a little complex, simplify it
> by factoring out dmar_search_domain_by_dev_info() and
> dmar_insert_dev_info().
>
> Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com
> <mailto:jiang.liu@linux.intel.com>>
> ---
> drivers/iommu/intel-iommu.c | 161
> ++++++++++++++++++++-----------------------
> 1 file changed, 75 insertions(+), 86 deletions(-)
>
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 1704e97..da65884 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -1957,18 +1957,70 @@ find_domain(struct pci_dev *pdev)
> return NULL;
> }
>
> +static inline struct dmar_domain *
> +dmar_search_domain_by_dev_info(int segment, int bus, int devfn)
> +{
> + struct device_domain_info *info;
> +
> + list_for_each_entry(info, &device_domain_list, global)
> + if (info->segment == segment && info->bus == bus &&
> + info->devfn == devfn)
> + return info->domain;
> +
> + return NULL;
> +}
> +
> +static int dmar_insert_dev_info(int segment, int bus, int devfn,
> + struct pci_dev *dev, struct
> dmar_domain **domp)
> +{
> + struct dmar_domain *found, *domain = *domp;
> + struct device_domain_info *info;
> + unsigned long flags;
> +
> + info = alloc_devinfo_mem();
> + if (!info)
> + return -ENOMEM;
> +
> + info->segment = segment;
> + info->bus = bus;
> + info->devfn = devfn;
> + info->dev = dev;
> + info->domain = domain;
> + if (!dev)
> + domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES;
> +
> + spin_lock_irqsave(&device_domain_lock, flags);
> + if (dev)
> + found = find_domain(dev);
> + else
> + found = dmar_search_domain_by_dev_info(segment, bus,
> devfn);
> + if (found) {
> + spin_unlock_irqrestore(&device_domain_lock, flags);
> + free_devinfo_mem(info);
> + if (found != domain) {
> + domain_exit(domain);
> + *domp = found;
> + }
> + } else {
> + list_add(&info->link, &domain->devices);
> + list_add(&info->global, &device_domain_list);
> + if (dev)
> + dev->dev.archdata.iommu = info;
> + spin_unlock_irqrestore(&device_domain_lock, flags);
> + }
> +
> + return 0;
> +}
>
>
> I am a little bit confused about the "alloc before check" sequence in
> above function. I believe the purpose of allocating the
> device_domain_info before searching the domain with spin_lock hold is to
> avoid the memory allocation in the spin_lock? In this case, why can't we
> use mutex instead of spin_lock? In my understanding, if we use mutex, we
> can first check if the domain exists or not before we really allocate
> device_domain_info, right?
Hi Kai,
Thanks for review!
The domain->devices list may be access in interrupt context,
so we need to protect it with spin_lock_irqsave(). Otherwise it may
case deadlock.

>
> Another question is when is info->iommu field initialized? Looks it is
> not initialized here?
It's set in function iommu_support_dev_iotlb() for devices supporting
device IOTLB.

>
> -Kai
>
> +
> /* domain is initialized */
> static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev,
> int gaw)
> {
> - struct dmar_domain *domain, *found = NULL;
> + struct dmar_domain *domain;
> struct intel_iommu *iommu;
> struct dmar_drhd_unit *drhd;
> - struct device_domain_info *info, *tmp;
> - struct pci_dev *dev_tmp;
> + struct pci_dev *bridge;
> unsigned long flags;
> - int bus = 0, devfn = 0;
> - int segment;
> - int ret;
> + int segment, bus, devfn;
>
> domain = find_domain(pdev);
> if (domain)
> @@ -1976,119 +2028,56 @@ static struct dmar_domain
> *get_domain_for_dev(struct pci_dev *pdev, int gaw)
>
> segment = pci_domain_nr(pdev->bus);
>
> - dev_tmp = pci_find_upstream_pcie_bridge(pdev);
> - if (dev_tmp) {
> - if (pci_is_pcie(dev_tmp)) {
> - bus = dev_tmp->subordinate->number;
> + bridge = pci_find_upstream_pcie_bridge(pdev);
> + if (bridge) {
> + if (pci_is_pcie(bridge)) {
> + bus = bridge->subordinate->number;
> devfn = 0;
> } else {
> - bus = dev_tmp->bus->number;
> - devfn = dev_tmp->devfn;
> + bus = bridge->bus->number;
> + devfn = bridge->devfn;
> }
> spin_lock_irqsave(&device_domain_lock, flags);
> - list_for_each_entry(info, &device_domain_list, global) {
> - if (info->segment == segment &&
> - info->bus == bus && info->devfn == devfn) {
> - found = info->domain;
> - break;
> - }
> - }
> + domain = dmar_search_domain_by_dev_info(segment,
> bus, devfn);
> spin_unlock_irqrestore(&device_domain_lock, flags);
> /* pcie-pci bridge already has a domain, uses it */
> - if (found) {
> - domain = found;
> + if (domain)
> goto found_domain;
> - }
> }
>
> - domain = alloc_domain();
> - if (!domain)
> - goto error;
> -
> - /* Allocate new domain for the device */
> drhd = dmar_find_matched_drhd_unit(pdev);
> if (!drhd) {
> printk(KERN_ERR "IOMMU: can't find DMAR for device
> %s\n",
> pci_name(pdev));
> - free_domain_mem(domain);
> return NULL;
> }
> iommu = drhd->iommu;
>
> - ret = iommu_attach_domain(domain, iommu);
> - if (ret) {
> + /* Allocate new domain for the device */
> + domain = alloc_domain();
> + if (!domain)
> + goto error;
> + if (iommu_attach_domain(domain, iommu)) {
> free_domain_mem(domain);
> goto error;
> }
> -
> if (domain_init(domain, gaw)) {
> domain_exit(domain);
> goto error;
> }
>
> /* register pcie-to-pci device */
> - if (dev_tmp) {
> - info = alloc_devinfo_mem();
> - if (!info) {
> + if (bridge) {
> + if (dmar_insert_dev_info(segment, bus, devfn, NULL,
> &domain)) {
> domain_exit(domain);
> goto error;
> }
> - info->segment = segment;
> - info->bus = bus;
> - info->devfn = devfn;
> - info->dev = NULL;
> - info->domain = domain;
> - /* This domain is shared by devices under p2p bridge */
> - domain->flags |= DOMAIN_FLAG_P2P_MULTIPLE_DEVICES;
> -
> - /* pcie-to-pci bridge already has a domain, uses it */
> - found = NULL;
> - spin_lock_irqsave(&device_domain_lock, flags);
> - list_for_each_entry(tmp, &device_domain_list, global) {
> - if (tmp->segment == segment &&
> - tmp->bus == bus && tmp->devfn == devfn) {
> - found = tmp->domain;
> - break;
> - }
> - }
> - if (found) {
> - spin_unlock_irqrestore(&device_domain_lock,
> flags);
> - free_devinfo_mem(info);
> - domain_exit(domain);
> - domain = found;
> - } else {
> - list_add(&info->link, &domain->devices);
> - list_add(&info->global, &device_domain_list);
> - spin_unlock_irqrestore(&device_domain_lock,
> flags);
> - }
> }
>
> found_domain:
> - info = alloc_devinfo_mem();
> - if (!info)
> - goto error;
> - info->segment = segment;
> - info->bus = pdev->bus->number;
> - info->devfn = pdev->devfn;
> - info->dev = pdev;
> - info->domain = domain;
> - spin_lock_irqsave(&device_domain_lock, flags);
> - /* somebody is fast */
> - found = find_domain(pdev);
> - if (found != NULL) {
> - spin_unlock_irqrestore(&device_domain_lock, flags);
> - if (found != domain) {
> - domain_exit(domain);
> - domain = found;
> - }
> - free_devinfo_mem(info);
> + if (dmar_insert_dev_info(segment, pdev->bus->number,
> pdev->devfn,
> + pdev, &domain) == 0)
> return domain;
> - }
> - list_add(&info->link, &domain->devices);
> - list_add(&info->global, &device_domain_list);
> - pdev->dev.archdata.iommu = info;
> - spin_unlock_irqrestore(&device_domain_lock, flags);
> - return domain;
> error:
> /* recheck it here, maybe others set it */
> return find_domain(pdev);
> --
> 1.7.10.4
>
> _______________________________________________
> iommu mailing list
> iommu@lists.linux-foundation.org
> <mailto:iommu@lists.linux-foundation.org>
> https://lists.linuxfoundation.org/mailman/listinfo/iommu
>
>


\
 
 \ /
  Last update: 2014-01-08 07:21    [W:0.281 / U:0.016 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site