Merge branch 'staging-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[pandora-kernel.git] / arch / x86 / kernel / amd_iommu.c
index d6192bc..cd8cbeb 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <linux/pci.h>
+#include <linux/pci-ats.h>
 #include <linux/bitmap.h>
 #include <linux/slab.h>
 #include <linux/debugfs.h>
@@ -365,7 +366,7 @@ static void iommu_poll_events(struct amd_iommu *iommu)
        spin_unlock_irqrestore(&iommu->lock, flags);
 }
 
-irqreturn_t amd_iommu_int_handler(int irq, void *data)
+irqreturn_t amd_iommu_int_thread(int irq, void *data)
 {
        struct amd_iommu *iommu;
 
@@ -375,6 +376,11 @@ irqreturn_t amd_iommu_int_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+irqreturn_t amd_iommu_int_handler(int irq, void *data)
+{
+       return IRQ_WAKE_THREAD;
+}
+
 /****************************************************************************
  *
  * IOMMU command queuing functions
@@ -463,6 +469,37 @@ static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address,
                cmd->data[2] |= CMD_INV_IOMMU_PAGES_PDE_MASK;
 }
 
+static void build_inv_iotlb_pages(struct iommu_cmd *cmd, u16 devid, int qdep,
+                                 u64 address, size_t size)
+{
+       u64 pages;
+       int s;
+
+       pages = iommu_num_pages(address, size, PAGE_SIZE);
+       s     = 0;
+
+       if (pages > 1) {
+               /*
+                * If we have to flush more than one page, flush all
+                * TLB entries for this domain
+                */
+               address = CMD_INV_IOMMU_ALL_PAGES_ADDRESS;
+               s = 1;
+       }
+
+       address &= PAGE_MASK;
+
+       memset(cmd, 0, sizeof(*cmd));
+       cmd->data[0]  = devid;
+       cmd->data[0] |= (qdep & 0xff) << 24;
+       cmd->data[1]  = devid;
+       cmd->data[2]  = lower_32_bits(address);
+       cmd->data[3]  = upper_32_bits(address);
+       CMD_SET_TYPE(cmd, CMD_INV_IOTLB_PAGES);
+       if (s)
+               cmd->data[2] |= CMD_INV_IOMMU_PAGES_SIZE_MASK;
+}
+
 static void build_inv_all(struct iommu_cmd *cmd)
 {
        memset(cmd, 0, sizeof(*cmd));
@@ -593,18 +630,48 @@ void iommu_flush_all_caches(struct amd_iommu *iommu)
        }
 }
 
