These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / powerpc / platforms / pseries / iommu.c
index 61d5a17..bd98ce2 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/crash_dump.h>
 #include <linux/memory.h>
 #include <linux/of.h>
+#include <linux/iommu.h>
+#include <linux/rculist.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 
 #include "pseries.h"
 
+static struct iommu_table_group *iommu_pseries_alloc_group(int node)
+{
+       struct iommu_table_group *table_group = NULL;
+       struct iommu_table *tbl = NULL;
+       struct iommu_table_group_link *tgl = NULL;
+
+       table_group = kzalloc_node(sizeof(struct iommu_table_group), GFP_KERNEL,
+                          node);
+       if (!table_group)
+               goto fail_exit;
+
+       tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, node);
+       if (!tbl)
+               goto fail_exit;
+
+       tgl = kzalloc_node(sizeof(struct iommu_table_group_link), GFP_KERNEL,
+                       node);
+       if (!tgl)
+               goto fail_exit;
+
+       INIT_LIST_HEAD_RCU(&tbl->it_group_list);
+       tgl->table_group = table_group;
+       list_add_rcu(&tgl->next, &tbl->it_group_list);
+
+       table_group->tables[0] = tbl;
+
+       return table_group;
+
+fail_exit:
+       kfree(tgl);
+       kfree(table_group);
+       kfree(tbl);
+
+       return NULL;
+}
+
+static void iommu_pseries_free_group(struct iommu_table_group *table_group,
+               const char *node_name)
+{
+       struct iommu_table *tbl;
+#ifdef CONFIG_IOMMU_API
+       struct iommu_table_group_link *tgl;
+#endif
+
+       if (!table_group)
+               return;
+
+       tbl = table_group->tables[0];
+#ifdef CONFIG_IOMMU_API
+       tgl = list_first_entry_or_null(&tbl->it_group_list,
+                       struct iommu_table_group_link, next);
+
+       WARN_ON_ONCE(!tgl);
+       if (tgl) {
+               list_del_rcu(&tgl->next);
+               kfree(tgl);
+       }
+       if (table_group->group) {
+               iommu_group_put(table_group->group);
+               BUG_ON(table_group->group);
+       }
+#endif
+       iommu_free_table(tbl, node_name);
+
+       kfree(table_group);
+}
+
 static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
                                      __be64 *startp, __be64 *endp)
 {
@@ -193,7 +262,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
        int ret = 0;
        unsigned long flags;
 
-       if (npages == 1) {
+       if ((npages == 1) || !firmware_has_feature(FW_FEATURE_MULTITCE)) {
                return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
                                           direction, attrs);
        }
@@ -285,6 +354,9 @@ static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long n
 {
        u64 rc;
 
+       if (!firmware_has_feature(FW_FEATURE_MULTITCE))
+               return tce_free_pSeriesLP(tbl, tcenum, npages);
+
        rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages);
 
        if (rc && printk_ratelimit()) {
@@ -460,8 +532,6 @@ static int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn,
        return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg);
 }
 
-
-#ifdef CONFIG_PCI
 static void iommu_table_setparms(struct pci_controller *phb,
                                 struct device_node *dn,
                                 struct iommu_table *tbl)
@@ -546,6 +616,12 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb,
        tbl->it_size = size >> tbl->it_page_shift;
 }
 
+struct iommu_table_ops iommu_table_pseries_ops = {
+       .set = tce_build_pSeries,
+       .clear = tce_free_pSeries,
+       .get = tce_get_pseries
+};
+
 static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
 {
        struct device_node *dn;
@@ -610,12 +686,13 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
        pci->phb->dma_window_size = 0x8000000ul;
        pci->phb->dma_window_base_cur = 0x8000000ul;
 
-       tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                          pci->phb->node);
+       pci->table_group = iommu_pseries_alloc_group(pci->phb->node);
+       tbl = pci->table_group->tables[0];
 
        iommu_table_setparms(pci->phb, dn, tbl);
-       pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
-       iommu_register_group(tbl, pci_domain_nr(bus), 0);
+       tbl->it_ops = &iommu_table_pseries_ops;
+       iommu_init_table(tbl, pci->phb->node);
+       iommu_register_group(pci->table_group, pci_domain_nr(bus), 0);
 
        /* Divide the rest (1.75GB) among the children */
        pci->phb->dma_window_size = 0x80000000ul;
@@ -625,6 +702,11 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
        pr_debug("ISA/IDE, window size is 0x%llx\n", pci->phb->dma_window_size);
 }
 
