x86/amd-iommu: Don't detach device from pt domain on driver unbind
[pandora-kernel.git] / arch / x86 / kernel / amd_iommu.c
index 7987f20..12a541d 100644 (file)
@@ -1077,29 +1077,38 @@ static void __attach_device(struct amd_iommu *iommu,
                            struct protection_domain *domain,
                            u16 devid)
 {
-       unsigned long flags;
-       u64 pte_root = virt_to_phys(domain->pt_root);
+       u64 pte_root;
 
-       domain->dev_cnt += 1;
+       /* lock domain */
+       spin_lock(&domain->lock);
+
+       pte_root = virt_to_phys(domain->pt_root);
 
        pte_root |= (domain->mode & DEV_ENTRY_MODE_MASK)
                    << DEV_ENTRY_MODE_SHIFT;
        pte_root |= IOMMU_PTE_IR | IOMMU_PTE_IW | IOMMU_PTE_P | IOMMU_PTE_TV;
 
-       write_lock_irqsave(&amd_iommu_devtable_lock, flags);
-       amd_iommu_dev_table[devid].data[0] = lower_32_bits(pte_root);
-       amd_iommu_dev_table[devid].data[1] = upper_32_bits(pte_root);
        amd_iommu_dev_table[devid].data[2] = domain->id;
+       amd_iommu_dev_table[devid].data[1] = upper_32_bits(pte_root);
+       amd_iommu_dev_table[devid].data[0] = lower_32_bits(pte_root);
 
        amd_iommu_pd_table[devid] = domain;
-       write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+
+       domain->dev_cnt += 1;
+
+       /* ready */
+       spin_unlock(&domain->lock);
 }
 
 static void attach_device(struct amd_iommu *iommu,
                          struct protection_domain *domain,
                          u16 devid)
 {
+       unsigned long flags;
+
+       write_lock_irqsave(&amd_iommu_devtable_lock, flags);
        __attach_device(iommu, domain, devid);
+       write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
 
        /*
         * We might boot into a crash-kernel here. The crashed kernel
@@ -1132,6 +1141,15 @@ static void __detach_device(struct protection_domain *domain, u16 devid)
 
        /* ready */
        spin_unlock(&domain->lock);
+
+       /*
+        * If we run in passthrough mode the device must be assigned to the
+        * passthrough domain if it is detached from any other domain
+        */
+       if (iommu_pass_through) {
+               struct amd_iommu *iommu = amd_iommu_rlookup_table[devid];
+               __attach_device(iommu, pt_domain, devid);
+       }
 }
 
 /*
@@ -1177,6 +1195,8 @@ static int device_change_notifier(struct notifier_block *nb,
        case BUS_NOTIFY_UNBOUND_DRIVER:
                if (!domain)
                        goto out;
+               if (iommu_pass_through)
+                       break;
                detach_device(domain, devid);
                break;
        case BUS_NOTIFY_ADD_DEVICE: