iommu/amd: Attach and detach complete alias group
[cascardo/linux.git] / drivers / iommu / amd_iommu.c
index ecb0109..a5e6b0a 100644 (file)
@@ -87,6 +87,28 @@ int amd_iommu_max_glx_val = -1;
 
 static struct dma_map_ops amd_iommu_dma_ops;
 
+/*
+ * This struct contains device specific data for the IOMMU
+ */
+struct iommu_dev_data {
+       struct list_head list;            /* For domain->dev_list */
+       struct list_head dev_data_list;   /* For global dev_data_list */
+       struct list_head alias_list;      /* Link alias-groups together */
+       struct iommu_dev_data *alias_data;/* The alias dev_data */
+       struct protection_domain *domain; /* Domain the device is bound to */
+       atomic_t bind;                    /* Domain attach reference count */
+       u16 devid;                        /* PCI Device ID */
+       bool iommu_v2;                    /* Device can make use of IOMMUv2 */
+       bool passthrough;                 /* Default for device is pt_domain */
+       struct {
+               bool enabled;
+               int qdep;
+       } ats;                            /* ATS state */
+       bool pri_tlp;                     /* PASID TLB required for
+                                            PPR completions */
+       u32 errata;                       /* Bitmap for errata to apply */
+};
+
 /*
  * general struct to manage commands send to an IOMMU
  */
@@ -114,6 +136,8 @@ static struct iommu_dev_data *alloc_dev_data(u16 devid)
        if (!dev_data)
                return NULL;
 
+       INIT_LIST_HEAD(&dev_data->alias_list);
+
        dev_data->devid = devid;
        atomic_set(&dev_data->bind, 0);
 
@@ -362,6 +386,9 @@ static int iommu_init_device(struct device *dev)
                        return -ENOTSUPP;
                }
                dev_data->alias_data = alias_data;
+
+               /* Add device to the alias_list */
+               list_add(&dev_data->alias_list, &alias_data->alias_list);
        }
 
        ret = init_iommu_group(dev);
@@ -2122,35 +2149,29 @@ static void do_detach(struct iommu_dev_data *dev_data)
 static int __attach_device(struct iommu_dev_data *dev_data,
                           struct protection_domain *domain)
 {
+       struct iommu_dev_data *head, *entry;
        int ret;
 
        /* lock domain */
        spin_lock(&domain->lock);
 
-       if (dev_data->alias_data != NULL) {
-               struct iommu_dev_data *alias_data = dev_data->alias_data;
+       head = dev_data;
 
-               /* Some sanity checks */
-               ret = -EBUSY;
-               if (alias_data->domain != NULL &&
-                               alias_data->domain != domain)
-                       goto out_unlock;
-
-               if (dev_data->domain != NULL &&
-                               dev_data->domain != domain)
-                       goto out_unlock;
+       if (head->alias_data != NULL)
+               head = head->alias_data;
 
-               /* Do real assignment */
-               if (alias_data->domain == NULL)
-                       do_attach(alias_data, domain);
+       /* Now we have the root of the alias group, if any */
 
-               atomic_inc(&alias_data->bind);
-       }
+       ret = -EBUSY;
+       if (head->domain != NULL)
+               goto out_unlock;
 
-       if (dev_data->domain == NULL)
-               do_attach(dev_data, domain);
+       /* Attach alias group root */
+       do_attach(head, domain);
 
-       atomic_inc(&dev_data->bind);
+       /* Attach other devices in the alias group */
+       list_for_each_entry(entry, &head->alias_list, alias_list)
+               do_attach(entry, domain);
 
        ret = 0;
 
@@ -2298,6 +2319,7 @@ static int attach_device(struct device *dev,
  */
 static void __detach_device(struct iommu_dev_data *dev_data)
 {
+       struct iommu_dev_data *head, *entry;
        struct protection_domain *domain;
        unsigned long flags;
 
@@ -2307,15 +2329,14 @@ static void __detach_device(struct iommu_dev_data *dev_data)
 
        spin_lock_irqsave(&domain->lock, flags);
 
-       if (dev_data->alias_data != NULL) {
-               struct iommu_dev_data *alias_data = dev_data->alias_data;
+       head = dev_data;
+       if (head->alias_data != NULL)
+               head = head->alias_data;
 
-               if (atomic_dec_and_test(&alias_data->bind))
-                       do_detach(alias_data);
-       }
+       list_for_each_entry(entry, &head->alias_list, alias_list)
+               do_detach(entry);
 
-       if (atomic_dec_and_test(&dev_data->bind))
-               do_detach(dev_data);
+       do_detach(head);
 
        spin_unlock_irqrestore(&domain->lock, flags);