+struct iommu_table_ops iommu_table_lpar_multi_ops = {
+       .set = tce_buildmulti_pSeriesLP,
+       .clear = tce_freemulti_pSeriesLP,
+       .get = tce_get_pSeriesLP
+};
 
 static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
 {
@@ -653,15 +735,17 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
        ppci = PCI_DN(pdn);
 
        pr_debug("  parent is %s, iommu_table: 0x%p\n",
-                pdn->full_name, ppci->iommu_table);
+                pdn->full_name, ppci->table_group);
 
-       if (!ppci->iommu_table) {
-               tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                                  ppci->phb->node);
+       if (!ppci->table_group) {
+               ppci->table_group = iommu_pseries_alloc_group(ppci->phb->node);
+               tbl = ppci->table_group->tables[0];
                iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window);
-               ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node);
-               iommu_register_group(tbl, pci_domain_nr(bus), 0);
-               pr_debug("  created table: %p\n", ppci->iommu_table);
+               tbl->it_ops = &iommu_table_lpar_multi_ops;
+               iommu_init_table(tbl, ppci->phb->node);
+               iommu_register_group(ppci->table_group,
+                               pci_domain_nr(bus), 0);
+               pr_debug("  created table: %p\n", ppci->table_group);
        }
 }
 
@@ -683,13 +767,15 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
                struct pci_controller *phb = PCI_DN(dn)->phb;
 
                pr_debug(" --> first child, no bridge. Allocating iommu table.\n");
-               tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                                  phb->node);
+               PCI_DN(dn)->table_group = iommu_pseries_alloc_group(phb->node);
+               tbl = PCI_DN(dn)->table_group->tables[0];
                iommu_table_setparms(phb, dn, tbl);
-               PCI_DN(dn)->iommu_table = iommu_init_table(tbl, phb->node);
-               iommu_register_group(tbl, pci_domain_nr(phb->bus), 0);
-               set_iommu_table_base_and_group(&dev->dev,
-                                              PCI_DN(dn)->iommu_table);
+               tbl->it_ops = &iommu_table_pseries_ops;
+               iommu_init_table(tbl, phb->node);
+               iommu_register_group(PCI_DN(dn)->table_group,
+                               pci_domain_nr(phb->bus), 0);
+               set_iommu_table_base(&dev->dev, tbl);
+               iommu_add_device(&dev->dev);
                return;
        }
 
@@ -697,13 +783,14 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
         * an already allocated iommu table is found and use that.
         */
 
-       while (dn && PCI_DN(dn) && PCI_DN(dn)->iommu_table == NULL)
+       while (dn && PCI_DN(dn) && PCI_DN(dn)->table_group == NULL)
                dn = dn->parent;
 
-       if (dn && PCI_DN(dn))
-               set_iommu_table_base_and_group(&dev->dev,
-                                              PCI_DN(dn)->iommu_table);
-       else
+       if (dn && PCI_DN(dn)) {
+               set_iommu_table_base(&dev->dev,
+                               PCI_DN(dn)->table_group->tables[0]);
+               iommu_add_device(&dev->dev);
+       } else
                printk(KERN_WARNING "iommu: Device %s has no iommu table\n",
                       pci_name(dev));
 }
@@ -1088,7 +1175,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
        dn = pci_device_to_OF_node(dev);
        pr_debug("  node is %s\n", dn->full_name);
 
-       for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
+       for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->table_group;
             pdn = pdn->parent) {
                dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
                if (dma_window)
@@ -1104,18 +1191,21 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
        pr_debug("  parent is %s\n", pdn->full_name);
 
        pci = PCI_DN(pdn);
-       if (!pci->iommu_table) {
-               tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                                  pci->phb->node);
+       if (!pci->table_group) {
+               pci->table_group = iommu_pseries_alloc_group(pci->phb->node);
+               tbl = pci->table_group->tables[0];
                iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window);
-               pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
-               iommu_register_group(tbl, pci_domain_nr(pci->phb->bus), 0);
-               pr_debug("  created table: %p\n", pci->iommu_table);
+               tbl->it_ops = &iommu_table_lpar_multi_ops;
+               iommu_init_table(tbl, pci->phb->node);
+               iommu_register_group(pci->table_group,
+                               pci_domain_nr(pci->phb->bus), 0);
+               pr_debug("  created table: %p\n", pci->table_group);
        } else {
-               pr_debug("  found DMA window, table: %p\n", pci->iommu_table);
+               pr_debug("  found DMA window, table: %p\n", pci->table_group);
        }
 