+/*
+ * Command send function for flushing on-device TLB
+ */
+static int device_flush_iotlb(struct device *dev, u64 address, size_t size)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct amd_iommu *iommu;
+       struct iommu_cmd cmd;
+       u16 devid;
+       int qdep;
+
+       qdep  = pci_ats_queue_depth(pdev);
+       devid = get_device_id(dev);
+       iommu = amd_iommu_rlookup_table[devid];
+
+       build_inv_iotlb_pages(&cmd, devid, qdep, address, size);
+
+       return iommu_queue_command(iommu, &cmd);
+}
+
 /*
  * Command send function for invalidating a device table entry
  */
 static int device_flush_dte(struct device *dev)
 {
        struct amd_iommu *iommu;
+       struct pci_dev *pdev;
        u16 devid;
+       int ret;
 
+       pdev  = to_pci_dev(dev);
        devid = get_device_id(dev);
        iommu = amd_iommu_rlookup_table[devid];
 
-       return iommu_flush_dte(iommu, devid);
+       ret = iommu_flush_dte(iommu, devid);
+       if (ret)
+               return ret;
+
+       if (pci_ats_enabled(pdev))
+               ret = device_flush_iotlb(dev, 0, ~0UL);
+
+       return ret;
 }
 
 /*
@@ -615,6 +682,7 @@ static int device_flush_dte(struct device *dev)
 static void __domain_flush_pages(struct protection_domain *domain,
                                 u64 address, size_t size, int pde)
 {
+       struct iommu_dev_data *dev_data;
        struct iommu_cmd cmd;
        int ret = 0, i;
 
@@ -631,6 +699,15 @@ static void __domain_flush_pages(struct protection_domain *domain,
                ret |= iommu_queue_command(amd_iommus[i], &cmd);
        }
 
+       list_for_each_entry(dev_data, &domain->dev_list, list) {
+               struct pci_dev *pdev = to_pci_dev(dev_data->dev);
+
+               if (!pci_ats_enabled(pdev))
+                       continue;
+
+               ret |= device_flush_iotlb(dev_data->dev, address, size);
+       }
+
        WARN_ON(ret);
 }
 
@@ -1400,17 +1477,22 @@ static bool dma_ops_domain(struct protection_domain *domain)
        return domain->flags & PD_DMA_OPS_MASK;
 }
 
-static void set_dte_entry(u16 devid, struct protection_domain *domain)
+static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats)
 {
        u64 pte_root = virt_to_phys(domain->pt_root);
+       u32 flags = 0;
 
        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;
 
-       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);
+       if (ats)
+               flags |= DTE_FLAG_IOTLB;
+
+       amd_iommu_dev_table[devid].data[3] |= flags;
+       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);
 }
 
 static void clear_dte_entry(u16 devid)
@@ -1427,16 +1509,22 @@ static void do_attach(struct device *dev, struct protection_domain *domain)
 {
        struct iommu_dev_data *dev_data;
        struct amd_iommu *iommu;
+       struct pci_dev *pdev;
+       bool ats = false;
        u16 devid;
 
        devid    = get_device_id(dev);
        iommu    = amd_iommu_rlookup_table[devid];
        dev_data = get_dev_data(dev);
+       pdev     = to_pci_dev(dev);
+
+       if (amd_iommu_iotlb_sup)
+               ats = pci_ats_enabled(pdev);
 
        /* Update data structures */
        dev_data->domain = domain;
        list_add(&dev_data->list, &domain->dev_list);
-       set_dte_entry(devid, domain);
+       set_dte_entry(devid, domain, ats);
 
        /* Do reference counting */
        domain->dev_iommu[iommu->index] += 1;
@@ -1529,9 +1617,13 @@ out_unlock:
 static int attach_device(struct device *dev,
                         struct protection_domain *domain)
 {
+       struct pci_dev *pdev = to_pci_dev(dev);
        unsigned long flags;
        int ret;
 
+       if (amd_iommu_iotlb_sup)
+               pci_enable_ats(pdev, PAGE_SHIFT);
+
        write_lock_irqsave(&amd_iommu_devtable_lock, flags);
        ret = __attach_device(dev, domain);
        write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
@@ -1588,12 +1680,16 @@ static void __detach_device(struct device *dev)
  */
 static void detach_device(struct device *dev)
 {
+       struct pci_dev *pdev = to_pci_dev(dev);
        unsigned long flags;
 
        /* lock device table */
        write_lock_irqsave(&amd_iommu_devtable_lock, flags);
        __detach_device(dev);
        write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
+
+       if (amd_iommu_iotlb_sup && pci_ats_enabled(pdev))
+               pci_disable_ats(pdev);
 }
 
 /*
@@ -1605,10 +1701,9 @@ static struct protection_domain *domain_for_device(struct device *dev)
        struct protection_domain *dom;
        struct iommu_dev_data *dev_data, *alias_data;
        unsigned long flags;
-       u16 devid, alias;
+       u16 devid;
 
        devid      = get_device_id(dev);
-       alias      = amd_iommu_alias_table[devid];
        dev_data   = get_dev_data(dev);
        alias_data = get_dev_data(dev_data->alias);
        if (!alias_data)
@@ -1743,8 +1838,9 @@ static void update_device_table(struct protection_domain *domain)
        struct iommu_dev_data *dev_data;
 
        list_for_each_entry(dev_data, &domain->dev_list, list) {
+               struct pci_dev *pdev = to_pci_dev(dev_data->dev);
                u16 devid = get_device_id(dev_data->dev);
-               set_dte_entry(devid, domain);
+               set_dte_entry(devid, domain, pci_ats_enabled(pdev));
        }
 }