-       set_iommu_table_base_and_group(&dev->dev, pci->iommu_table);
+       set_iommu_table_base(&dev->dev, pci->table_group->tables[0]);
+       iommu_add_device(&dev->dev);
 }
 
 static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
@@ -1145,7 +1235,7 @@ static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
                 * search upwards in the tree until we either hit a dma-window
                 * property, OR find a parent with a table already allocated.
                 */
-               for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
+               for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->table_group;
                                pdn = pdn->parent) {
                        dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
                        if (dma_window)
@@ -1162,11 +1252,10 @@ static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
                }
        }
 
-       /* fall back on iommu ops, restore table pointer with ops */
+       /* fall back on iommu ops */
        if (!ddw_enabled && get_dma_ops(dev) != &dma_iommu_ops) {
                dev_info(dev, "Restoring 32-bit DMA via iommu\n");
                set_dma_ops(dev, &dma_iommu_ops);
-               pci_dma_dev_setup_pSeriesLP(pdev);
        }
 
 check_mask:
@@ -1189,7 +1278,7 @@ static u64 dma_get_required_mask_pSeriesLP(struct device *dev)
                dn = pci_device_to_OF_node(pdev);
 
                /* search upwards for ibm,dma-window */
-               for (; dn && PCI_DN(dn) && !PCI_DN(dn)->iommu_table;
+               for (; dn && PCI_DN(dn) && !PCI_DN(dn)->table_group;
                                dn = dn->parent)
                        if (of_get_property(dn, "ibm,dma-window", NULL))
                                break;
@@ -1202,15 +1291,6 @@ static u64 dma_get_required_mask_pSeriesLP(struct device *dev)
        return dma_iommu_ops.get_required_mask(dev);
 }
 
-#else  /* CONFIG_PCI */
-#define pci_dma_bus_setup_pSeries      NULL
-#define pci_dma_dev_setup_pSeries      NULL
-#define pci_dma_bus_setup_pSeriesLP    NULL
-#define pci_dma_dev_setup_pSeriesLP    NULL
-#define dma_set_mask_pSeriesLP         NULL
-#define dma_get_required_mask_pSeriesLP        NULL
-#endif /* !CONFIG_PCI */
-
 static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action,
                void *data)
 {
@@ -1269,8 +1349,9 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
                 * the device node.
                 */
                remove_ddw(np, false);
-               if (pci && pci->iommu_table)
-                       iommu_free_table(pci->iommu_table, np->full_name);
+               if (pci && pci->table_group)
+                       iommu_pseries_free_group(pci->table_group,
+                                       np->full_name);
 
                spin_lock(&direct_window_list_lock);
                list_for_each_entry(window, &direct_window_list, list) {
@@ -1300,22 +1381,11 @@ void iommu_init_early_pSeries(void)
                return;
 
        if (firmware_has_feature(FW_FEATURE_LPAR)) {
-               if (firmware_has_feature(FW_FEATURE_MULTITCE)) {
-                       ppc_md.tce_build = tce_buildmulti_pSeriesLP;
-                       ppc_md.tce_free  = tce_freemulti_pSeriesLP;
-               } else {
-                       ppc_md.tce_build = tce_build_pSeriesLP;
-                       ppc_md.tce_free  = tce_free_pSeriesLP;
-               }
-               ppc_md.tce_get   = tce_get_pSeriesLP;
                pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeriesLP;
                pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeriesLP;
                ppc_md.dma_set_mask = dma_set_mask_pSeriesLP;
                ppc_md.dma_get_required_mask = dma_get_required_mask_pSeriesLP;
        } else {
-               ppc_md.tce_build = tce_build_pSeries;
-               ppc_md.tce_free  = tce_free_pSeries;
-               ppc_md.tce_get   = tce_get_pseries;
                pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeries;
                pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeries;
        }
@@ -1333,8 +1403,6 @@ static int __init disable_multitce(char *str)
            firmware_has_feature(FW_FEATURE_LPAR) &&
            firmware_has_feature(FW_FEATURE_MULTITCE)) {
                printk(KERN_INFO "Disabling MULTITCE firmware feature\n");
-               ppc_md.tce_build = tce_build_pSeriesLP;
-               ppc_md.tce_free  = tce_free_pSeriesLP;
                powerpc_firmware_features &= ~FW_FEATURE_MULTITCE;
        }
        return 1;